[
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: [hlerenow]\npatreon: # Replace with a single Patreon username\nopen_collective: # Replace with a single Open Collective username\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\notechie: # Replace with a single Otechie username\nlfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry\ncustom: # ['https://github.com/sponsors/hlerenow?frequency=one-time&sponsor=hlerenow']\n"
  },
  {
    "path": ".github/workflows/static.yml",
    "content": "name: Deploy static content to Pages\n\non:\n  push:\n    branches: [master]\n  workflow_dispatch:\n\nconcurrency:\n  group: 'pages'\n  cancel-in-progress: true\n\njobs:\n  build:\n    permissions: write-all\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Use Node.js 18.x\n        uses: actions/setup-node@v3\n        with:\n          node-version: 18.20.8\n\n      - name: Checkout code\n        uses: actions/checkout@v3\n\n      - uses: pnpm/action-setup@v2\n        with:\n          version: 8\n\n      - run: |\n          pnpm -v\n          node -v\n          npm -v\n          pnpm install --no-frozen-lockfile\n          pnpm run build\n          cd packages/engine-website-app/dist\n          mkdir documents\n          cp -r ../../docs-app/dist/* ./documents\n\n      - name: Upload build artifacts\n        uses: actions/upload-pages-artifact@v3\n        with:\n          name: github-pages\n          path: 'packages/engine-website-app/dist'\n\n  deploy:\n    runs-on: ubuntu-latest\n    needs: build # Ensure deployment happens after build\n    permissions: write-all\n    environment:\n      name: github-pages\n      url: ${{ steps.deployment.outputs.page_url }}\n\n    steps:\n      - name: Download build artifact\n        uses: actions/download-artifact@v4\n        with:\n          name: github-pages\n\n      - name: Setup Pages\n        uses: actions/configure-pages@v2\n\n      - name: Deploy to GitHub Pages\n        id: deployment\n        uses: actions/deploy-pages@v4\n"
  },
  {
    "path": ".gitignore",
    "content": "# See http://help.github.com/ignore-files/ for more about ignoring files.\n\n# compiled output\ndist\ntmp\n/out-tsc\n\n# dependencies\nnode_modules\n\n# JetBrains IDEs\n.idea\n*.iml\n*.iws\n\n\n# IDEs and editors\n/.idea\n.project\n.classpath\n.c9/\n*.launch\n.settings/\n*.sublime-workspace\n\n# IDE - VSCode\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n\n# misc\n/.sass-cache\n/connect.lock\n/coverage\n/libpeerconnection.log\nnpm-debug.log\nyarn-error.log\ntestem.log\n/typings\n\n# System Files\n.DS_Store\nThumbs.db\n\n# Generated Docusaurus files\n.docusaurus/\n.cache-loader/\n\n*.js.map\n*error.log\n\npackages/build-script/lib/*\nstats.html\nexample\n\n\n# drawio\n*.bkp\n*.dtmp\n\n*.eslintcache"
  },
  {
    "path": ".husky/pre-commit",
    "content": "#!/usr/bin/env sh\n. \"$(dirname -- \"$0\")/_/husky.sh\"\n\nnpx lint-staged\n"
  },
  {
    "path": ".npmrc",
    "content": "registry=https://registry.npmjs.org/"
  },
  {
    "path": ".prettierignore",
    "content": "# Ignore artifacts:\nbuild\ncoverage\n\n# Ignore all HTML files:\n*.html\n\n**/.git\n**/.svn\n**/.hg\n**/node_modules"
  },
  {
    "path": ".prettierrc.json",
    "content": "{\n  \"trailingComma\": \"es5\",\n  \"tabWidth\": 2,\n  \"semi\": true,\n  \"singleQuote\": true,\n  \"printWidth\": 120\n}"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n  \"cSpell.words\": [\n    \"chamn\",\n    \"crossorigin\",\n    \"CSSUI\",\n    \"JSEXPRESSION\",\n    \"lowcode\",\n    \"monac\",\n    \"PNPM\",\n    \"superstruct\"\n  ]\n}"
  },
  {
    "path": "BUILD_FIX_SUMMARY.md",
    "content": "# 构建配置修复总结\n\n## 问题描述\n\n1. **CommonJS 引入问题**：错误提示 `Missing \"./src/types/material\" specifier in \"@chamn/model\" package`\n2. **ES 模块不纯净**：构建出的 ES 模块将 React 及其运行时打包进去，而不是作为外部依赖\n\n## 根本原因\n\n### 1. 源码路径导入\n代码中使用了 `@chamn/model/src/types/material` 这样的源码路径，但 package.json 的 `exports` 字段未定义该路径。\n\n### 2. External 配置不完整\n- `external: ['react', 'react-dom']` 只能匹配精确的模块名\n- 无法匹配 `react/jsx-runtime`、`react-dom/client` 等子路径\n- 导致 React JSX 运行时被打包进最终产物\n\n### 3. 构建配置覆盖问题\n`vite.build.rollupOptions` 会覆盖而非合并基础配置中的 `external`。\n\n## 修复方案\n\n### 1. 修复源码导入路径\n\n**修改文件：**\n- `packages/engine/src/component/CustomSchemaForm/index.tsx`\n- `packages/engine/src/plugins/OutlineTree/util.tsx`\n\n**修改前：**\n```typescript\nimport { getMTitle } from '@chamn/model/src/types/material';\nimport { ShapeSetterObjType } from '@chamn/model/src/types/material';\n```\n\n**修改后：**\n```typescript\nimport { getMTitle, ShapeSetterObjType } from '@chamn/model';\n```\n\n### 2. 更新 Package.json 导出配置\n\n**修改文件：**\n- `packages/engine/package.json`\n- `packages/layout/package.json`\n\n**修改内容：**\n```json\n{\n  \"main\": \"dist/index.cjs.js\",\n  \"module\": \"dist/index.es.js\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./dist/index.d.ts\",\n      \"module-sync\": \"./dist/index.es.js\",\n      \"import\": \"./dist/index.es.js\",\n      \"require\": \"./dist/index.cjs.js\"\n    }\n  }\n}\n```\n\n### 3. 使用函数形式自动外部化所有 node_modules（最佳实践）\n\n**修改文件：**\n- `packages/build-script/src/config/base.ts`\n- `packages/engine/build.config.ts`\n- `packages/model/build.config.js`\n- `packages/layout/build.config.js`\n- `packages/render/build.config.ts`\n\n**关键修改：**\n\n```typescript\n// build-script/src/config/base.ts\nexport type BuildScriptConfig = {\n  external?: (string | RegExp)[] | ((id: string, importer?: string, isResolved?: boolean) => boolean);\n  // ... 其他配置\n};\n```\n\n```typescript\n// engine/build.config.ts\nexternal: (id) => {\n  // 排除相对路径、绝对路径和别名路径（项目内部文件）\n  if (id.startsWith('.') || id.startsWith('/') || id.startsWith('@/')) {\n    return false;\n  }\n  // 外部化所有 node_modules 中的包\n  return true;\n},\n```\n\n**优势：**\n- ✅ 自动外部化所有 node_modules 依赖，无需手动维护列表\n- ✅ 支持子路径（如 `react/jsx-runtime`、`antd/es/tag/CheckableTag`）\n- ✅ 避免遗漏新增的依赖包\n\n### 4. 简化构建配置\n\n**修改前（engine/build.config.ts）：**\n```typescript\nvite: {\n  build: {\n    rollupOptions: {\n      external: [...], // 这会覆盖基础配置\n    }\n  }\n}\n```\n\n**修改后：**\n```typescript\n// 顶层配置 external，由 vite.common.ts 自动转换\nexternal: [/^react($|\\/)/, ...],\nvite: {\n  // 不再重复配置 build.rollupOptions\n  define: { ... }\n}\n```\n\n## 验证结果\n\n### 构建输出对比\n\n**修复前：**\n- `index.es.js`: 100,520 行\n- 包含内联的 react-jsx-runtime 代码\n- 文件大小：~3.5MB\n\n**第一次修复后（使用正则表达式）：**\n- `index.es.js`: 94,484 行（减少 6,036 行）\n- 正确使用 `import { jsx, jsxs } from \"react/jsx-runtime\"`\n- 文件大小：~3.49MB（减少约 10KB）\n\n**最终修复后（自动外部化所有 node_modules）：**\n- `index.es.js`: 11,551 行（减少 88,969 行，88.5% 体积减少！）\n- 所有依赖都被正确外部化\n- 文件大小：327KB（减少 3.17MB，90.7% 体积减少！）\n\n### 构建日志\n\n```\nNo name was provided for external module \"react/jsx-runtime\" in \"output.globals\" – guessing \"jsxRuntime\".\n```\n\n这条警告证明 `react/jsx-runtime` 被正确识别为外部依赖。\n\n### 代码检查\n\n```javascript\n// 修复后的 dist/index.es.js 开头\nimport { jsx as W, jsxs as He, Fragment as Ht } from \"react/jsx-runtime\";\nimport * as We from \"react\";\nimport he, { memo, useCallback, ... } from \"react\";\nimport BR, { flushSync, createPortal, ... } from \"react-dom\";\n```\n\n## 最佳实践\n\n1. **External 配置（推荐）**：使用函数形式自动外部化所有 node_modules\n   ```typescript\n   external: (id) => {\n     if (id.startsWith('.') || id.startsWith('/') || id.startsWith('@/')) {\n       return false;\n     }\n     return true;\n   }\n   ```\n\n2. **导入路径**：始终从包的主入口导入，避免使用源码路径\n\n3. **Package.json**：确保 `main`、`module`、`exports` 字段与实际构建输出一致\n\n4. **构建配置**：在顶层配置 `external`，避免在 `vite.build.rollupOptions` 中重复配置\n\n5. **别名路径**：确保项目内部的别名路径（如 `@/`）不被外部化\n\n## 相关文件清单\n\n### 修改的文件\n- `packages/build-script/src/config/base.ts`\n- `packages/build-script/src/config/vite.build.ts`\n- `packages/engine/build.config.ts`\n- `packages/engine/package.json`\n- `packages/engine/src/component/CustomSchemaForm/index.tsx`\n- `packages/engine/src/plugins/OutlineTree/util.tsx`\n- `packages/model/build.config.js`\n- `packages/layout/build.config.js`\n- `packages/layout/package.json`\n- `packages/render/build.config.ts`\n\n### 构建命令\n```bash\n# 重新构建所有包\nnpm run build\n\n# 或单独构建\ncd packages/engine && npm run build\ncd packages/model && npm run build\ncd packages/layout && npm run build\ncd packages/render && npm run build\n```\n\n## 注意事项\n\n1. 所有依赖包都需要重新构建\n2. 确保 peerDependencies 中的 react 版本与项目一致\n3. UMD 格式需要在 `global` 配置中为外部依赖提供全局变量名\n\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Change Log\n\nAll notable changes to this project will be documented in this file.\nSee [Conventional Commits](https://conventionalcommits.org) for commit guidelines.\n\n## [0.10.4](https://github.com/ByteCrazy/chameleon/compare/v0.10.3...v0.10.4) (2026-01-17)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, engine-website-app:** 🐛 fixed pkg deps ([22b0000](https://github.com/ByteCrazy/chameleon/commit/22b0000a921ed3bd63cdf3366687184d51ee9dc5))\n\n## [0.10.3](https://github.com/ByteCrazy/chameleon/compare/v0.10.2...v0.10.3) (2026-01-17)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed  engine deps ([c1addf4](https://github.com/ByteCrazy/chameleon/commit/c1addf43214d016e466019a5aabb8960881b6418))\n\n## [0.10.2](https://github.com/ByteCrazy/chameleon/compare/v0.10.1...v0.10.2) (2026-01-17)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed string and text can not input chinese text ([3821d00](https://github.com/ByteCrazy/chameleon/commit/3821d00d5dffd7d70e995690df5358f5a596ae93))\n\n## [0.10.1](https://github.com/ByteCrazy/chameleon/compare/v0.10.0...v0.10.1) (2026-01-11)\n\n### ✨ Features | 新功能\n\n* **build-script, demo-page, engine, render:** 🎸 update build script dts & optimize modal root selector ([024d5ab](https://github.com/ByteCrazy/chameleon/commit/024d5ab26d19fc5f50172c328638a551fb99c129))\n* **docs-app, render:** 🎸 adapte ssr render ([7f19d8f](https://github.com/ByteCrazy/chameleon/commit/7f19d8f9084e16de27460bc1e5dea28eba31d6dd))\n\n## [0.10.0](https://github.com/hlerenow/chameleon/compare/v0.9.3...v0.10.0) (2025-12-25)\n\n### ✨ Features | 新功能\n\n* **engine:** 🎸 optimize emptyValueSetter auto trigger value update ([a2b0409](https://github.com/hlerenow/chameleon/commit/a2b04094035d421638d4657d31fd2b417906e9c6))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* 优化构建配置，修复 ES 模块外部化问题 ([866f0ae](https://github.com/hlerenow/chameleon/commit/866f0ae39472625137d73d143625d88d873f6c00))\n\n### 📝 Documentation | 文档\n\n* 添加 GridLayout 代码示例和插件开发文档 ([cade905](https://github.com/hlerenow/chameleon/commit/cade90591efff604a8ab3395c905c4f713ba2b93))\n* **build-script:** 添加完整的 README 使用文档 ([fcd6172](https://github.com/hlerenow/chameleon/commit/fcd6172e381364f010986864593264b160380342))\n\n## [0.9.3](https://github.com/ByteCrazy/chameleon/compare/v0.9.2...v0.9.3) (2025-07-20)\n\n### ✨ Features | 新功能\n\n* **engine, render:** 🎸 add $EVENT_PARAMS and remove $Event $PARAMS_RUNTIME ([b8e58c1](https://github.com/ByteCrazy/chameleon/commit/b8e58c1e5e98d2e4b3d31e2ba52a26fda256eb47))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **render:** 🐛 fixed style props not apply to dom  from props ([2e0c80a](https://github.com/ByteCrazy/chameleon/commit/2e0c80ad02881319089b1b08225c325e37a41c6c))\n\n## [0.9.2](https://github.com/ByteCrazy/chameleon/compare/v0.9.1...v0.9.2) (2025-07-13)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed call node method setting ([27ea497](https://github.com/ByteCrazy/chameleon/commit/27ea49747ca8dd24f862304ec552e805c9057e62))\n* **render:** 🐛 fixed ref get not correct on designer mode ([14544f4](https://github.com/ByteCrazy/chameleon/commit/14544f45a5dadeacfab5a711504b9a736ee7835e))\n\n## [0.9.1](https://github.com/ByteCrazy/chameleon/compare/v0.9.0...v0.9.1) (2025-07-13)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, render:** 🐛 fixed CustomSchemaForm title ([178cddd](https://github.com/ByteCrazy/chameleon/commit/178cddd3e8d9147822e91fcd1ce7d8e08c70d924))\n\n## [0.9.0](https://github.com/ByteCrazy/chameleon/compare/v0.8.6...v0.9.0) (2025-07-13)\n\n### 👷 Continuous Integration | CI 配置\n\n* 🎡 upgrade node version to 18.20.8 ([0a68c22](https://github.com/ByteCrazy/chameleon/commit/0a68c22dc7d320c3139687c956c126816b453fb4))\n\n### ✨ Features | 新功能\n\n* **engine, model:** 🎸 support EmptyValueSetter ([da1e36f](https://github.com/ByteCrazy/chameleon/commit/da1e36f9915282b3f7cb40672cf2a000bd4162e5))\n\n## [0.8.6](https://github.com/ByteCrazy/chameleon/compare/v0.8.5...v0.8.6) (2025-06-21)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **demo-page, engine, render:** 🐛 fixed action flow failed node not connect correct ([e235a2b](https://github.com/ByteCrazy/chameleon/commit/e235a2be2d1e1935dfc40b56936de763efc4fb67))\n* **render:** 🐛 fixed $RESPONSE value is error ([dee9906](https://github.com/ByteCrazy/chameleon/commit/dee9906c967eb079d5a7e53431dc9d29458195c8))\n\n## [0.8.5](https://github.com/ByteCrazy/chameleon/compare/v0.8.4...v0.8.5) (2025-04-13)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed custom seeter not register success ([19d739a](https://github.com/ByteCrazy/chameleon/commit/19d739a97000641846c02ec19555926fe88ce3b4))\n\n## [0.8.4](https://github.com/ByteCrazy/chameleon/compare/v0.8.3...v0.8.4) (2025-04-13)\n\n### ✨ Features | 新功能\n\n* **engine:** 🎸 optimize action flow layout ([9c970c9](https://github.com/ByteCrazy/chameleon/commit/9c970c9482274d4d5cf6beb76912a10784e62c79))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, render:** 🐛 CSS editor value update error ([209106a](https://github.com/ByteCrazy/chameleon/commit/209106a6a71819862626ee39c887d1835a8b5611))\n* **engine, render:** 🐛 fixed event not trigger when loop ([e3a10a8](https://github.com/ByteCrazy/chameleon/commit/e3a10a8ab77a3c5321f47440c7bdc2ad52e82ee2))\n\n## [0.8.3](https://github.com/ByteCrazy/chameleon/compare/v0.8.2...v0.8.3) (2025-04-12)\n\n**Note:** Version bump only for package chameleon\n\n## [0.8.2](https://github.com/ByteCrazy/chameleon/compare/v0.8.1...v0.8.2) (2025-04-11)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, render:** 🐛 fixed functionSeter and expressionSetter dts ([1e326e2](https://github.com/ByteCrazy/chameleon/commit/1e326e288f8d072497c01c1ccb0c06bed0521949))\n\n## [0.8.1](https://github.com/ByteCrazy/chameleon/compare/v0.8.0...v0.8.1) (2025-04-10)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed globalStateDts ([03c0b71](https://github.com/ByteCrazy/chameleon/commit/03c0b714343a7cf76710b164a43008af430b1d7c))\n\n## [0.8.0](https://github.com/ByteCrazy/chameleon/compare/v0.7.0...v0.8.0) (2025-04-10)\n\n### ✨ Features | 新功能\n\n* **demo-page, engine, render:** 🎸 update run time var for function ([d8fb90a](https://github.com/ByteCrazy/chameleon/commit/d8fb90ac72a233d7fdb5fcb36e0b7963f83c5acf))\n* **engine, model, render:** 🎸 fixed cycle update when update value in mount ([9b30922](https://github.com/ByteCrazy/chameleon/commit/9b30922ffac4a5b4fb1fe123c4604cc0fff63911))\n* **engine, render:** 🎸 optimize expression setter ([07c11e9](https://github.com/ByteCrazy/chameleon/commit/07c11e9bdf011f70763623d6a2f185a3c3830e5e))\n* **engine, render:** 🎸 optimize globalState update ([4e7bb6a](https://github.com/ByteCrazy/chameleon/commit/4e7bb6ab68cd69fe6d429abe417587c3ac26eb18))\n* **engine:** 🎸 dynamic generate page dts ([2dc1a0b](https://github.com/ByteCrazy/chameleon/commit/2dc1a0bca0a53223b61b8c4ce216b26a70887c96))\n* **engine:** 🎸 optimize express setter ([37141f9](https://github.com/ByteCrazy/chameleon/commit/37141f97a98e53e0050e328c2de03341984b3ad5))\n\n## [0.7.0](https://github.com/ByteCrazy/chameleon/compare/v0.6.0...v0.7.0) (2025-04-06)\n\n### ✨ Features | 新功能\n\n* **engine, layout:** 🎸 optimize select node interactive time ([41d105c](https://github.com/ByteCrazy/chameleon/commit/41d105c2408458b38cda0c9fac601dd89fd744aa))\n* **engine:** 🎸 add css code editor ([7715f29](https://github.com/ByteCrazy/chameleon/commit/7715f29cd49d530e54941c85659ca3c671be91e3))\n* **engine:** 🎸 optimize style UI panel ([0d6f239](https://github.com/ByteCrazy/chameleon/commit/0d6f2394281bf4add5437b0c6e4c681f5d64d6e3))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, layout:** 🐛 optimize select active box ([b1a6041](https://github.com/ByteCrazy/chameleon/commit/b1a604140652f1c0871a463bc32020c39a07846c))\n\n## [0.6.0](https://github.com/ByteCrazy/chameleon/compare/v0.5.2...v0.6.0) (2025-03-30)\n\n### ✨ Features | 新功能\n\n* **engine:** 🎸 support config monacoEdito cdn url ([32d08bc](https://github.com/ByteCrazy/chameleon/commit/32d08bcaec7ebe8a187d0b2eae8bd5e4b64b3dc9))\n\n## [0.5.2](https://github.com/ByteCrazy/chameleon/compare/v0.5.1...v0.5.2) (2025-03-30)\n\n### ✨ Features | 新功能\n\n* **model:** 🎸 support export sxtra ([272de8d](https://github.com/ByteCrazy/chameleon/commit/272de8d989c3085830bf37469885c7a92abaf0da))\n\n## [0.5.1](https://github.com/ByteCrazy/chameleon/compare/v0.5.0...v0.5.1) (2025-03-30)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed search status reset ([994b36c](https://github.com/ByteCrazy/chameleon/commit/994b36ce744fd10002cf2996ae26702a959ef5b4))\n\n## [0.5.0](https://github.com/ByteCrazy/chameleon/compare/v0.4.0...v0.5.0) (2025-03-30)\n\n### ✨ Features | 新功能\n\n* **engine:** 🎸 componentLib add search and customSearchBar ([4801f40](https://github.com/ByteCrazy/chameleon/commit/4801f403da1af705d5dc5eef579e7dba6560b08f))\n\n## [0.4.0](https://github.com/ByteCrazy/chameleon/compare/v0.3.21...v0.4.0) (2025-03-29)\n\n### ✨ Features | 新功能\n\n* **model:** 🎸 update model define, add extra T ([f098ad2](https://github.com/ByteCrazy/chameleon/commit/f098ad26dc7525749d280445202971ede12490f4))\n\n## [0.3.21](https://github.com/ByteCrazy/chameleon/compare/v0.3.20...v0.3.21) (2025-03-26)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, material:** 🐛 fixed RGLEL cycle update item ([f045818](https://github.com/ByteCrazy/chameleon/commit/f045818d6d1e1b4dbcf107d727976f6d92600b94))\n\n## [0.3.20](https://github.com/ByteCrazy/chameleon/compare/v0.3.19...v0.3.20) (2025-03-26)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **material:** 🐛 fixed meterial meta not export ([aaea9bd](https://github.com/ByteCrazy/chameleon/commit/aaea9bd8fb95aa007b3b6ccd06e4b3171af35926))\n\n## [0.3.19](https://github.com/ByteCrazy/chameleon/compare/v0.3.18...v0.3.19) (2025-03-26)\n\n**Note:** Version bump only for package chameleon\n\n## [0.3.18](https://github.com/ByteCrazy/chameleon/compare/v0.3.17...v0.3.18) (2025-03-26)\n\n**Note:** Version bump only for package chameleon\n\n## [0.3.17](https://github.com/ByteCrazy/chameleon/compare/v0.3.16...v0.3.17) (2025-03-25)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, material, model, render:** 🐛 fixed node update value material is undefined ([969174d](https://github.com/ByteCrazy/chameleon/commit/969174da968aec4a1ee1fec7ca44a5e459be56bc))\n\n## [0.3.16](https://github.com/ByteCrazy/chameleon/compare/v0.3.15...v0.3.16) (2025-03-24)\n\n### ✨ Features | 新功能\n\n* **engine:** 🎸 setter supprot get current nodemodel ([f59a136](https://github.com/ByteCrazy/chameleon/commit/f59a136cc134388c382827d731f683e9d9a298e5))\n\n## [0.3.15](https://github.com/ByteCrazy/chameleon/compare/v0.3.14...v0.3.15) (2025-03-23)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **layout:** 🐛 fixed canvas default css ([41d0ebd](https://github.com/ByteCrazy/chameleon/commit/41d0ebd91b0d9a9fa2d1b7892ec1ec82ecd44a33))\n\n## [0.3.14](https://github.com/ByteCrazy/chameleon/compare/v0.3.13...v0.3.14) (2025-03-23)\n\n### ✨ Features | 新功能\n\n* **layout:** 🎸 ban drag img audio viewo el at editor canvas ([0f2624b](https://github.com/ByteCrazy/chameleon/commit/0f2624bd45eada0290610f7bb52e1683c0c84189))\n\n## [0.3.13](https://github.com/ByteCrazy/chameleon/compare/v0.3.12...v0.3.13) (2025-03-23)\n\n### ✨ Features | 新功能\n\n* **render:** 🎸 redner support config document context ([8d3c041](https://github.com/ByteCrazy/chameleon/commit/8d3c041263ed567a1e91087abcde382519876b2e))\n\n## [0.3.12](https://github.com/ByteCrazy/chameleon/compare/v0.3.11...v0.3.12) (2025-03-23)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, layout:** 🐛 fixed drag interactive and fixed accetpNode action ([c15d03c](https://github.com/ByteCrazy/chameleon/commit/c15d03cc0406533e5eab55e27ce5eb1223d91c72))\n\n## [0.3.11](https://github.com/ByteCrazy/chameleon/compare/v0.3.10...v0.3.11) (2025-03-22)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **render:** 🐛 fixed PlaceHoder UI ([349a951](https://github.com/ByteCrazy/chameleon/commit/349a951ac9e2351acb0c556bc0ecb4295367e94e))\n\n## [0.3.10](https://github.com/ByteCrazy/chameleon/compare/v0.3.9...v0.3.10) (2025-03-22)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **layout:** 🐛 fixed placeholder UI ([7134583](https://github.com/ByteCrazy/chameleon/commit/71345836c5575399f2fadcd4be5773c593efb95f))\n\n## [0.3.9](https://github.com/ByteCrazy/chameleon/compare/v0.3.8...v0.3.9) (2025-03-22)\n\n### ✨ Features | 新功能\n\n* **engine, layout:** 🎸 optimize drag interactive ([f512f14](https://github.com/ByteCrazy/chameleon/commit/f512f14ecf3caaa148279543999a74d35ae44701))\n\n## [0.3.8](https://github.com/ByteCrazy/chameleon/compare/v0.3.7...v0.3.8) (2025-03-16)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed customAdvanceHook not rect on outline and hotkey ([c21bdb0](https://github.com/ByteCrazy/chameleon/commit/c21bdb0e275ebe4ae096d18b90bd406874c9de79))\n\n## [0.3.7](https://github.com/ByteCrazy/chameleon/compare/v0.3.6...v0.3.7) (2025-03-16)\n\n### ✨ Features | 新功能\n\n* **demo-page, engine, model, render:** 🎸 support inject eng inner env to runtime ([baa5c11](https://github.com/ByteCrazy/chameleon/commit/baa5c11d389019a7e4e4b8e000433a99038b4ae3))\n\n## [0.3.6](https://github.com/ByteCrazy/chameleon/compare/v0.3.5...v0.3.6) (2025-03-09)\n\n### ✨ Features | 新功能\n\n* **engine:** 🎸 add hiddenWidget for eng and optimize setter ([42b9846](https://github.com/ByteCrazy/chameleon/commit/42b984681a9efc7cdee7dc4287cd994fd37c592c))\n\n## [0.3.5](https://github.com/ByteCrazy/chameleon/compare/v0.3.4...v0.3.5) (2025-03-09)\n\n### ✨ Features | 新功能\n\n* **engine, layout:** 🎸 support controll preview mode ([97b83e5](https://github.com/ByteCrazy/chameleon/commit/97b83e58f0f22c76da51e5a3d22db2c82a2f70d2))\n* **engine:** 🎸 add workbench widget control config ([0fcab5b](https://github.com/ByteCrazy/chameleon/commit/0fcab5b5ad715762327c26d3f7eae4680604644b))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **demo-page, engine, render:** 🐛 fixed action flow only run first node ([c4bdb85](https://github.com/ByteCrazy/chameleon/commit/c4bdb85d0ca6ed09c6c66d15847de5bd14da556e))\n* **engine, model:** 🐛 update select setter name ([054fd48](https://github.com/ByteCrazy/chameleon/commit/054fd48f49ec1d1bdb79d7bac44acedb601d6fdf))\n* **engine:** 🐛 fixed last connect line not save ([2bacb15](https://github.com/ByteCrazy/chameleon/commit/2bacb1541cbc753df15a0216df9d6d6aac86cb1d))\n\n## [0.3.4](https://github.com/ByteCrazy/chameleon/compare/v0.3.3...v0.3.4) (2025-02-16)\n\n### ✨ Features | 新功能\n\n* **engine, layout, render:** 🎸 action node express support $response ([9bf1570](https://github.com/ByteCrazy/chameleon/commit/9bf1570c3b80404be78a5d3ca2c401464e7645d3))\n\n## [0.3.3](https://github.com/ByteCrazy/chameleon/compare/v0.3.2...v0.3.3) (2025-02-16)\n\n### ✨ Features | 新功能\n\n* **engine:** 🎸 JSON setter support reactive value ([0262067](https://github.com/ByteCrazy/chameleon/commit/0262067fc641b8dd3c812cd3cf1f114df7e33f9c))\n\n## [0.3.2](https://github.com/ByteCrazy/chameleon/compare/v0.3.1...v0.3.2) (2025-02-16)\n\n### ✨ Features | 新功能\n\n* **engine:** 🎸 optimize TCustomAPIInput type ([1b33f75](https://github.com/ByteCrazy/chameleon/commit/1b33f75a8b6e324b1f7695a62dfa1ae97d115cc7))\n\n## [0.3.1](https://github.com/ByteCrazy/chameleon/compare/v0.3.0...v0.3.1) (2025-02-16)\n\n### ✨ Features | 新功能\n\n* **engine, render:** 🎸 optimize CustomAPISelectInput from and event list label ([8dbf5af](https://github.com/ByteCrazy/chameleon/commit/8dbf5af08e50c60c5ff7adc5c7e644c4ca1a9c08))\n\n## [0.3.0](https://github.com/ByteCrazy/chameleon/compare/v0.2.4...v0.3.0) (2025-02-15)\n\n### ♻️ Code Refactoring | 代码重构\n\n* **render:** 💡 optimize convertModelToComponent code logic ([3334dd5](https://github.com/ByteCrazy/chameleon/commit/3334dd5f421b0d1b78ccde403c9673cadcea91da))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, engine-website-app, material, model, render:** 🐛 fixed GRL hidden offsetY loop add size ([3df132b](https://github.com/ByteCrazy/chameleon/commit/3df132b6493026b42435d4868d77915b9f7316b2))\n* **engine:** 🐛 fixed ActionFlowSetter cicle deps ([b9bd177](https://github.com/ByteCrazy/chameleon/commit/b9bd177e38be054a8860d19516651d9ab813e27b))\n* **engine:** 🐛 fixed ActionFlowSetter cycle deps ([e69dfd7](https://github.com/ByteCrazy/chameleon/commit/e69dfd7df7129a88c54e238a05b96bb3270fbb2f))\n* **engine:** 🐛 fixed ActionFlowSetter update problem ([0034848](https://github.com/ByteCrazy/chameleon/commit/0034848e31c3b30b6782723af10e7f8e0152390a))\n* **engine:** 🐛 fixed outline drag excepetion after remove page ([82ff3fd](https://github.com/ByteCrazy/chameleon/commit/82ff3fd80ffce58b2b840ae219d29e61dd34a6e4))\n* **engine:** 🐛 resolve hot key confict with action flow setter ([e91898c](https://github.com/ByteCrazy/chameleon/commit/e91898c8deae98805ea2df7a1e8f3bc382bd3873))\n\n### ✨ Features | 新功能\n\n* **demo-page, engine, model, render:** 🎸 action node use link struct ([599ece4](https://github.com/ByteCrazy/chameleon/commit/599ece4927523f7c0e330cac722c9c9e6976ea3c))\n* **demo-page, engine, model, render:** 🎸 add event panel ([e8c5648](https://github.com/ByteCrazy/chameleon/commit/e8c5648017b40cbae42c576267d1e3b9d9660918))\n* **demo-page, engine, model, render:** 🎸 support TActionLogicItem prop ([e1b9d1e](https://github.com/ByteCrazy/chameleon/commit/e1b9d1e150ae810750249322ddf906b62eee9969))\n* **demo-page, model, render:** 🎸 optimize afterResponse ([b4060ba](https://github.com/ByteCrazy/chameleon/commit/b4060ba0a0a01960ae5f8dffea159e2d5ea680b6))\n* **demo-page, model, render:** 🎸 support ASSIGN_VALUE node ([74c395b](https://github.com/ByteCrazy/chameleon/commit/74c395baec55911de734e47d4a270f9cf016ee21))\n* **demo-page, model:** 🎸 define action and event type ([f7ca5d3](https://github.com/ByteCrazy/chameleon/commit/f7ca5d3b8ad7610f840845f555bfcdb2adddae0a))\n* **demo-page, render:** 🎸 getMethods support get methods from ref ([0a26967](https://github.com/ByteCrazy/chameleon/commit/0a2696739eee29c3e30b805d537605a06496ebc5))\n* **demo-page, render:** 🎸 support eventListener attr ([fa0977f](https://github.com/ByteCrazy/chameleon/commit/fa0977fab37b961d1b7a0b04c8e528e43ab9503f))\n* **demo-page, render:** 🎸 support ON_DID_RENDER and ON_WILL_DESTROY inner event ([b4743e8](https://github.com/ByteCrazy/chameleon/commit/b4743e855766b69a8c5bc58ea3849694948bd501))\n* **docs-app, engine, engine-website-app, layout, material, model, render:** 🎸 do ActionFlowSetter 50% ([6399466](https://github.com/ByteCrazy/chameleon/commit/6399466c7253436591e071df25828a357bb7089e))\n* **engine, engine-website-app, render:** 🎸 RequestAPINode support custom select ([5b72385](https://github.com/ByteCrazy/chameleon/commit/5b72385673bb646aff3ad2c5a9d8fffa142733dd))\n* **engine, model:** 🎸 add  call node method node ([1a5e79c](https://github.com/ByteCrazy/chameleon/commit/1a5e79c49964c589da167b64985327a3cbb03da8))\n* **engine, model:** 🎸 add run code node ([7ac6182](https://github.com/ByteCrazy/chameleon/commit/7ac61829586a19d4fcdc1765fa878d6a16008858))\n* **engine, model:** 🎸 request API 70% ([cee1228](https://github.com/ByteCrazy/chameleon/commit/cee1228c2a3265320cb32579f6f7b532b6962908))\n* **engine:** 🎸 add react-flow ([fafaaf9](https://github.com/ByteCrazy/chameleon/commit/fafaaf95c589c0c99ce0953f285bf53a0e423e21))\n* **engine:** 🎸 JumpLinkNode 100% ([ed0707e](https://github.com/ByteCrazy/chameleon/commit/ed0707e0232bf82bedc7b0db569c908d2001e6d7))\n* **engine:** 🎸 optimize ActionFlowSetter ([fa9af2f](https://github.com/ByteCrazy/chameleon/commit/fa9af2fba32f921411ed05e8d7e68293de5dde88))\n* **engine:** 🎸 optimize call node method node ([0b00029](https://github.com/ByteCrazy/chameleon/commit/0b00029627d6f90381b658ccc7f125e9b6116dcf))\n* **engine:** 🎸 optimize MoveableModal interactive ([2eccb94](https://github.com/ByteCrazy/chameleon/commit/2eccb942b447fa4e54bcd6f9207a2ed053e06526))\n* **engine:** 🎸 optimize RequestAPINode custom ([0440695](https://github.com/ByteCrazy/chameleon/commit/044069501608b1a8b62a08600c3c2d293e1ebe54))\n* **engine:** 🎸 optimize selectNodeByTree ([b6959d9](https://github.com/ByteCrazy/chameleon/commit/b6959d98714a16669b3896b677ebb78aa080d8f3))\n* **engine:** 🎸 optimize SetterSwitcher code struct ([d481fe6](https://github.com/ByteCrazy/chameleon/commit/d481fe68665f4fc7b1034e8b1ef9e2bfbe517012))\n* **engine:** 🎸 parseActionLogicToNodeList 30% ([f120154](https://github.com/ByteCrazy/chameleon/commit/f1201549a5537f170dbdce7efca7c85ce3add2ad))\n* **engine:** 🎸 support labelAlign config ([9590f86](https://github.com/ByteCrazy/chameleon/commit/9590f861fe8efebcd81ba38df3b159a528e066b6))\n* **engine:** 🎸 support render flow by schema data ([e04e76d](https://github.com/ByteCrazy/chameleon/commit/e04e76d23115c64823dc6fbf5d460f589df98b3d))\n* **model, render:** 🎸 add acion logic type defined ([a7c32a3](https://github.com/ByteCrazy/chameleon/commit/a7c32a33f80a00458fe16fbc4d0c34631c103b61))\n* **model, render:** 🎸 optimize render react adapter code struct ([d9fa3d0](https://github.com/ByteCrazy/chameleon/commit/d9fa3d0a72e4d7e04e4e86e62d2a4f6f31bd808e))\n* **render:** 🎸 add findDOMNode API ([2898452](https://github.com/ByteCrazy/chameleon/commit/2898452491554afddf36a795876182ff0d1bfc59))\n\n## [0.2.4](https://github.com/ByteCrazy/chameleon/compare/v0.2.3...v0.2.4) (2024-12-08)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, material:** 🐛 fixed RGL init layout not correcnt and upgrade gridstack ([6b66b47](https://github.com/ByteCrazy/chameleon/commit/6b66b47b37e1fcd96602132bb44373a01d5de946))\n\n## [0.2.3](https://github.com/ByteCrazy/chameleon/compare/v0.2.2...v0.2.3) (2024-12-08)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed CVideo interactive ([4b1dabf](https://github.com/ByteCrazy/chameleon/commit/4b1dabfe89efbe2a3dfbb642e77ae10e74daaf0e))\n* **engine:** 🐛 fixed hotkey not work sometimes ([d029841](https://github.com/ByteCrazy/chameleon/commit/d029841679183d5cffcbb991cf64cfcfea9de34e))\n\n## [0.2.2](https://github.com/ByteCrazy/chameleon/compare/v0.2.1...v0.2.2) (2024-12-07)\n\n### ✨ Features | 新功能\n\n* **material:** 🎸 optimize RGL ([518402f](https://github.com/ByteCrazy/chameleon/commit/518402ff0d173e60fd31430222aea8e5856fa0da))\n\n## [0.2.1](https://github.com/ByteCrazy/chameleon/compare/v0.2.0...v0.2.1) (2024-12-07)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, engine-website-app, material, model, render:** 🎸 fixed RGL component and optimize project struct ([99f0679](https://github.com/ByteCrazy/chameleon/commit/99f0679d93a2fd696034ebed8f1abcd9d9e601d4))\n\n## [0.2.0](https://github.com/ByteCrazy/chameleon/compare/v0.1.1...v0.2.0) (2024-12-07)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed inner meta image drag problem ([f752d9e](https://github.com/ByteCrazy/chameleon/commit/f752d9ecf611cfc3f55576d0758905c6ac322a4e))\n* **layout:** 🐛 fixed canvas scroll ([9f4c5b9](https://github.com/ByteCrazy/chameleon/commit/9f4c5b9afaf5e3370a72256f2484b9390354f5a3))\n\n### ✨ Features | 新功能\n\n* **build-script, demo-page, engine, engine-website-app, layout, material, model, render:** 🎸 upgrade vite to 6.0 ([bcac2b1](https://github.com/ByteCrazy/chameleon/commit/bcac2b15b83b41a7042ca37368c1b45302ad81d5))\n* **engine, layout, render:** 🎸 replace findDOMNode API ([af2531a](https://github.com/ByteCrazy/chameleon/commit/af2531a095124ac55d4f6dc6896d430f52f5da82))\n\n## [0.1.1](https://github.com/ByteCrazy/chameleon/compare/v0.1.0...v0.1.1) (2024-11-11)\n\n### ✨ Features | 新功能\n\n* **material:** 🎸 optimize material gridItem UI ([34b8b8e](https://github.com/ByteCrazy/chameleon/commit/34b8b8e1b09aac22e538eff44488dc2021a5add6))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed cycle dependencies ([cefa3f0](https://github.com/ByteCrazy/chameleon/commit/cefa3f0a4a9c72c81b5337bbdb6e0429ca247252))\n* **render:** 🐛 fixeds useRender not memory ([d579bfa](https://github.com/ByteCrazy/chameleon/commit/d579bfa401c39810a5dadc55e3fa1f3a0f407322))\n\n## [0.1.0](https://github.com/ByteCrazy/chameleon/compare/v0.0.46...v0.1.0) (2024-09-07)\n\n### 👷 Continuous Integration | CI 配置\n\n* 🎡 update ci ([7a1abc2](https://github.com/ByteCrazy/chameleon/commit/7a1abc21a7a9b80f53db1579ee928488ef678970))\n* 🎡 update ci ([f21d4f9](https://github.com/ByteCrazy/chameleon/commit/f21d4f96266bb66adf02f961e2bee6aed59c7716))\n* **build-script, docs-website, material, render:** 🎡 not build docs ([37aa10a](https://github.com/ByteCrazy/chameleon/commit/37aa10abf0324f3465a6921a9a014875e9500f8f))\n* **engine, engine-website-app, layout, model:** 🎡 update github ci ([259e9db](https://github.com/ByteCrazy/chameleon/commit/259e9db229576cd09c5aae1f28a2c228927011c7))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **demo-page, docs-app, engine, engine-website-app, layout, material:** 🐛 fixed material meta not correct ([55b755c](https://github.com/ByteCrazy/chameleon/commit/55b755ce0c833e594b46447b2d6608cf56f7a593))\n* **docs-website, engine, layout:** 🐛 fixed higligh toolbar pos ([0356109](https://github.com/ByteCrazy/chameleon/commit/0356109e8a11a5a85ab90d1610ef7ef8549db0a9))\n* **engine-website-app:** 🐛 fixed demo-app assets path ([4cc071e](https://github.com/ByteCrazy/chameleon/commit/4cc071ecfe77e93b46645b5e4a7d806acf78d896))\n* **engine, engine-website-app, material:** 🐛 fixed ReactGridLayout edit mode lable not correct ([9801839](https://github.com/ByteCrazy/chameleon/commit/9801839d9ff6a6548b0e23f1e31850cd56e7fff0))\n* **engine, layout:** 🐛 fixed toolbox width not correct ([4dc159a](https://github.com/ByteCrazy/chameleon/commit/4dc159a1931a853ba4cdb664638f27ba71b1ecf2))\n* **engine:** 🐛 fixed advanceCustom hook logic ([5fb2619](https://github.com/ByteCrazy/chameleon/commit/5fb261962af141affef0e8e2cf1f0b62d16b0d45))\n* **engine:** 🐛 fixed BackgroundInput color input ([944517c](https://github.com/ByteCrazy/chameleon/commit/944517c9078b29c5076784a1df66752494125e9f))\n* **engine:** 🐛 fixed CSSUIPanel value not corrent ([1a4dc64](https://github.com/ByteCrazy/chameleon/commit/1a4dc645171253b261a3d2c9ebd3cf27ec692d34))\n* **engine:** 🐛 fixed github build ([a4ace81](https://github.com/ByteCrazy/chameleon/commit/a4ace818d20c657a255fd25d5771113e5191d55d))\n* **engine:** 🐛 fixed render url ([27736de](https://github.com/ByteCrazy/chameleon/commit/27736de41f239b4911535097c7334b73eea35224))\n* **engine:** 🐛 fixed shadow UI Input ([3fbb753](https://github.com/ByteCrazy/chameleon/commit/3fbb753bb26b03dd5a25bce0fc6621f2d8b778e1))\n* **material:** 🐛 fixed GridItem copy ([70f9f68](https://github.com/ByteCrazy/chameleon/commit/70f9f6811da3dd46707e0a7304182b807eb745c9))\n* **material:** 🐛 remove inner pkg version ([eefcf7e](https://github.com/ByteCrazy/chameleon/commit/eefcf7e11e8405e1d96fdf0c1abdd8959473db1c))\n* **model:** 🐛 fixed export meta repeat ([614bedd](https://github.com/ByteCrazy/chameleon/commit/614beddb464f6e66d1d77bcccd1f9b996702c344))\n\n### ✨ Features | 新功能\n\n* **build-script, demo-page, engine, layout, render:** 🎸 add lang switch ([29da65e](https://github.com/ByteCrazy/chameleon/commit/29da65ee1aa09550d910ddfbbcb9d8b4db983373))\n* **demo-page, engine, engine-website-app, layout, material, model:** 🎸 add hot action ([c7a405b](https://github.com/ByteCrazy/chameleon/commit/c7a405b35fedcbe47e163d32a8550bb82c9ae7d6))\n* **demo-page, engine, engine-website-app, material:** 🎸 add designerSizer and fixed GridItem bug ([4665aed](https://github.com/ByteCrazy/chameleon/commit/4665aed300d54c77be4abcb9a8cc0f1710ac2145))\n* **demo-page, engine, render:** 🎸 add getGlobalState and optimize node udpate ([4d3934f](https://github.com/ByteCrazy/chameleon/commit/4d3934fd8febe616a44e5d39da0e10964f3c800d))\n* **docs-app, engine, layout, material, model, render:** 🎸 optimize GL layout ([02274b4](https://github.com/ByteCrazy/chameleon/commit/02274b432903dc247c5613873f14715dd806decd))\n* **docs-app:** 🎸 use docs-app to do doc website ([2504946](https://github.com/ByteCrazy/chameleon/commit/25049463fdd2de507f19017be4e5269b7b5f7a2d))\n* **engine-website-app, material:** 🎸 optimize RGL ([dd04999](https://github.com/ByteCrazy/chameleon/commit/dd04999a1509e29d6d738ea3a4250a146bfc295e))\n* **engine, layout:** 🎸 add set canvas width method ([a18369f](https://github.com/ByteCrazy/chameleon/commit/a18369f0d4bbb4bcf04ce2695be313d767d4bbe5))\n* **engine, material:** 🎸 add GRL meterial ([ae15c34](https://github.com/ByteCrazy/chameleon/commit/ae15c34a3f2736db61a933dc7d09166d9a619473))\n* **engine, model:** 🎸 add advanceOptions property ([d75f178](https://github.com/ByteCrazy/chameleon/commit/d75f178fa70d4c49b451dff9dddfb30d4c061196))\n* **engine, model:** 🎸 add hotKey plugin and fixed reloadPage event not trigge ([1cbf1e1](https://github.com/ByteCrazy/chameleon/commit/1cbf1e1a345d94c3b758b26cfbf1ecde69ce051c))\n* **engine:** 🎸 add canvas size change button ([19ebdf8](https://github.com/ByteCrazy/chameleon/commit/19ebdf8d635b6b412979db81dc5d4f1b43d793a9))\n* **engine:** 🎸 add hotAction ([c82bd21](https://github.com/ByteCrazy/chameleon/commit/c82bd21243aed9371a8576f572f600f1894f60bf))\n* **engine:** 🎸 add width input ([c38422d](https://github.com/ByteCrazy/chameleon/commit/c38422d0848c4d60e8cb5b5b480671515c1d6101))\n* **engine:** 🎸 optimize hotkeys methods ([bc83dba](https://github.com/ByteCrazy/chameleon/commit/bc83dba17fa4a23fa25548a284bbd69858bf15a0))\n\n### 📝 Documentation | 文档\n\n* **demo-page, engine-website-app:** ✏️ demo app add switch page ([c098997](https://github.com/ByteCrazy/chameleon/commit/c098997e2c53bbe135ce263fcd1912ddc53146fe))\n* **docs-app, engine-website-app:** ✏️ fixed doc url path ([181aa3f](https://github.com/ByteCrazy/chameleon/commit/181aa3f0fe1f329d8c2e8bd83759781468edcf5d))\n* **docs-app:** ✏️ update doc link ([dc5759d](https://github.com/ByteCrazy/chameleon/commit/dc5759dd962ac910df3f3a1650ec48f2d7ee92d8))\n* **engine:** ✏️ add layout resource ([15f4325](https://github.com/ByteCrazy/chameleon/commit/15f4325e418d63bb3f046d58766e9bb1d2c96344))\n* **engine:** ✏️ update md resource ([de95460](https://github.com/ByteCrazy/chameleon/commit/de954609bbc4dc24dbf704d809ffb1de9a9116b2))\n\n## [0.0.46](https://github.com/ByteCrazy/chameleon/compare/v0.0.45...v0.0.46) (2024-06-30)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed registerCustomSetter method not valid ([9bdaa3b](https://github.com/ByteCrazy/chameleon/commit/9bdaa3b9feb1610a754b9aaa0925530f474dd3c3))\n\n## [0.0.45](https://github.com/ByteCrazy/chameleon/compare/v0.0.44...v0.0.45) (2024-06-30)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **model:** 🐛 fixed addMaterials method logic ([b7b1d14](https://github.com/ByteCrazy/chameleon/commit/b7b1d14737b2b25362809fc15a7b9ae25cba18fc))\n\n## [0.0.44](https://github.com/ByteCrazy/chameleon/compare/v0.0.43...v0.0.44) (2024-06-30)\n\n### ✨ Features | 新功能\n\n* **engine, model, render:** 🎸 optimize addMaterials and fixed inner mat schema ([aa77803](https://github.com/ByteCrazy/chameleon/commit/aa77803c75b203ee2a098fbee143f4a9581e15fa))\n\n## [0.0.43](https://github.com/ByteCrazy/chameleon/compare/v0.0.42...v0.0.43) (2024-06-29)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, layout:** 🎸 remove scss dts generate ([9ba7af3](https://github.com/ByteCrazy/chameleon/commit/9ba7af30601804f94e90a0408745fd48de38d8b5))\n\n## [0.0.42](https://github.com/ByteCrazy/chameleon/compare/v0.0.41...v0.0.42) (2024-06-01)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, layout:** 🐛 fixed drag and drop problem on chrome ([5c655c3](https://github.com/ByteCrazy/chameleon/commit/5c655c3a2f288bd90b70212f0f114adf3c23f8a1))\n\n## [0.0.41](https://github.com/ByteCrazy/chameleon/compare/v0.0.40...v0.0.41) (2024-05-26)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, layout, material, model, render:** 🎸 optimize build script ([0a60cc4](https://github.com/ByteCrazy/chameleon/commit/0a60cc4f5e5635d9e544b5141eaed5b8433901a5))\n\n## [0.0.40](https://github.com/ByteCrazy/chameleon/compare/v0.0.39...v0.0.40) (2024-04-28)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* fixed collectVariable logic ([e51bce9](https://github.com/ByteCrazy/chameleon/commit/e51bce9db030f8657575900406200972eee2e928))\n\n## [0.0.39](https://github.com/ByteCrazy/chameleon/compare/v0.0.38...v0.0.39) (2024-04-27)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **build-script, demo-page, engine, layout, material, model, render:** 🐛 fix flatObject collectVariable refeer pos ([1c4163a](https://github.com/ByteCrazy/chameleon/commit/1c4163aeeb89176a1ff5b50f76930889df7751fe))\n\n## [0.0.38](https://github.com/ByteCrazy/chameleon/compare/v0.0.37...v0.0.38) (2024-04-27)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, layout:** 🐛 fixed assets load iuess when relaod ([3eccc60](https://github.com/ByteCrazy/chameleon/commit/3eccc60bb7be5a469704a9c4f769161e525481f4))\n\n## [0.0.37](https://github.com/ByteCrazy/chameleon/compare/v0.0.36...v0.0.37) (2024-04-27)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed updateMaterials components map problem ([b868e88](https://github.com/ByteCrazy/chameleon/commit/b868e884a9b65021effdee8872a462fd8539c9c4))\n\n## [0.0.36](https://github.com/ByteCrazy/chameleon/compare/v0.0.35...v0.0.36) (2024-04-27)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **render:** 🐛 fixed collectVariable method bug ([d5b7c5f](https://github.com/ByteCrazy/chameleon/commit/d5b7c5f274da52de09875cb9a9acf263ff051e59))\n\n## [0.0.35](https://github.com/ByteCrazy/chameleon/compare/v0.0.34...v0.0.35) (2024-04-27)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed onPluginReadyOk return value ([8550591](https://github.com/ByteCrazy/chameleon/commit/85505913af29459882caef5b83d17c2a8aca45b3))\n\n### ✨ Features | 新功能\n\n* **render:** 🎸 support comp name  with '.' and adapte __esModule prop ([0020e9e](https://github.com/ByteCrazy/chameleon/commit/0020e9e0db01cc08895c042c54253d133676a672))\n\n## [0.0.34](https://github.com/ByteCrazy/chameleon/compare/v0.0.33...v0.0.34) (2024-04-27)\n\n### ✨ Features | 新功能\n\n* **docs-website, engine, model:** 🎸 add update assetsPackageList logic ([2bce2f4](https://github.com/ByteCrazy/chameleon/commit/2bce2f4758b203695ec119d6e201e3186d7fab84))\n\n## [0.0.33](https://github.com/ByteCrazy/chameleon/compare/v0.0.32...v0.0.33) (2024-04-27)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fix css editor initial value not correct problem ([96c3e58](https://github.com/ByteCrazy/chameleon/commit/96c3e58755ed4fdfa3e200f3049b5024cb9c6719))\n\n### ✨ Features | 新功能\n\n* **engine, layout, render:** 🎸 optimize history step save ([dca964a](https://github.com/ByteCrazy/chameleon/commit/dca964a990a3cc0c5fd853d853a55a4957999644))\n* **engine, render:** 🎸 designer plugin add  updateComponent method ([f161177](https://github.com/ByteCrazy/chameleon/commit/f16117793402681a1eda8f8cba9c06e2ee5247ad))\n\n## [0.0.32](https://github.com/ByteCrazy/chameleon/compare/v0.0.31...v0.0.32) (2024-01-30)\n\n### ✨ Features | 新功能\n\n* **docs-website, engine, model, render:** 🎸 add updateMaterials、updatePage methods ([39ed2b6](https://github.com/ByteCrazy/chameleon/commit/39ed2b692a8a7379a79b96cb3fd8cdb76a4f01f2))\n\n## [0.0.31](https://github.com/ByteCrazy/chameleon/compare/v0.0.30...v0.0.31) (2023-10-17)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed arraySetter delete bug ([1b53538](https://github.com/ByteCrazy/chameleon/commit/1b53538a67ff35a6cfedffe661126f6912e78bbf))\n\n## [0.0.30](https://github.com/ByteCrazy/chameleon/compare/v0.0.29...v0.0.30) (2023-10-17)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **render:** 🐛 remove child node cache ([0bca7b6](https://github.com/ByteCrazy/chameleon/commit/0bca7b6b1577c79e219f57ff9699b49000bb17ab))\n\n## [0.0.29](https://github.com/ByteCrazy/chameleon/compare/v0.0.28...v0.0.29) (2023-10-17)\n\n### ✨ Features | 新功能\n\n* **build-script, engine:** 🎸 optimize css editor ([66c0541](https://github.com/ByteCrazy/chameleon/commit/66c0541eaee12b2eb0ceb4fb3a7748e1bf69768d))\n* **demo-page, engine, model:** 🎸 add ant color setter ([830de46](https://github.com/ByteCrazy/chameleon/commit/830de46babdf40a740248c37d8e6dc2043db1434))\n* **demo-page, engine, model:** 🎸 add ColorSetter ([e72b7f1](https://github.com/ByteCrazy/chameleon/commit/e72b7f15dd8bba498b776226d5debd07c8fd4234))\n* **demo-page, engine, model:** 🎸 add radio setter ([e6f9b1d](https://github.com/ByteCrazy/chameleon/commit/e6f9b1d9dba7cd3a9a82116bf5a1068c06a5a1d6))\n* **demo-page, engine:** 🎸 add cssSize setter ([74bf136](https://github.com/ByteCrazy/chameleon/commit/74bf136047000a67795e1360f2a923902366a513))\n* **demo-page, engine:** 🎸 add slider setter ([0fe1d29](https://github.com/ByteCrazy/chameleon/commit/0fe1d29257f214e69ca9fed6e5e4c11ccccb56bb))\n* **demo-page, render:** 🎸 compatible new style schema ([8165453](https://github.com/ByteCrazy/chameleon/commit/816545302e4d88a0976e8fdf3b17ed8a9a2978bd))\n* **engine, model, render:** 🎸 cssEditor support cssText ([3dc74b4](https://github.com/ByteCrazy/chameleon/commit/3dc74b4895d414a718c00911a39d8491fafcfaee))\n* **engine, model:** 🎸 optimize style editor ([9c660ce](https://github.com/ByteCrazy/chameleon/commit/9c660ce694a32059450f19c824bcde9889049db8))\n* **engine, model:** 🎸 style varible、c s scss editor support styles text ([c988fcf](https://github.com/ByteCrazy/chameleon/commit/c988fcf17a2dbbc486e809a03c642726f02cf547))\n* **engine:** 🎸 add border input ([ae7d3c3](https://github.com/ByteCrazy/chameleon/commit/ae7d3c3e1c07bf68cfa74ef15ab8f499c9b7afc3))\n* **engine:** 🎸 add CSSSizeInput componrnt ([cd70933](https://github.com/ByteCrazy/chameleon/commit/cd70933fbb044058b65c402465a69139da4d8a4a))\n* **engine:** 🎸 add DimensionInput、FontInput、MarginInput、PaddingInput ([08aa4e5](https://github.com/ByteCrazy/chameleon/commit/08aa4e5a959ace882312b77a15ab5c973d2015b8))\n* **engine:** 🎸 CSSUI editor sync value ([549fa7e](https://github.com/ByteCrazy/chameleon/commit/549fa7ea676d9f31d10e3b78888ec2318df2cdc2))\n* **engine:** 🎸 optimize style setter ([eba569c](https://github.com/ByteCrazy/chameleon/commit/eba569cb5871f6cf8cce459c69d3d3beb7e0459a))\n* **engine:** 🎸 style UI finish ([a38fb84](https://github.com/ByteCrazy/chameleon/commit/a38fb846475631caa1a6253af7de11fd30027c48))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **build-script, engine:** 🐛 recocer build-script bin file ([5a2ca69](https://github.com/ByteCrazy/chameleon/commit/5a2ca69c4e5c48b3b0686478f6e5c40cd21c08ad))\n* **engine, render:** 🐛 fixed build error and add child cache for render ([9026474](https://github.com/ByteCrazy/chameleon/commit/90264746fe99b8cdd4d055f2d778e67aae38af87))\n* **engine:** 🐛 fixed MonacoEditor not trigger value change event ([0f6c3ee](https://github.com/ByteCrazy/chameleon/commit/0f6c3ee82a31b1a23388486f4cb12f9d7424ae66))\n* **engine:** 🐛 fixed updatePanelValue not dispose problem ([b648986](https://github.com/ByteCrazy/chameleon/commit/b6489860398fe672de0e8009d91525dcc54909f2))\n\n## [0.0.28](https://github.com/ByteCrazy/chameleon/compare/v0.0.27...v0.0.28) (2023-08-29)\n\n### ✨ Features | 新功能\n\n* **model, render:** 🎸 node support custom dropPlaceholder ([dabdbc0](https://github.com/ByteCrazy/chameleon/commit/dabdbc00b375d5b8491ff3498075caac45473a98))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed click can't add component into canvas ([b8e8c2c](https://github.com/ByteCrazy/chameleon/commit/b8e8c2c703f20c2bc2836dc2e1f91b02b994e5ca))\n* **layout:** 🐛 fixed not auto inert component when drag on a empty aera ([45d9ae3](https://github.com/ByteCrazy/chameleon/commit/45d9ae3b0b874a225e068f378f6be07584bb2f87))\n\n## [0.0.27](https://github.com/ByteCrazy/chameleon/compare/v0.0.26...v0.0.27) (2023-08-28)\n\n### ✨ Features | 新功能\n\n* **engine:** 🎸 plugin system support add addCustomView method ([ce6db8f](https://github.com/ByteCrazy/chameleon/commit/ce6db8fcf1b87e6e3e14ed7464e7615c0ecc852b))\n\n## [0.0.26](https://github.com/ByteCrazy/chameleon/compare/v0.0.25...v0.0.26) (2023-08-27)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **render:** 🐛 fixed process is not defined in render ([03e258f](https://github.com/ByteCrazy/chameleon/commit/03e258f5674bdb54da6dee8dd7a68c31f7432a69))\n\n## [0.0.25](https://github.com/ByteCrazy/chameleon/compare/v0.0.24...v0.0.25) (2023-08-27)\n\n**Note:** Version bump only for package chameleon\n\n## [0.0.24](https://github.com/ByteCrazy/chameleon/compare/v0.0.23...v0.0.24) (2023-08-27)\n\n### ✨ Features | 新功能\n\n* **engine, model, render:** 🎸 fixed node maybe is null on rightPanel ([aee551e](https://github.com/ByteCrazy/chameleon/commit/aee551e77454f61900318003f9e3a3ffb1ef9427))\n\n## [0.0.23](https://github.com/ByteCrazy/chameleon/compare/v0.0.22...v0.0.23) (2023-08-26)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, render:** 🐛 fixed CSSPropertiesVariableBindEditor value not update ([9084c32](https://github.com/ByteCrazy/chameleon/commit/9084c32bb919c2e6191c29b871f9d1537d139050))\n\n### ✨ Features | 新功能\n\n* **engine, model:** 🎸 support custom rightPanel ([803d731](https://github.com/ByteCrazy/chameleon/commit/803d731819faa03430b2a17a154d3ff1e0daca28))\n* **engine, model:** 🎸 support inject custom setter ([a49ec4a](https://github.com/ByteCrazy/chameleon/commit/a49ec4ae4a98b42cf1cfb768990b64c981539881))\n\n## [0.0.22](https://github.com/ByteCrazy/chameleon/compare/v0.0.21...v0.0.22) (2023-08-24)\n\n### ✨ Features | 新功能\n\n* **demo-page, engine, layout, model:** 🎸 add disableEditorDragDom config ([1779f44](https://github.com/ByteCrazy/chameleon/commit/1779f44aff20370ea3336e72352032c6416f7dd3))\n\n## [0.0.21](https://github.com/ByteCrazy/chameleon/compare/v0.0.20...v0.0.21) (2023-08-19)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **render:** 🐛 fixed assetsLoader resource is empty problem ([1e7ec0a](https://github.com/ByteCrazy/chameleon/commit/1e7ec0ab966c4b618638ac519401f4df5cfc8f82))\n\n### ✨ Features | 新功能\n\n* **engine, layout, model, render:** 🎸 optimize wrapComponent config ([d5916a7](https://github.com/ByteCrazy/chameleon/commit/d5916a7e6ee3cf79a32d4d23663b6873d86fe671))\n\n## [0.0.20](https://github.com/ByteCrazy/chameleon/compare/v0.0.19...v0.0.20) (2023-08-19)\n\n### ✨ Features | 新功能\n\n* **model, render:** 🎸 support wrapComponent meterial config ([73fa19c](https://github.com/ByteCrazy/chameleon/commit/73fa19cd698bd61ef8079ae71dfd284600796ad2))\n\n## [0.0.19](https://github.com/ByteCrazy/chameleon/compare/v0.0.18...v0.0.19) (2023-08-17)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, render:** 🐛 fixed if assets is empty, loader will not success ([169d9ad](https://github.com/ByteCrazy/chameleon/commit/169d9ad9e5791353ad3a68dd0212c6ecfda64a29))\n\n### ✨ Features | 新功能\n\n* **engine:** 🎸 add HistoryPlugin type definition ([13a66fb](https://github.com/ByteCrazy/chameleon/commit/13a66fb5141b308af191394b6c377a0d387ea628))\n* **engine:** 🎸 use @monaco-editor/react replace monaco-editor ([848ee87](https://github.com/ByteCrazy/chameleon/commit/848ee87dedbccc71d3a7366320ad03122ed15d38))\n\n## [0.0.18](https://github.com/ByteCrazy/chameleon/compare/v0.0.17...v0.0.18) (2023-08-14)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **render:** 🐛 fixed page reload assets load failed ([b9eb815](https://github.com/ByteCrazy/chameleon/commit/b9eb815e75e1cd1738112ee18147f6faf0035155))\n\n### ✨ Features | 新功能\n\n* **engine:** 🎸 add className and style for engine ([c2b7ef3](https://github.com/ByteCrazy/chameleon/commit/c2b7ef3c1c68708963dece239528889701eb0fd7))\n* **engine:** 🎸 add onMount for engine ([17c80ae](https://github.com/ByteCrazy/chameleon/commit/17c80aecc09f13da5f33d7a59b5849d73d99d12a))\n\n## [0.0.17](https://github.com/ByteCrazy/chameleon/compare/v0.0.16...v0.0.17) (2023-08-06)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed types error ([1b68777](https://github.com/ByteCrazy/chameleon/commit/1b68777eb58225d750f40b16a7d263acb385477a))\n* **layout:** 🐛 fixed Cannot read properties of undefined (reading 'id') ([008655c](https://github.com/ByteCrazy/chameleon/commit/008655c35bb73a93c48f35bbbd5eac0a515d7163))\n\n## [0.0.16](https://github.com/ByteCrazy/chameleon/compare/v0.0.15...v0.0.16) (2023-08-06)\n\n### 👷 Continuous Integration | CI 配置\n\n* 🎡 update ci build ([081fbde](https://github.com/ByteCrazy/chameleon/commit/081fbde6bb840f3682e86ad7cbbcdc1e04e89c36))\n* 🎡 update ci script ([099f032](https://github.com/ByteCrazy/chameleon/commit/099f03220711d0542081f462c517aae8ce4dea32))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, layout:** 🐛 fixed some node not exits querySelector method ([2ddc186](https://github.com/ByteCrazy/chameleon/commit/2ddc1863e6eb207ae2e2b705a9fef4d23bece37c))\n\n### ✨ Features | 新功能\n\n* **build-script-example, demo-page, engine, layout, material, model, render:** 🎸 support direct import render from package on dev mode ([6bb38e2](https://github.com/ByteCrazy/chameleon/commit/6bb38e29678a8a4d08a7fcf9548fa548399259b0))\n* **demo-page, engine, layout, model:** 🎸 support  advanceCustom drag and drop hooks ([fb21e15](https://github.com/ByteCrazy/chameleon/commit/fb21e1501f6829e4f13a35ea7be942ff72b0be91))\n* **demo-page, engine, layout, model:** 🎸 support toolbarViewRender and ghostViewRedner ([43ced37](https://github.com/ByteCrazy/chameleon/commit/43ced371cfb7dbb58a96fc15e1bf635092307fa8))\n* **demo-page, engine, layout:** 🎸 support DropViewRender ([1c025a6](https://github.com/ByteCrazy/chameleon/commit/1c025a6d1f57f450b2de262a787375f3f4891008))\n* **demo-page, engine, model:** 🎸 support onDelete onCopy ([b6a76bb](https://github.com/ByteCrazy/chameleon/commit/b6a76bb244bbe2c050de17157bda97cb7ad21abc))\n* **engine, layout, model:** 🎸 add customDropViewRender prop ([cea7f1e](https://github.com/ByteCrazy/chameleon/commit/cea7f1e3c2b0a8a13aef7cdcb0191593414615aa))\n* **engine, layout, model:** 🎸 finish layout transform ([73ead35](https://github.com/ByteCrazy/chameleon/commit/73ead357b9aded2b5ee2b545da6f2b3677ce8393))\n* **engine:** 🎸 optimze plugin type definition ([da9c107](https://github.com/ByteCrazy/chameleon/commit/da9c107b20646714834642ad09b2cbdfcb6eb7cd))\n* **layout, model:** 🎸 optimize advanceCustom type definition ([0f529d1](https://github.com/ByteCrazy/chameleon/commit/0f529d1a8d8adbfd0970ba13a75a116a26e4d2f8))\n* **layout, model:** 🎸 optimize advanceCustom type definition ([8cd11aa](https://github.com/ByteCrazy/chameleon/commit/8cd11aa9fb0dca0b605d9ed8504c2e827109dc8f))\n* **layout, model:** 🎸 update meterial types definition ([fcf4e82](https://github.com/ByteCrazy/chameleon/commit/fcf4e827942afe4df8ad4210a0a3134138a5b10e))\n* **layout:** 🎸 select hightlight use outline ([00654d0](https://github.com/ByteCrazy/chameleon/commit/00654d082c2823a6b01f109571f25a18bc51f5c5))\n* **layout:** 🎸 setCanDrag add hook ([04ec3b6](https://github.com/ByteCrazy/chameleon/commit/04ec3b687310f689bbaaaa4a037b25d9ae5f8b9a))\n\n## [0.0.15](https://github.com/ByteCrazy/chameleon/compare/v0.0.14...v0.0.15) (2023-07-01)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, layout:** 🐛 fixed component will be add repeatedly after reload page ([1507549](https://github.com/ByteCrazy/chameleon/commit/1507549632c3c6e09d8555fd0286fcc72855c358))\n\n### ✨ Features | 新功能\n\n* auto detect toolbox dir ([#13](https://github.com/ByteCrazy/chameleon/issues/13)) ([4047408](https://github.com/ByteCrazy/chameleon/commit/404740870a658fa1740abe29ca50feac4a4028e4))\n* **demo-page, docs-website, engine, model, render:** 🎸 optimize assets load ([3ee6d58](https://github.com/ByteCrazy/chameleon/commit/3ee6d58a88e5af3fc631723240783d5c97a273b0))\n* **demo-page, engine:** 🎸 setter support initialValue ([794d807](https://github.com/ByteCrazy/chameleon/commit/794d8072518cb3b1a897b04a2e96f5ca53fdb365))\n* **docs-website, engine, layout, material, render:** 🎸 support inject thridLib on $context ([ca2f074](https://github.com/ByteCrazy/chameleon/commit/ca2f07492b713c32e5a41d1f250f7888763cb665))\n* **engine, render:** 🎸 change internal api ([0c0c7c3](https://github.com/ByteCrazy/chameleon/commit/0c0c7c3fa8f86c08ab0cefb0396107ed8c52dcd5))\n* **render:** 🎸 render support store reactive ([61c7485](https://github.com/ByteCrazy/chameleon/commit/61c74857ba9541bb20d3722d351019c0ad97ac81))\n\n## [0.0.14](https://github.com/ByteCrazy/chameleon/compare/v0.0.13...v0.0.14) (2023-05-10)\n\n### ✨ Features | 新功能\n\n* **engine:** 🎸 add node id in advance panel ([d457203](https://github.com/ByteCrazy/chameleon/commit/d45720316ab95f75f6f67a5dbf31bb0030ceb3f1))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, layout:** 🐛 fixed new component can not be add into canvas ([4057fa9](https://github.com/ByteCrazy/chameleon/commit/4057fa925c18bf06325de04b270be5f6c23351c8))\n* **engine, layout:** 🐛 fixed type problem ([751a392](https://github.com/ByteCrazy/chameleon/commit/751a39244228f74138acf7f567d23e888b8ff687))\n* **engine, render:** 🐛 fixed removeMediaCSS method run failed ([9127ec3](https://github.com/ByteCrazy/chameleon/commit/9127ec3d13f43a1c8763b2350bc0224d683da85c))\n\n## [0.0.13](https://github.com/ByteCrazy/chameleon/compare/v0.0.12...v0.0.13) (2023-05-03)\n\n### ✨ Features | 新功能\n\n* add-nearby-component ([85a301c](https://github.com/ByteCrazy/chameleon/commit/85a301c3d95e785c116ab38c0b3a452ceb33742f))\n* **build-script, build-script-example, demo-page, engine, layout, material, model, render:** 🎸 config code style config ([2325504](https://github.com/ByteCrazy/chameleon/commit/23255048fa4a3d4fc8f5cfa1312db5abd6cac70d))\n* **build-script, demo-page, engine, layout, model, render:** 🎸 update meterial schema ([14f4e5c](https://github.com/ByteCrazy/chameleon/commit/14f4e5caf0a4f6eca131e139d0c34ea21969dc0b))\n* **demo-page, docs-website, engine, layout, model, render:** 🎸 node support methods config ([e080b20](https://github.com/ByteCrazy/chameleon/commit/e080b20cea3cea27b95def196e078c1c225d3a83))\n* **demo-page, engine, layout, model:** 🎸 finish customEvent config ([84640e3](https://github.com/ByteCrazy/chameleon/commit/84640e3d06b79858590b9fe92ef4764fbe3f4f7b))\n* **demo-page, engine, layout:** 🎸 outline support cancel drag by node material ([cd428d3](https://github.com/ByteCrazy/chameleon/commit/cd428d362a8bfe7d5b6e58ed2b6290e19c00672b))\n* **demo-page, engine, layout:** 🎸 support cancel drag and custom node drag event ([825717e](https://github.com/ByteCrazy/chameleon/commit/825717e063846b72f95b0e41d8cba279ef5270c8))\n* **demo-page, engine:** 🎸 complete advanceCustom material feature ([98cf273](https://github.com/ByteCrazy/chameleon/commit/98cf273543f2fd534c254990d61052534a6649da))\n* **demo-page, engine:** 🎸 supprot onSelectNode ([f496a82](https://github.com/ByteCrazy/chameleon/commit/f496a82920cab7ae8704335b2ecd02a7887de3b2))\n* **engine, layout, model:** 🎸 support supportDispatchNativeEvent field ([489fd05](https://github.com/ByteCrazy/chameleon/commit/489fd05b588e85ed4cea81cc33ab27739a1bac59))\n* **engine, layout:** 🎸 optimize layout event system ([56f35ef](https://github.com/ByteCrazy/chameleon/commit/56f35ef83cc7e1658bfeba9081f997ff457cf09e))\n* **engine, layout:** 🎸 support isSupportDispatchNativeEvent filed ([4f830b4](https://github.com/ByteCrazy/chameleon/commit/4f830b42b6e84d74e9d462c3b3e090de89686e18))\n* **engine, render:** 🎸 html native tag component support container filed ([475fdbd](https://github.com/ByteCrazy/chameleon/commit/475fdbd5a34581ab7cafe67b5c9d3b94102b3a16))\n* remove self-executing function ([e224524](https://github.com/ByteCrazy/chameleon/commit/e224524198aa3f1339afd3bc9f1bacb72bc4d20e))\n* remove some useless code and fix some problems detected by eslint. ([a8e6ec2](https://github.com/ByteCrazy/chameleon/commit/a8e6ec2f107250facefc81cf87f694e40f3ed684))\n* select element after in add node ([0c0d116](https://github.com/ByteCrazy/chameleon/commit/0c0d1164783420da306062292674fbccb0a8ffb0))\n* update model code ([f74162a](https://github.com/ByteCrazy/chameleon/commit/f74162a32dda1fa4244705a0b63066d73a4d6537))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* adjust find node logic ([5f9f073](https://github.com/ByteCrazy/chameleon/commit/5f9f07320888b3a83a4a24b04cfbd2e9e82aa4b1))\n* **engine, layout, model:** 🐛 fixed type error ([d11e81f](https://github.com/ByteCrazy/chameleon/commit/d11e81f607ef6a41a2dfda0b4ac657a9a87e948c))\n* **engine:** 🐛 fixed insert node logic ([7fd4866](https://github.com/ByteCrazy/chameleon/commit/7fd4866d7daaece1c8c3b2a846b55da27a1e0936))\n* eslint warning ([403342f](https://github.com/ByteCrazy/chameleon/commit/403342f60420326a69e4b46a1560ba0f4845ed60))\n* **model:** page thirdLibs forget process find return ([e1c5d2b](https://github.com/ByteCrazy/chameleon/commit/e1c5d2b3a7e365f892274c80402a72b7003151a8))\n\n## [0.0.12](https://github.com/ByteCrazy/chameleon/compare/v0.0.11...v0.0.12) (2023-04-23)\n\n### 👷 Continuous Integration | CI 配置\n\n* 🎡 update publish script ([dab187a](https://github.com/ByteCrazy/chameleon/commit/dab187afcca315664284a4bc03d01a40a43e1cdf))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **build-script, build-script-example, engine, layout, material, model, render:** 🐛 fix can not find vite/client.d.ts problem ([8a53540](https://github.com/ByteCrazy/chameleon/commit/8a53540f75737707f69f93d2f701203dbf88084a))\n\n### ✨ Features | 新功能\n\n* **demo-page, docs-website, engine, model:** 🎸 update schema and change err to warn when schema check ([e753862](https://github.com/ByteCrazy/chameleon/commit/e7538626bad6681e4d488ac835fef61a603c0853))\n* **demo-page, engine, layout, model, render:** 🎸 node support config self isContainer property ([d94f1f9](https://github.com/ByteCrazy/chameleon/commit/d94f1f9203f2c273856fd4aa489a1ed9cbb0f0ec))\n\n## [0.0.11](https://github.com/ByteCrazy/chameleon/compare/v0.0.10...v0.0.11) (2023-04-21)\n\n### ✨ Features | 新功能\n\n*  add reload method of plugin ([3b2cb2a](https://github.com/ByteCrazy/chameleon/commit/3b2cb2aa3804e250ed8ce37027168282f63db879))\n* add replaceSubTopBarView method ([da32d9c](https://github.com/ByteCrazy/chameleon/commit/da32d9c67a6fff1a562886c027c1604a54e19b7e))\n* **build-script, build-script-example, demo-page, engine, layout, material, model, render:** 🎸 optimize build-script output file format ([eda44d2](https://github.com/ByteCrazy/chameleon/commit/eda44d255bd3c5af19013bdd61342e1f0817413a))\n* **build-script, docs-website, engine:** 🎸 add material develop doc ([0aacca0](https://github.com/ByteCrazy/chameleon/commit/0aacca0f726bc13606b814f8890b4e8ff8982142))\n* **engine, model:** 🎸 support custom setter ([5236c54](https://github.com/ByteCrazy/chameleon/commit/5236c543559aac517e833741ffa8046bc1c7f1a3))\n* RightPanel add replacePanel、removePanel methods ([ee6c2e1](https://github.com/ByteCrazy/chameleon/commit/ee6c2e1b45bf93075342cee060fe3038ec7b8e53))\n\n### 📝 Documentation | 文档\n\n* **docs-website, engine:** ✏️ add plugin、setter develop doc ([288edc4](https://github.com/ByteCrazy/chameleon/commit/288edc4999f732ff2f27b1c6e50900b9b0094700))\n* **docs-website, model:** ✏️ add material setter doc ([c7490fb](https://github.com/ByteCrazy/chameleon/commit/c7490fb9c9e9ef4cba9e33c5270a035592e3c46e))\n\n## [0.0.10](https://github.com/ByteCrazy/chameleon/compare/v0.0.9...v0.0.10) (2023-04-17)\n\n**Note:** Version bump only for package chameleon\n\n## [0.0.9](https://github.com/ByteCrazy/chameleon/compare/v0.0.8...v0.0.9) (2023-04-17)\n\n### ✨ Features | 新功能\n\n* **build-script:** 🎸 build.config supprot es module ([275c97d](https://github.com/ByteCrazy/chameleon/commit/275c97d861b6394fbdd13b92fd1049c208f72b39))\n* **docs-website, engine, render:** 🎸 render add collectVariable flatObject util method ([ae25160](https://github.com/ByteCrazy/chameleon/commit/ae25160b568c267041b3827e836c95f60ecaee59))\n\n## [0.0.8](https://github.com/ByteCrazy/chameleon/compare/v0.0.7...v0.0.8) (2023-04-16)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, layout, model, render:** 🎸 optimize pack package ([78d99c5](https://github.com/ByteCrazy/chameleon/commit/78d99c507ad65ca39101f0239467737d2b445c87))\n* **docs-website, engine, layout:** 🎸 add defaultRender for engine, add some docs ([91f4257](https://github.com/ByteCrazy/chameleon/commit/91f4257e9f9b9391267e4b8d64e6ed811912381f))\n* **docs-website, engine:** 🎸 add docs ([0e6f605](https://github.com/ByteCrazy/chameleon/commit/0e6f6053fa3cd5e13b6a7a8258c27518c30470c5))\n* **docs:** 🎸 add doc home page ([29efec1](https://github.com/ByteCrazy/chameleon/commit/29efec168b71879ee58809d3be9e8f7084ca4d88))\n* **docs:** 🎸 init docs project ([3331cec](https://github.com/ByteCrazy/chameleon/commit/3331cec7c7ed664e21270b7b98d7dc5f32574da9))\n\n### 👷 Continuous Integration | CI 配置\n\n* 🎡 modify ci flow ([ab9c47e](https://github.com/ByteCrazy/chameleon/commit/ab9c47e2f0492e8db1fdf8dcbddbec2a1801e1dd))\n* 🎡 update ci ([085f0ba](https://github.com/ByteCrazy/chameleon/commit/085f0ba25586ba34cb3525edab488f30cd1ed790))\n* 🎡 update ci ([32a5110](https://github.com/ByteCrazy/chameleon/commit/32a511054c733e277112d37f3e3f146bab41c3c2))\n\n### 📝 Documentation | 文档\n\n* **build-script, docs-website:** ✏️ change quickStart md ([f43945f](https://github.com/ByteCrazy/chameleon/commit/f43945fdd7eaf03b4ad3099980d7f3fb0a949b4c))\n* **docs-website, engine:** ✏️ add doc ([2fb5cf5](https://github.com/ByteCrazy/chameleon/commit/2fb5cf5fd4dcb3859ecbdc8d42adf787d9e0255d))\n* **docs-website:** ✏️ add doc project directory structure ([cc175e0](https://github.com/ByteCrazy/chameleon/commit/cc175e0bbce602ecc20433517e4bef9c8184f18f))\n\n## [0.0.7](https://github.com/ByteCrazy/chameleon/compare/v0.0.6...v0.0.7) (2023-03-29)\n\n### ✨ Features | 新功能\n\n* **build-script, build-script-example, demo-page, engine, layout, material, model, render:** 🎸 external monaco eitor and remove style panel ([494d898](https://github.com/ByteCrazy/chameleon/commit/494d898fd75dabe84d867ff45e84ae63f9b59c5e))\n\n## [0.0.6](https://github.com/ByteCrazy/chameleon/compare/v0.0.5...v0.0.6) (2023-03-28)\n\n**Note:** Version bump only for package chameleon\n\n## [0.0.5](https://github.com/ByteCrazy/chameleon/compare/v0.0.4...v0.0.5) (2023-03-28)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, layout, material, model, render:** 🎸 complete package info ([86b095d](https://github.com/ByteCrazy/chameleon/commit/86b095da6c955946300591b1775f59c1a141c065))\n\n## [0.0.4](https://github.com/ByteCrazy/chameleon/compare/v0.0.3...v0.0.4) (2023-03-27)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed engine d.ts path not correct problem ([fb20ca6](https://github.com/ByteCrazy/chameleon/commit/fb20ca604fff4e85b0264eff6383154ecfefd437))\n\n## [0.0.2](https://github.com/ByteCrazy/chameleon/compare/v0.0.1...v0.0.2) (2023-03-27)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fix engine not incluce dist folder ([fc2c39e](https://github.com/ByteCrazy/chameleon/commit/fc2c39e88aa2eeb82ba5e3989e5bdf244d112dfb))\n"
  },
  {
    "path": "DEV_README.md",
    "content": "# chameleon\n\n## Dev steps\n\n```shell\ncd ./\npnpm i\npnpm run build\ncd package/engine\npnpm run start\n```\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# Chameleon\n\n> Chameleon is ever-changing\n\nA web visual programming engine, help to build a web page with 5 minutes. every people can use it easy.\n\n[Docs](https://hlerenow.github.io/chameleon/documents/) | [Demo](https://hlerenow.github.io/chameleon/)\n\n[Online(Beta) Service](https://cb.hai-fe.com)\n\n## Install\n\n```shell\nnpm i @chamn/engine @chamn/model @chamn/render\n```\n\n## Usage\n\n[Example Project](https://github.com/ByteCrazy/chameleon-demo)\n\n### ScreenSnapshot\n\n<img width=\"1776\" alt=\"image\" src=\"https://github.com/user-attachments/assets/7b06dc4c-80a3-455d-bc91-14a1cf1fb331\">\n\n\n![image](https://github.com/hlerenow/chameleon/blob/master/packages/engine/md-images/layout.gif)\n\n![image](https://user-images.githubusercontent.com/13299648/218920783-0d1cc275-a238-4d80-a717-dbbbf54b4713.png)\n\n![image](https://user-images.githubusercontent.com/13299648/218920845-0c4c549d-df56-4b0a-9b72-95dd0c0fcaf5.png)\n\n![image](https://user-images.githubusercontent.com/13299648/218921002-a25cfdd6-f27a-4b19-83fe-a6a264e4e4b5.png)\n\n![image](https://user-images.githubusercontent.com/13299648/218920640-9be3b1ba-1dc2-42c5-922f-f3c5f97a9d96.png)\n"
  },
  {
    "path": "changelog.config.cjs",
    "content": "module.exports = {\n  disableEmoji: false,\n  // format: '{type}{scope}: {emoji}{subject}',\n  list: ['test', 'feat', 'fix', 'chore', 'docs', 'refactor', 'style', 'ci', 'perf'],\n  maxMessageLength: 64,\n  minMessageLength: 3,\n  questions: ['type', 'scope', 'subject', 'body', 'breaking', 'issues', 'packages'],\n  types: {\n    chore: {\n      description: 'Build process or auxiliary tool changes',\n      emoji: '🤖',\n      value: 'chore',\n    },\n    ci: {\n      description: 'CI related changes',\n      emoji: '🎡',\n      value: 'ci',\n    },\n    docs: {\n      description: 'Documentation only changes',\n      emoji: '✏️',\n      value: 'docs',\n    },\n    feat: {\n      description: 'A new feature',\n      emoji: '🎸',\n      value: 'feat',\n    },\n    fix: {\n      description: 'A bug fix',\n      emoji: '🐛',\n      value: 'fix',\n    },\n    perf: {\n      description: 'A code change that improves performance',\n      emoji: '⚡️',\n      value: 'perf',\n    },\n    refactor: {\n      description: 'A code change that neither fixes a bug or adds a feature',\n      emoji: '💡',\n      value: 'refactor',\n    },\n    release: {\n      description: 'Create a release commit',\n      emoji: '🏹',\n      value: 'release',\n    },\n    style: {\n      description: 'Markup, white-space, formatting, missing semi-colons...',\n      emoji: '💄',\n      value: 'style',\n    },\n    test: {\n      description: 'Adding missing tests',\n      emoji: '💍',\n      value: 'test',\n    },\n    messages: {\n      type: \"Select the type of change that you're committing:\",\n      customScope: 'Select the scope this component affects:',\n      subject: 'Write a short, imperative mood description of the change:\\n',\n      body: 'Provide a longer description of the change:\\n ',\n      breaking: 'List any breaking changes:\\n',\n      footer: 'Issues this commit closes, e.g #123:',\n      confirmCommit: 'The packages that this commit has affected\\n',\n    },\n  },\n};\n"
  },
  {
    "path": "design/chameleon.drawio",
    "content": "<mxfile host=\"Electron\" modified=\"2023-05-09T14:29:55.352Z\" agent=\"5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/20.3.0 Chrome/104.0.5112.114 Electron/20.1.3 Safari/537.36\" etag=\"dS-NkhxV7Sr9prfl54vX\" version=\"20.3.0\" type=\"device\" pages=\"2\"><diagram id=\"TcPI6cIad4Egc41Xfp0x\" name=\"工具栏展示逻辑\">7Zpdk5owFIZ/DZfukCAfXq667s50tnbGfk3vIgRIBcKGUN3++iYQUEBntzOrrC1eKDlJCDnvk5xjVDNm8e6eoTR8pB6ONKh7O82YaxBCywHiQ1qeSwsAll5aAkY8ZdsbVuQ3VsaqWU48nDUackojTtKm0aVJgl3esCHG6LbZzKdRc9QUBbhjWLko6lq/EY+HpdWB9t7+gEkQ8np+k7ImRlVjNZMsRB7dHpiMO82YMUp5eRXvZjiS3qv8UvZbnKitH4zhhL+mw/d1Ov6afPxhPzmjDz+f11+eHozRuLzLLxTlasLqYflz5YFtSDhepciV5a2QWTOmIY8jUQLiEmVp6Xef7LAYapptMHdDVRsiN8wZvpe95mNh8GnCFygmkQTilrny5i7PxJBzlAs3YqYarWjOiiFDzoXW0DRuxZuYnXyTDbKbgNIgwigl2Y1L46LCzYqmC78cQlw2BjHhtD1MSZzwnXhyzugGz2hEWTF1Q74Wwp9T5SXMON6ddD+oRRXLAdMYcyaeQFcdRpahQKiWAjTL8nbPlemoNuEBU7URKZaD+uZ7ucWFUvwv1Icd9Wc0Tmkip9TGgNE88aTAc+mqF6C4XgZ6VbreHN9caaOj9Gexi04R+1919kkUHax0D2HHd4/tAZbr4LX/RmTUYU2RAZ0uGTUEh2QY5wLD7IDxSUZEqM+WCUckwV1CxPx5C4OG0xKxgbQ8rEwoIkEiiq7wV6GK9CYRwfZWVcTE8+QwR7lrknm96J1ji5n0zZE9JBIXSiQssxVdKk37yyOcIY84wyJvC22arxT6fGnEZEgj3kEaAXS7RQbsknHZ7b/Ka4Y84qq3GKD3DhIYEokLJRLABu8ukwDDkcQ51jmwdachtXEkYlw4lwDDmcR7SCYggE004KT3GNA9lR6SiWvYZNrxpP9sonu8tUw0+RNS2iEoC1GKCyz4KbEP6FojdxMU4i9zHhFJUmH3ENssRS/CpRP0G918EYvO8taL178PzMiGLWCOHJQfDUr62YCxjgMzpZxLrw3M9MyMMWkhM+7uMdC8LDPds8+CmccybgzM9M0MGFsvf9F5M2hEcf+jflF38N8I4+4P</diagram><diagram id=\"R30iJOuWzjQSZIw3P-wu\" name=\"Style 面板\">7LzXsvNMci34NH05E4QhzCW8IUB4wtzBe4DwIJ5+qrj390ut1jmhMeqjMyHGNiRMmaw0a2Ul+DeM609pjt+VPmZ59zf0lp1/w/i/oSiCowT4B498fo7QJPpzoJzr7Peifzng1Ff+e/D2e3Srs3z5uwvXcezW+v33B9NxGPJ0/btj8TyPx99fVozd3/f6jsv8Hw44adz941G/ztbq5yiFkv9yXM7rsvrTM0LQP2f6+M/FvzNZqjgbj391CBP+hnHzOK4/7/qTyzsovD9y+blP/B+c/Wtgcz6s/5EbCuzDHa3mGskVmjKfxytx/R+/rexxt/1O+Hew6+ePBOZxG7IcNnL7G8YeVb3mzjtO4dkDrDk4Vq19Bz4h4O3S5mta/X6o4rTa5lyCF/E4PLvOY5tzYzfO37ax2/cFzhTjsIpxX3dQTZg5hb2k6wJGwscbkG4+/17kjNv87btaV6AC6B1jwB8wafgHXrD8n+U4ll0ev+vl/0zH/nsiXb6XisVPF+Dt33VyR9l/282PIqJwbL8iyuc1P/+Hskf+WlFgCvnY5+sM+rn9ueH+qwSfP9r9+/n4F53CyN9j1b/SJ/LPwfhXj8u/2v6XpQZvflf7/8bKo/+w8k7dv8Gi/zPXH8NoWhT/i69/UXfdvxozQYjid8z/JL1A6H9HL/D/LLXA/kEt3vNY5MtSj0Pc/bdz+E9wDhj+H1AC6p+pBPg/KMGrXra4M+Mh/0cdADNf/81C/91SDuOQ/xsz+j0Ud3U5gI8pkNRXulCONQi+zO+Jvs4y2M2/q1l/r3v/c+X6/7kK/Vs/cv93NOjfCy/Yf5YG3f/X4gqaILGY+C++7v8mruRIds/J/6S4gv9H8cZ/mkYQ/400/xmegCD+yyFN8r+R5v8Dj/AbIv9JKvHPBZnUf4PM/zeRAsP+v2Mg1H+EgfxTwSf939DhfyV0+Adv8b8eOvzJBf7PVGKp4jd8W/ffhN5fTEKLk7wzx6VegW8B55NxXcEKYGwHT7Bx2pZfbfojzSwv4q1b/x0uso5QseLl/ZNmLOoTaiD77ZD5c/T25whsKl7jv2HMz0dUfA/l31CufrGGfdweUjky4PV0vErwSvAu9MAf48MxIfjPpQfV3cCbB+53vIWwqnXTS09W96jvlshiGEfpX92ReR7SxqVBk4QjVJagCG/FzVmjFVpWJR6KehNU32JZ9VEzHXvEuN5snKJw6lNORVuwXrJ946Wyqw+l51mz5vhSUwyx/4wkY/g6/ySzvVgG7HntYAYGXMbvD9kNwV78DWUdirlznOGrh/Q4JEtbJq8yu7veqzV+1rgVMhI6MlyjMH31WV7LTeXLg6l10MazuBc3mhEwY/3c+qLASBAWRGp6NEBj2I5ZGFGhD6UFBx+ul+Oi3e+bpKpqyY0hXuXFze9zVdsV4eMqTFubvKt+MpOKIsV9n/X6lDxXyGNxYQQmZx+ycmulUnJEgu0iTHIF/tN6obPY7qfich7jku5gKzw9hKrQHaBxotkbll0JNyu/BCZiZ+7Sre/0We8Cf2rwnha2gDJUZDBu2OvJZffkwfpDmmjg3IUU1/7xaNcN3eMoI1WRBfP4RETakBvgR2wsONXg5RrXsq1PTcnyRsqSAtybTcOY9B4oIeX2tVvmEK/Ki9XvR0tLAWzadz4KGofwracOqKja75J6vTWhvgJe1rslELvpLR47rpnAusRBF4RMuAbO1/2m4W8rGJDYJhrZ5Uq5uXeKCkCvwWCcu/9C7vgG7nkZtDVHVBKH5dHRuYGmaEESyjNfXak2zSYtMPrdI3XkJvWl0/dEFuJY/OQYEQ/IsV/WFMwUIjy3YkE7/m7cropVXEM3GcmsGQX054qMqjFsttSH3VhlS1KflFTC+TjPtXVCTa/aRWXcE80P+3VpuXUkj3NNGC4xAaQUHT9nFzRz8Aq0tXZRkEbgTcTBwdvUXb/Fx7ZYDvNuDgZhSYMoL5QReJWNubNxNjcSsPmmPsHVksAKe7ncRuVOybBllktYJh10RlsY/gN0vFIYRWA2nwEqXcpWyTg7E88Me1cEbmGxhWVfpUaUbEWxdsoetzIzS1Mr9Xl8GimbUsyMlIx2CCMYIUNLSlpyTG4rztTyW8dUb+YeMfqpuIXCHhN3TGz4YMRH1ToWL47PJFQeo/QYmXWUEQ+ITrSIvKRo9jUxK8GYL/agmYTgDb1s2wP4Ha7J+bvOPvsjFA8W9OuOcXBc3aHNx1MbjRl3noeCHflekkUV7BVUEibkJsbzZMfTlpE7Pfs+eqqVxmXbMQ4ivn3z0b5ecAZvW4srFJEQT0NHgbb6p/0ybV6zRLQM1ga/Cyktazd/t31aeBGCOdq7LdIScpOx0YPDR/izr+qLlw2JD3v2beh+KKvH3a75njXC+l6RvSLpwOGwp88dWYyfyGm+m5AU3qgYhHPWZCgTLQ8y1FCcyg5woXgQTQV0fNlYvWCjXPukho8rZHVtEpkyBt4EpQQnDKIk/NEaitMoezjeYEQND49oeGLiZaF+SptnBqNigee6s88H5y4qaVlJJUic8RSiWDlvkRda3lElNecL+aZ2eG3WPvUSWfcmqocn1q0iZuHccW3qkXV9exRHHducXj/vba6q+qqgt1Z0HqDfliUcNHL5sM7vfPNRhzbJ7Zgqgw8Ulirb8VS/HTVp4xx3Z9FZXtLpgzPCq/KY860xo/Np752oPbh0XD9j6Vhpp3y8TR1J259e2rsTH+btXX5eWRcbnnUPgn6mermbqDf/aMqXcnqFGN0fczs9P6X/KurpMUHPi4zD490TDTlF+chc1nJFjfSeAovrKzm20Zv38l4vd389nv5revtThNggFtWEECFPl9CDG1AY0SVpAXpEWoqP+8wP5LNzdzQlm7ZAjHjK4+ZECu0lre42DYgfwf4e+SQSLTa50JwtaQtj53L8h3TaQGKGcy5VinafYMs+0JcGhnlh6Mj5WxyH0XteH8st+VRGHHdz7g9qFk4IOZXPWH0C+CMmlrsK280lZjM+eSSQQaSYPEK93DwVrShhwjg8PmcbSNOD6OcZ9LC5cl4kY7FtKnEGk5vb803anYKwZbQv3iqMCAGRgManmm1a6Tj8iTdGQ66AMr0nmUnVrAK+iRVkBTWa+GE+7RaRLCqazv5qZEMncCrtI/aJtTDE9IgTYa4m9e7z6d1otB/8+OgTNDoel0Oq4T0x7qXWuEWS4Qh/mWYc3cdg9ym53Bw2XeoPoRnjAmN0UnXtlkmfD5BxoZj3tDC0+1yYYKxo1CX3V79mcNxt4j+ykEfsbfGJK0WjJkr725QYZNT6X7FlSfJcJ/BuvQwMz4Z9IdfVJUNzfdIbhgY0ksqOZTwtkueX2OBC/NUMqC4NumdE/r3yhkY1hts1DbcbUQwsmup31PzFFVW4SQ4xaGHk1yxlgejGlvsN/uswJOB7KDzkJDG72jgfhElx232CIN8X9tizARtGiLIYih9KfWnP6BEfzSzm+euNO9fdyGXyk+5ZhK/B1OwmSlHkjSxApFCG21SoJEHsLUDmLJ3cm32GoYTAPjFoMZCh0y4IOMQBW7QgDORwzS+X0X32eegvxutZPBSCcvlwlyJ0lnMxqcxyFPTUzWkthjiOr6ficIKcKM1d6Mc0bz43kQpCOxe16Z3S/KUb/Yn48oI/gxP2bsgHDeC5EHo2cAlPpfft5CV5k1mP6FSat4y0z0yi9eLZIIudNrSviGmInuBWuR43xTiV20p3PZWCUYjMLiNUsH9oNY20V03ct85u4mx2dGoZP1ejubVdCpPi8nVG4aF9TE3X1AD4TIttz1WGwdmn3QVGd1Q7XArxAVcmfKpzO4iOZ6rz0pLtmIrkqDpH2FgOUlaFitUDqWB37zk5xswiSn8jNEfK5JOw9vHdhkAygXVa19pEXhyg4ieFlnTPyPmk7tD72U7MtyXrONpbQCP6YdlbL12Ciw7xsX8+6Kxve1NP5UtFwmk+nXVXxbWbb10OwguI7mw+vPLqhYmvdcJeQYI02YrHSLTScSxhNTpHCLTu9YMc6EIjK+gzZp4nQHrsJPXntj6raC+09J13T23uEqiFK8R/EINApDpRq3upZkv7J4omFwa10MWOjQKmUW0PaFkfDa5oUBAYM03cDddXn1yj2UQaiODMhD+1uE1sVOTHF7n2BlaTSfHAlN3onCVQTxfTqCYXsoaL9XaH0Cm7eo+c70nc5WRmZ0eH0q9E7Fw/kO7WPmOUg/sl3hNdhL74zk9e3TSaG9aTcOUynHwMxUT6WAhNb5BxGGcwOPVhJWDMRaeaWJkoaZHgKiAkghfscdHzEBnKK4N7Ml8SN9k+sjsfhumGNLOPVKRvimfI0oVKjLF5spD2mMabXrUhH4d7wZBohbMlrjY7im+5MIDOqIy/rjwb1fu448EBV2NyO9W8NxnXZP1zvKHceyV71Ig375AGtHunfktCAL0P85EZxPuzm5OKv01hoXpsRfHuMrZE9NN8hzgzdy+ImTENre5ZrXUHDBiPuj7RgKnpbMtP2JinkNCxYamYpfOboIjgQpAm6FcimXk6DRO6yQxsdsA1eaLxcHrdZqz3lhiud7yxL2TYaJS8aOhV9wG78/tu0MmikViS1eSJ7Z8cYidoR7/aQ1olacnzSfemfUKnauLnfYDuRiC84e1RyIDAgBLt78QiaJ68w8hAknhyUY/CzOeBniFlCoqZ7HA5hfNyh3eoEyX54p+tMVQYKhsItRSNfHj7+Sk4d5cpeNMkvztqmwLzgrq88UKsX/Ph7yqBL5i/wpHuPElcOZ0CJzajtIwRX9wkCiBusDbUA/MkidO8oG40AwHYG8vjNaBFLK6hr8IFQ2fRgtkpeijrwle5SxWPdzeyIq+Vb85isOoUYOfPkHnUGcMyAQz2ReAd6hM7+p4zj2HYSPZNWWypjjnDqT5jszcB5z/c5HR2cbqtvLIPJrC3gw+RLvwZYh04qrgl8YtqM8JCclc5qSwUGPGwGcjfNLVOpPgT9vsalh8X50b+RW5g+dlCfHjNR0D6U6E7CUKPVmN4KogjuGDQXF/gKjONXCkqYVf4T4/nk22qj+Wt4ZMH7Jzhq84Xut71bswDsnsG/AmzN8PAk2Cm4CV9j4PXxHD/6jhbxQz/rz7zy891X5/zGr8HhQ5Qdxtn8k/G3suXncVSg6uKoEop6zm2KrytGx8MAIf4HJN4rwyILjWmkCFIGNEqRA+FyAbEn1Oryx3L0mFiWxOlmjWg90qAi8g/OMYPposZFHVR10qW7wy4GMx4+cM0b5ivvW9xJKrPWrbw5T715+WWheX2spstABbZUmoxtqQ4PqeNb17gRZ/hlBr8Voxly+EysdMEyH7J1ErJNILqcI3A5ibTvhhWcMqFaZQHa/s5saE/t3FBqZxcwB8PkRFsQZFBc1zjic06Zz/tWOLB6eX7pJzKYgUbSGrS4D1h4mNxvXy7T61BeeiuON4YWwDtPDh4zyQiGTHN1j97LL3XnEvMGc7FYdCSqbAgufPy3yUcCWiFwR1U1jQthCk0LQFMcAK6Xfi2bF0cUIg9mD3qHbYeHCnoRVPNUb7uVN1G1eSmNn5YKqf8CHnvZEUMaC02rAJFF2HcvqtxK10nrBzHnJe6HhmlBlz3zvvsWwgf+QvhxRcufEXFsW8oCCd+OjYrgNX90tnDbkUdeDVWIjbpK503A2Tbi29ReI8/8/AKS/lwPeQszevFw4EKXh1Dedym0vMsGzTnPsxRSp/WSNkJglO5+gym5YwoPO0vhMzWbpeyl/U7S0YxOkxG/QnEzlEP+NJiFyTu3FCR3fdteRnm3rUPJN8GGaOoNEezDYkhnTR0nVbjt7Dse2aBq6qSoCcEWkVJ4VT8XIg8zxcI+L/wA1qLQr+t4ztCx/H5eJVaNIMYBbpxGmbBxHdi3HD3upq+0SVXEUCzFXToCz4u+1ohKKqL/fU+3zyMRNQioTSdRehRtkPFbHkdIhh4BcbgjqFyi5/pLBCBZXzllNXCoVA/Q+nLn/+FDR1lNYlP+o4fSDDVnGGgD/sjcyz1eVvvbGm7Dj2cq24qioJ3r6RpmuuKYrIoizRxi1/mjM0oWRTB64XITXhzfmhxHiYimczzvBGbdkStrFM/Jz7X2ruqMK3JSpAI+RxcYs33hNp8LTFlGepvGi2fMHxByim5bjOr6Jzt+23IZakJl4eMkDA6wrzhDhqhcVRih/cCx8dCoiLzFb0/Omdb09yUsp283+kNRdH73CltwAlEZqyer73e7Z8RNdeOfT4/LpjU1JLW5KLYt3vyOqvrwrBBNtQEhlCaXuPGhmmCOUg2FOah97I84BpdblEA+vIjWRqJ1/58R3cE6Tp/8XnpIz3HgLFlRbC0sayyPNcFXxObMmi1YBQEHkIoWRAIxHl701axeu9qjAnJ1WURcQDmsyfavC9hmeb5g9HGw4QLApYDhSSDJOEiFDmgVmAlungPGqR5KwyMN9lfCdc5Q4jVess8nb0cEjEs3zNCiCWozVX7uxOFuJUVe1P1uH2GJkylsjmJX82+B9yRpgbQapQ4YXw8f5UJw66aS1MKvwOnXaf7DCRNZyYLVSuZ9zVqijRVzNf0MuWHP3LBMHSiDTUfx28YWgqybLDpy17QROZlBSaI7V6rJ3s8QsEe2obZnjBgyjMSb8H86q+zu9P0ywsSyn+IRYIgCEUi9PP9viO9jGG3NsGADa3ozfMCwwKNpwzT7ikP8GHZsMn2qAmHPWnqZgRsoc/W6BlPWgNKhSU8BCQP++u7OJw1RksTP8TNOvD7aQZLRKFxFP/oCZTmOOumUWFPc+yh5WVFRiP0j1LFL/hm6wdtuLJt8Dedas+MY0+dUW/CMNDPYnpcvUS8Mzezr31d1afbcSUminfLUEJH9fDb+MqfI/tx2xDPjBOuky+KxI18TYirxGFI0fTMgw8peZhuel37vsss6suKp++6W513YjIXjb1Fy+tRWUd4Ox58tr0frDch4RrAZJWXA9sAwUTvebl6GFi8uk9JK8uAAS1suh6uyfUzW4BDJ5k/PYHsHbS53W5QAeAe1/fsMGJHej8rVpD30E8SkozIfCtgZCArlaloAVAwqiL4+cbgSsBqKG+2gJvop42XQZT/SI3GXKiwfDOPyqOALIGRe33pA8Gz+vfTeTJjgM614U1B6uqeVm4MxGGvpiuVZQs0Mp9ewLPWiLagT2AyEDqq6v1+zDVT4EIabZIQ1gMwdqcw1tCsTxeB0wOOx/hxiG6RRc91Xe+3LKWOM/VldxLOG3+Wx6G9EQheIxPoOoLrEp8U7ngLY8DOHb21043laSoSaiXU69AUlJCz7A6bzzsyv5GYTq+DCST5lgyI+DQzh9hD8VxrHOgag7M083rA4EyS5Gz2V7SRQ0NskWWbHD8b0uEYqw9cT4PrW2RwxmRHCwrgl4fBqDvy6ViMOHNakvZUyIaBfoO+B9foPrB92/wU57TUnWsNLtpNqoDxAd8WCA9gQTt2rzAIC/1+JWkNLgAVztUsD9jNS3bg85anDOPQmcIkgBXeiur2ZsJabDhWzgtzwLi5Lmo8TjgSL+ZJAA6+JvbY1POl0NocxqLoc24mGAnxlGD8JHD8ftl9U53je8VGlnwHg/daHYZ4AOjQsg4b4hSNVC0fPIfmxCksY1u6eJ5qY87ArcUiVF6mPKArCt/F1K0SVKB3ImbEoD8OTzkznLVxHeJsSdGqNtvzBI/3Yn20Tko+j5u1jLhQPlXK6E5pU9/WLFBpKPfua+peKEGg2Da0T7ssbssgRIoFrE3lvpEnSV4zQmT5nm6JTnZbWTwgaQrhJkDioOU+vNfcvPupUcb+Z7JmqZI/oacn5oezvHPMbWFdQaT2g56ye0Rn2i2m+WnL7AIgojI9LPx5X5F4ehHkmWXZ6/Wan72bZ0/cuEYOQB4YwbXruijSzBWaS6UD5+O88k4VbcPHS2HynNckRayigHcYO+hINaygoq/vz0s5jBE3te9Kp9srOHZHD7kuhAws/41gmN6wdxfzY+llHOVcRvjjy2ukprre9NI2WN2wtjXTIf6QHZiXOEMqJyz8FFIByYAjRMjq8sQg8x/jQ2uY4WaFurQFrfxqMfsGVw+nYOSk9GmugJcOtIZTob9IgL8ICMlMWbSZEb7NRA5D/TB5oYy/t5/CXNHVXRT36Mdm59gVnR3V/0g65+iGyYo3QX2ec1o8dUEsvZUe+X0ihc0O4kVgoB8LgoC+gfC5VyEaGHdekhRZC+1YP0J7XfHm7fddM9Syn9p9Hj2UN5niNkVwiICMAMJOumLXiwOBLMbFzAevOmIlrtIT8HtkuxhXs21jRpeoohk3Y8YbbgJidNcQznCN0DDxE1pbatBXILKoBGW3ZIf31u+t6HaeRTzVqrgALgs5CdEsCKW1u62whoqPuPFoU42ntgi2wXEsYa4URj2CTsqqq3kXYgV4SF7JdxFCa4txUt1rfQhZkrupKjQk1g/1ly+1rCVEVXKOavVyvpiYV3lLCCv9fiJiBdG5rHAHQN3DYLvc1H4Jh+RIEHNHTTNrX1bwADQF4HfzUfcvW/nizbTUIXq/ZN73PXjAKrUDwH7DFnWit34RfgSZQJ9Ly4L/cLqgAgaS1lrUKZXtQbbAXKxkefryjKJTYKHUd44pxzsTXLHNfZmGwPaAJI2REl9XrXyHmHsiY9fC3re9+MutdEFo5o7OuVfOXS32iRqGliJTc9NF+MTi8mUnn2pkd5jAYVhyxsvefhfKMSvQ+0WHw+uDuwfxmm+ke3+tZFc8nHVu4pmAiAGMQ+UK7vVZfzEAhdBTpO6bl178o6MiLm3I+ilxbCloaqtbbZ8EW/elQKGoc4Ro3x7XiCKQPAvJfUp8L9aZGm/MPlwF64c+OQzFZtyHyAa9A46VNSznKfAxNkV2/l1BSXEtPffP4Vzt8eRIaRztryYAIagpWMqm9fpt1KS20+WvsbOFCnFzAUhgSpzplmlSw7bu0znx1Bw+9zThuLBVOtGDXLRbfU+bxDsgSATCQdRM3b6uBNrWhq8FSRAEvaIaW3m5cRgbacO0FPuUloT95tBo5PGTBGL7GcvoruuQHkQRZDnHI9JzAMkY99OFnG7NBDroY6bu/P1+/yat0j804qRm3sIgVgXWc4tVJsu3uspWGrxa4H/Oyo3G2xIATJSlvtadYT9lTgB0NbOUKMdKYLOvF5OOLZNFI6Rf5vOJJXEcF1FKUfQdOcjifUNjsoB0A1C0GxE9FxJgAGRuzhHjebW2pKs6RmFNRk8RUTQvilStWnvd98L3s5xMUesns8NKTPDSxNp2d5LUzPWs1v3JnA5C3DNASved9Exzb5g50Xg+86k80GE2MXfc2Qd8psaf490NgmF/pjDwIUm2+4A1LXEeaBCBxVr0wGiSNS2Hk8kkZpyEiVkqN+UNKOofdLUi/h6sN8BLICuAETZaIO965nswR6D1NXKqXkN/LocoNoJTz/3AnQeMeMo8Nuy7CbfObt805OInRqqUlLQ8Hw1AXn5opw51HOGAADY8D9f8qs+jjQ3Z6GHKEwQdWG2QtO3tmyC30jRks6LQRE3HmgOXWBStYnbgBGjhlxtLXKdYjDYZhuwuMeQBs9t/w9bbDGbkXC8eYDCftEc8mO8PWdghEB9sQApE2d8HhEQx4O9fyI7hqVjQ+Rb4+wwGd32aOz5IN916svTJNF3FPo3USKEimzyfZB88dXB1xCuAxeYIaNEW39DkOQdN0+yGodECKz/sUo/3LSunR/d0WJEPx1tuZnCvEgHBoFqqsgOTmOuIkXh7Eijq8bIIqvATtrSyGmjYHaNIJhPFU3vIez3SHH/0Uw+WY6osSpRpbvFiQ9I/IfDJlg9IymJXfPV5CE9ezVJHEGp0wUXoRbeMeTxaQ+V4hm3YE8cVcXvrXMo4RDKTJPFoTnmRXLWd+TpaF20+YKrauIW3vrFHZEEelb0ugFviTqPL71LVeMiBxYYK4viZb0Ow04c5DAMwvHokNEvHrdZcxuj+5livfzEPvL6XU0jwWN+D4C1/NsEQyF3eC8OBXoKOpN2fNeHtEaGX0kPpVDDxU2jNfBQQE/ZPoAkX1hPhr433Dc/wgrn48+vRbcDGhafE84z0aI/uLir+nG1kRLA9f96XRvhmSsIkoUvgAT7uQx044CxnlredD/NGh8C+uJgf1RuRxvxxCaq1Bckcougv749c2razLDIBjSZpwIKH7mqrkjPPmTF5Gr9N0ND2eQ4CVQIg5T62g9bVKLQusdiegzt6Ws7VfE0VyklNiD+LNaIuvgEs/qHJQRKGMb5IzCtPsYtl7dSrCT0DkfLFVN09cyOzdbVIPSU2Ybkyw6k6ahTC60qkEn/GGGjdp/VEm8aEu25Nb/gZMVmcvUSlewAhOfM51p3OCm89fAKc699tC7WkKTkI/2OIjjzHGqYLeN0donxMx6wFbfmWHCuu3qbjaZ0uPB+59cgUojl+EAIDEYILEAL7sn8RgqhwT6n0hUtAD+cb7UFUplmWOW9x8rztSMK2Jzcfc7UX1u8FDPtUgWai2rwi6Ses7EmckGnfxqexs09fCzEucvMfxIHXB0OIGQbiyM+8pfmNph+vcRAFS55NKQMh05axQF+SzaebY5Qw7l9IM74MRk1t5bB0Bm5Q2F2XD0ttIcotZBWKJYPa+gZrgKneCsVcT1ruxReAM1V3ygiY2rLPVe39XgNEevAZwCflW56APqJU1Yulzz7XWH1Oo/0qvnMD6Ma0XC/zKBMm9r3pO5ahUyGW0u7rs7LDXykBfOOV/wWvYpVP3YyDF7xLQFV/sBefsaL2wi7lQfi4fXeoL7byHceaOlEh1iBClCIIgJj1k8VurYpv64QY2jfnrCqCziSUHWyvbD4AYwgflKvaPfObk362Zau65CSd3ivxfsCqLSq2ABCMLVRV+JNXZmwA/5y6PLvo9YsIbYgsbcUC/P+bZhbhPWPIxD7ivWA7pQeUlgWq93rg3yGzlgXvYfr38np5XyAlVKBdrxIcQ/iutQeOgXsekya+Xv+bjgXwUFVpLU37jNPS/GA78KOb+N2j6h/BP6zAUpNEk0QqKQD02gjs/vCjcbO8llYnuEEQLjqwefWRe8YPOv3mrA+e9x73dLMfmcr5zSKTdD7edGe0o/oX4S+HFT4jax/I8XoY7Z31QuVh4ZPerkx06n9MxbXgwJoVMV5v5eB615AVVmB/eUPBDOT2vHuOinpexLLAOU34L18gv/p4ayM26Z/plAOfpPygYuXFTPs7AZj/tY3j/KvnksIhkpJS4aSVvxatdKrKe85DEnJni/7sJQ2VrPixcWRG5/jka3qF3Xm92+tEE3PosJpWt4QyAL4j40hHC+kKQ3V8So197mZxc9zsaVV9oIqPY12p3DDNW6jdGsg7hA+wo6HRH6Hum8un3vaVv0NUtdYApNznlUbQ/oPkO1/rhe4oQpiwDmcVEJF+zr0oQETua9HafnP2SZJk9RGmvoHDfMOMJRtWFEPXUb6h0lM1SH+hNz9JY+1fcr8LTaWWq4dI4eQAaKejB9sEK0vB6sVFvhYQ/vX0I6sdt8ZjXjx7NOXZ8qOIk91AQUVhxRAwaJPvRP7QamW3bQognJWlS1mtffKUhr1tvWLZ4PYHvuz7WsNUb4xi2KyCOD5H2bqu0BeNHZF97guG4XhY+CsApukTH33bnWDWWGwI/9JjUUSI0NVsp4FqPHQ5QVE3Els4oYEZkC8wvxY+zx3bRib00wWT+PK/+0exI1pEm1SNW+Qp8xIXL3WPTrp1yPn5OAnbMOwxxRyTB5l8T6cXmZ3XRb6+csdeZ4Jt27anCfnD5UT/Ms0iv3GVN6EPVay/HS8UjZ5nuvgSj2GyYUroS4h2cvoihVhEaAovtCyBmJImAarkIRnQw2FfljBNHuknM+hiQr644oDR6n6v1hlNHt0rPsNbHJkDCjdcDN34ZBKRHmCELsCjAPj3gYYpVT+TGLqMv3hpaQzD0HURy+c5iT7sFgMsTFzqnaZuFZbk27ZiSWsaBsdQZhtVPr9M/LMzvqnskXpWR4i0RMtS1NQ57/CtoVP0lNDdm0QRz8zg8aVtoUbnluIZKXHMvB78Mkhjt8bQpP9kkNbb9Ho0PSDMzRHCvB+kid/K2rGHTPZhLoKzIvQKRmyc0wNKXWs/0W7hZ1XdbOvT+vtr6oMkmcGog7+0uEj+Um8qcd53WoSbMDVv9Y/6LggKnuz7z5U5QPnJndKiuSPovCurbTSq4tNM7kXeT+2NQYJFeHJPZF78hFaT8wcsRsomTKvP0CqtfL2NtnVYZf8YNDEDmu4B5jV7jT/i12S2DRf37huZROQ8yhylKIpA0y14LOKidLQLKNC2vkDn83ry1XFHqtG5cYZHkiRYSBMQCTIHK4ST8do7s2Xew1c5uGpVhsCFPFMcielw+bJKq5xeE/56ICG+0hXLnljLG8vP7lB5b2rFvvofPd3f36Iz1oCI+nZ74lq9fQXOLsKnQdpU/Z7BWoynAOOsS7PgGYYKxjQ8bFYW68/rFQT5iyA+lZMvEUKszjk0COmPusRnR0G4FbDjEYkuHrgImE2Gj/qI/tzdyPftQzPQMPvrKNkzzD92A/jy5tY3oJqiPAsSd08d5Rw9s/6i4hpLNC3J8QAnV+jr3FGQsA/yQbpuMFLd751C4gQx8rBkOmvrU5Xe/Ztlk2ohSQn1CMfF/7wRhaV0SVHOqUPJbDkDIolWlI6frVLDdAS5LV0rSWqpZ3hqnTW9m4Dpo3e4p3cDYUcft/KlYNzEf+ba0EtoEtmIlndyedZK6q1wUotoe++dxr0gmDlZHmZl89MaTp7xjyeCYfTtffOTZ19124DjmXGLpPQYQ9X+wD1eM9i6FBA1yTScF6FLIgfz/S3KkzDlP5gwnycMyUdz9OdDU0+ldZOMKy1fCPhBosg7vfIqEVTlLTGaPIAbpZB5dHCXJDK+GyEbYKkbgjj8U76ui95/94l5yZbrFolXS5GJd7QQ0ZUGAtMh6BE8iUwciYWHSMVVVRgvmZcyFvjBnSWnPmGMKRloUlT10aQzcQEVXogdawCLRjO4wxKc50lju//xgtg6jPSjsFpqmDA/8N2NsQeBvlu6aT5h6oKev7UhtuLnFlTYHCygFl0z+0ztpEKAD1C6eczNxxHwZabd6ib9NFRDv+934jPeUElGyob73XUy1dpLy6QKP8etnsZJRoR831VkkQbcznqJT3otBH6o59L4uYQwhcRMGjWOf3zQcwXa86oGjKFCtiCpmgP+q/0w+w7iIkEMLhRrIDfdJpxO/TxqvL6YY5+K7C207vOUCbg377+B1cdqf9EHL5Dt+NzVtnqZ7W19llZ3KjIG7MHd28CDI4D57KTDhnqXJe1dbsiexKu/ZWNf1IPWPonqm4k3Sd1yGShg03VdytjeEpe+jDfMYW+uLN8iyFxNvkomvOtCevNgodHE8Xp/64bHowKIymlI9qmgXslk6VEA2mqYfdj16ls8MZL/sKnQUdRvxn99ymmg88otNzJm9OSL5KZa8vKHVWClD9uugI7L0vbNCWEJQmOcWCick6e4omMwCLwuTqv7VBQq4PNeKbHV5Lzv5pwXn0p7/e7okJ8Z4/lmwxGX2trfzUXc1T4PYgsluvvL1aP84eMk4s6wSJfn+jtLt5sw9MVJHpnvYz4J+P/2rQinLvOg7AcsvdIBxlO78sY6AUJeE7Z8Xv5LYqwl0AZXsx7GvTya4cKpvBedToX7YY8Ba7j05HCPrdZOdEfa0ZfqhoiEhl0Y4zeVX3F7XXV2u59v3p0E7meEH57D9Sl3PnXZ6NBFfHfzIjVjlPnmaCJiSOOrZPLnCkET//4kGldNuUGkiPaFreC3rvRRephMrAiwZpG7Lky98gITKbTSv9xsaf0yBAQV9UcU8cLvXbdWL8XYsJD1JRNxn6eV94OfvTKA7CJTxdf+i/ern9z8cXvZ7f8+17hQ899SBrylTTnatVvfIkxqmHdsmKebBUVzsWYtcRH0UffCuPLV/NgbzBSLLtAK8UYQbuuyo7x7MDxel5tsR3n+wAfnoCPE2SBWtCz1dhq19c1W7mUNYBDCy28tnvlXitWJYClSCff9z4/Dt6lgvGPJ9xkzzWiYdcKUIm9O4by4rqe4Dayo7Lwg1YjUlKE/MjbebEmJ94ARnkFjcx+0tIjNKnFm/cj9aiY5L47vB5dY5W+eXjgZu5Z0q1r+sJYPN4/SH505SlgtpYwrJ/wQmJprRmll/iJYkCuGJyd9fmu2qvbi3lmeT+1TuuJFMsUTp6KIpAm6PjHX7Q8OIJ51adM8Ty26sFsS0KgK4pRmwI7Dyt8qddb19Fftw/rOoYNHkYkLjkEN9+DEkYDJ4K5TLVDNuEGrBdAlywiaq8rrNSFchda25bQUB+PCN9u/9sCI39XpVMpRFPNtlzelWXPeVXHK8N/IT2cNfK5GzG6PmXIAQ/TLAgQhGC/fHkKjqP9Sz3T1AJeMqzB8Ssj8uxuBrsnvVsy6+X5/3B0TBC/9/O52aPUKXIIjNdmWfJndfYUoFUWfvasIIuRGxYw8B3d+EUyw/dQXkc2t+9khXzbTW/01+JM/hAO8UPLZX2QyDEMDa8ZX+ps+B7+7PeLFC2Ik7zqu7E2I3JnFW6B1szwjxHtLU7r/RGlUwGQsZpgkQJ2hyDYAay3YfXxeu6qrFGyKxWMpZ63F37R49B4SFx87h0x3IOfJpqnkSN4ekO8kuLcXNVSPFTQB102tzYAkqXNZtwMPn+fc4ZfFr/WpfGuF39Kg14QXkvefop989K4mUe0FjZ4haj/2zVUNp9TcZwN9vftwABEAcWsW000Im2e8+LmOe3leamL/OZ9+EtPw+cwuxwtrsnJERYgvScndB70KadUBrUS4GhDFh/LN68KKnxDQyfR2nNr6OTQSRM1KkHFcrCHQDF6QN6K6Tt0P8q2cIZWEqJ8XpE8epaXLUgNLwr45XPaSDYkZN04ocoNhY9romwg5jnJA6XXdURygkb4G0UzVlgA+28hSRNNceFy0lDbrOE0jn4GWAJbrX4B/SYIea0c5CxQ+lg3G4RQERNfHIfPnbcP8OVoBTnZVG4O1iV9UfpTHLTefJaA3DAMR9+c40AtW/W0uXdapUKjYABhhjK96DsKt4f5kO7FvBRx1y37LBjHsmt9Llq0NezXAgUlZKQP+qMfra7rHgHePb20nhdo2194HnDtbtw0+Z/+twbNcGs9sXHwzjrwDQad3nHqN912WZSxbXbZ8IEAd6FH/wPpICM7+Cr5blraf75PoGGRcOCGm9nyLH6J+j6GKfj5BkqZeIbcq8mVcmP+t7PLTBX0Ul7XkNdwPIpux/UlBsIbMjwm8kiS+FfIzJHX0h92NJCEJQAkMuVoNR6Oz/Q0QRHAbIvXOzgrOtjUUaxx4FBpZQ50G5g3WDVKS7LYH9dhdD+AldgYwelslzseStfG8MsoAFZijed63+Mu4i7m17xSli0Zsn58PJ2zcOLd3mIwRswbFWljRxKJJP7owd0ZncsprCEGhUuaqA/mmX3ewAC8UuMRh0/LlqEz2PMhGQr97oP7vnqYf87iXAi5j5dgtq0KbM6BG/DwOG/g+gVx36g5VwvKenyfwu3cE0LW6LO8nO0q52fpDvD46t+BKPF869nC4NN+md7ZGUXT26cPSHyuseC/MEoY+TEShoc2XkN89JoqlpxOA+aYpUd6cJHwzB6dclM5fLYGjxnUVNmS5aYfdgBVkVu9G7/ZHn2sEoensZcAHOdhIX7zo2Ua2xLiP8iER5DwDmiKzZDhYUiwsjaBCbgtQEaxz3ZXDINSzjNhWPY6JJ1aTyE0Af17HAKKOzTTAtfbtQNCPDmZP1u7WmcixIBKPBTfiKdpFHupIgpXPVjiVnXgtzwsqstrbH30AGNLs1cC6bwpYRChKhjnom1bxqaKj8dPVe1KWqgySixwr/MnLw/D1PEpOf2/HaKnuIlYvVpR4/DOaPA1LmViXHxQKW9lqQ0uvYYovj30tpHWcpnbc4qfMPjoZhcJRKIKom6Cm6Q7wJp3zJZ13DCxS7A8ezm9Ukxr9YsYCg7LoFBNEeV2gNg85sfvGRrkDzTpBavcIspTLEO1O51fOYTLcn74h+AuREfaFQVXyYUYdWJxsl6Tdke2JlnxXlTgqPocIVuHQ9Kxg2NDwifvkrKq2L/W438gBTZ/C2TYY0O1n1puEavG49dEqm2HGR9nhye4qO//c/koMA3R9POfb5KjpTaTAygdmU78D9D41epBlMHhlL/OkGssqNLI2CeHWPB56EGCXTfsMiCvXKHKNvDvQcgxdgs90HA3WsAV1J2HNiWT4UgzfGEVnWzOC0zKF3ulRlY5iH+qCT2B2jL2nR5IZ4/HB25tSXbqRvNievO+YVhUPckXcoOpdtn8qDcRbrB02tvUo+fEeKbijT7UK3S5lPvWM56hUukuvP65ME8hKF0tsND44F/Fl/eFMHp0RWKcpYmkA+QdRuzbjfmqP9RtGfXwTGCysnnwYR3C29HMmTgPgACOjtqf0sraPx4p+pJifz3pahKT36lreRc5XdwwWpImbLXzwR8emtO9F33px6CGW+xaEmYb9VFKDthjO0994o53ltPfvKMvTDfeaRuK+u6EtLE++vtka+nZWY8CnqXUt0sgs8kt4O6W3vJgbb6ItJMNCeLicyThccb/bQP473CUvyCUgaGWKB3LZWKawP/foFVRtJj5MZJzr157In/ibI24/QSDHUmCaRXY7N/79vtOognAbLBG5l6WzuFyiHWclYdDcRV9/fvNMrLkW/Ngzc6coj+fFtQKjvTZU4xO1ThbmHoZFhwgCfA6O9qpos51X2veywC+PpNjO07mcLcAvQ7Zgpd7rZnsbv1b31ynQHn6858bdLyIMdhjXrq753RgehmvqPj78HgVnRNUAd3PdvI57bm6SC9V2f26DnDd0un3WlSbjiMPHD9MVpq62MO2ia7T2tsflOBXlavWng8NVrxqrGaPR+uDFVGLzpP1LLjHbBtsRm9xsLrJmim6Z9uf5STn0KQ8wkBFEXBwPxSUO+2w+n9pHAjVPpbZqZDxCfZ7xWZbJx1a5swbbKDevFxxD5JJm5/klunma+So9H+PVx9t0LV9wbsz+0NQSa4n9li2tLOLwmyJuAUYtO6cxPveRHnLwKcemeza2fdLrl7kLo1sMMMiiUMHTurrDeiND5L9ZWriW1u2RZ1k3h0P+uOuM/DRBaICPbwG8POqoBKJ7a648nqlz9LEsN+B3nJ2FB8/WIwN4s9o1lsl/Sbl2kqHvRMnLVWqp4D4Efgg3FMOKonBw4p5yCPjIIThu0nT+C5IDuSSppSQi3WWbq8LEshlfL2d1IsefU9tkG1tQKbtVFx1Tm5ftN79onrp/HzEjxw9MuenTjsv9DRD6uyvwRazfa+JiokBVbMEL0OuIhG1tjiPRHe7wcteSaoftutzM3N2YG6KXbkfsLA10XE5Yua0aZanQGG/teL7Huhs/bTooRkXU7y6yGW5T4D6G2xzltL6Q1Fw+Z87nFhRbw/MXfaMubilB6Hv6kkCSTdP0GgKYPAQFgfmGOyy6ICne54V4vpqFXCNVyJVVipLNahZfLStygNTaPM5lrxH3m1pDueYMc1MPx0bF6es0F1iFkkwOC9xkBzVTaD27RU/Kc6e4abrYaMPsZG4+Xttvarn5ADax9dzOw4SULBvBx2v4eChqb13oh2pJrX0T6moVal+BHvQpOb2Hy1bnLI/2OQC1YIIy7dXt2HfTuduVXGd1pdhzE/vkpnxCRM8Ym93seyAgHJmMUt0tiTxGlXPlzN6VovZQbCL1y/BwWlEOa36dJ5z6oOtI9NbvPrCoCBHrOMdkK18u2zEhrPjjJiN7ecvv7t70fZ4KBAC3trpH19MPeGJpv2lPhNHYJkZLwHjFiRblidBohw3Fces8fGUOWy7Nzf7GZTsHYEJBHZOhDouq9ITI8Fu4rKlhDGYFECGMitB8GnO8sEKgKiF7OAj3wZ4lzVK+guf1+OHFbuNVGKtxpin1/iGUYG0LfmX8ULFJX9Z903e9sBLt5cuIhVE5LEf3xo+MeHb1zpDfqolbzZQjwsSi6stfKq9aNqzmiWKrelm3n2fLmBt8tqyzXuL9p86QiRSHazwhjL31Z+e8zJRyNKh3xyTilda8GndUJwTmoz8km/Nepa04odBOMorV0ll1uddNj6G+qbz3AlgpFXGTWe82oKLkag6HC6aWSL7XENkCs1BhWR6PbIX+Y18hszVIpQhmBNb1XFUtvdS4jR+enarqkEahw1gZ53ZqD1TPUA+REgiluf8R7eIRmTFQorjV/r1yPqjJ4Y2q0U8p9Guic+sbF6kUS3/M23hLNnMZxicljtoWfv4uc5IvmH6OK6sASCX+eTqtF2CNYiU+x9+KAqBKGYP8ZlFqgQvL8WSl8Ve23432141/e2oJWbGajoPnwu/WEpFK02AONS31MjpQrq55iAjhBjkQ0/XkFeiHjHTbQISZ4FMG4aM783xru/5iK2uWYve+oFcYGrIN838sfEzZ04+R8rtlAVw8pdeqaTCe+jCYU5gY8v1yBFFEKI+7TV6zlNCR2gBuIUgaOF3qZT501byBRnrvNKJvZjt0jXV4Xls5Ibkp1hAA8yxLo/J7VRF+8xNplHRJAagSIyFd2LG9QGnnJ9fj5JBZuCFJMtcFE2OU5bpoQy9M8f1CDLBKyFlC/oLdWhIjiGmxFUCnsAG8imT9lrpKAqFRXqpQH8XUB+gSi2w9T5soPOgJ+WavKtbk0qzD4TM/pHpe1yyK9RlFQJI0qa77Xrc+4CeAumELqZnmug3uGy94YXrxfiDSgwyYU5hU2yZJEn25MAUh6Lf5OnEcoil0ZqvjNq/aFy7RgNRBNKsXT94okveNKs6m6t8i88Trd4bSMGI/+yvD5BCBmy+yWN/T3nQiMKgw1CnQwmcNtPZDuC5byy5hcDnVZlovN59gfetNy0rNTeuu6L4g8T0ih+cRwAQhfH4m+kkImTVuekfxgtkikSsYj1afpln1CILgVLS9v7uATaPj7/v5qdt9wQ4Ah+EzTeFKY+YH7n0fE0EQn3VCk3yvqcYdsT8EnYUPde0/e2+8Cnh9njzIm5frQD7DEMSxcb/r2woI2rlDzMzUgry7LHvSywOQ+I8OGCp6hNdTcQyIqva9iOLYfwXxPlDEb94tnUwOZlbQXpJlPywKk2NlKDuKcqPmBDHyasTr3h4ND/VAdbi3rTwfoBdytN8smCGKGcgcY4wx3PrYYXm1FtkRiTcnx9gbEncO7puWkrO0/LmH65DI+C/iIk0ScNhlX/p+21I76w1JBQyZ9K7fHQOZJz8Ho5s9xI2ul/oNQGYTtXC0PiQLKnPUMtfssjsPaMxvmqYR7Jvg+/kh37tZ7AReap/UiZ795nLVcUThCOQyNPgGv3RjLs7hJQdFmqY4k+pHwvqUlct92J1ZUTyFqExBSKbPCZnO9w+j2PN9dd/t5/SyUzkqtG+iVWoI/8FixUQkSyP+QtgUXm3UL1hYUI/sPX9wO/LNU7k6ftiCqeC/a23Ah27+L+reY+lVZYkafJp/LpyAofAIbwXM8B7h3dM3hb59742O6IgedUSf0Y7zyZSKqsy10qwcB+w6p22nQE8iIEZZ9OPY/yoCg/gVm4iXnbDNqhQKXlR/py8bsNpolO73IVQaXAD5dzegi3HEKXaW+TgWsYJ0nuel6ZnAz/W1oeh24cVfmJey7RPb9c9oVeZ0MbFtheqwJHpNYwPrDvlcNiBN+PgEZt1sIufGwdc6JVCt+2TmLasTqTSNeUTwJ/kN7DeKuZ7XcbN9C0jQMHgLsILCJ0ZjF6sxpTTvjk18ns2eiReilDhrjY8XvnT6StMH8TBr2aQ09SSmutPIsI9LsEMZBJ2rRyUPLHywWfe06/KO5/R+9BIDxRjitV48eXKWT3cqHvMirDjfumCSyU1BhNcjHtOMqt9ptBvzfj3RAEWiXrMv1JValnW7uur8201lkx9hvFo4uX3V+NO9bQ64TFiV2NAmgD2b696FdqP25fEpXZA4H8GTeHWCpFLwyjrWnjwXL5o3pcVeHHMsokpourfCiTh+AFaRiDoQv4JJWhEZTq2rKx6diCBijOtRFCGZVCIP59Ff+9nW1007Tg3FTWO9tTCui0MxC1d95I7X/8i+xgoYIR/j+c4pUbmWbvU1pOYLCK0v14KXZo+4/VnWoAE1WTRVxccni9I0Fo0D6PHdDLLKLb6O6BzsxWuTOCqst43Ju4UPEf5iZZ8D2JEDOJK6fpuiYo/nAYJT77JRw2fyuYzqPC79W9WUZeDO94Scz0/3eJgBiMvqcDZJvEIMLl3kjbYgE1Vawvp4akIoFPEWxQn2TOyLCi6L2U1VA2HPwZ6A/UeYWhhCm0Lm2FSxnKVQJyORUnNK9xYnQQFJHv5z/bGT+g7H48uiz6GsGIAiuf5lsEaR2EZeQx9VAGZjuhztM/4mtOIWpok4Yd6hkX+5pt2XAaQ3KFd04kJDJ5VSPcoEOauFfXRgT53w2EC0JpGZ0BG9lLxsKIL5JjMeTA37y/UgdNKWE56PAE7HHv0A4te95RTNAzLrI1A9HSK7+q/R1jNNS+JxxOaqfay2WpWRLX0iBhpr5r+uiOcZFVWCFZQZTXa5J/WbX5YqjgkSCp3cc3KK2+zRlAfB9KJc0XuSffq598zZz/X77mADZ9ggy7AIASmEqmkkusDdyZTk29OHvA1Yxx0RcDtPB2jsgLNKf1Mh/eLLRykRHEXNbFZVFfWYYiMyfJIwvecNo8Q+eM+YscYWHPRC7bhysS86e9HoCKkW3nUu/vj7HX/BG25ioeBtM7Gj8T1d1IUi47UJjHjGndkRWGDDvgkfatxkTMMe3m2P5EFIvIUhMmPrzm4cvIamRM9+hcdmIqRfJJgQgQeEAc+ceR5oQvPKLN+CZnhfeCTEJHXTMWoJOSHDv3Zdc0gG42pXyOx7QUPJiA18BXKBPucF45bHL578PKwwX4fqaUuICz5w80aDKpIk4YRcVprZXIYqUXG8jVFcM8TIh71JDlsonN0jOuj6upsyIGgl9zBn1vhCRb75qjT6tOha1/Iv+4RDHD7+J0LfvvsnDb3LQsv7PZFvTuwF4C+WCyoY0zVbcfIsyNdJk4ifLGVR7GB/w2QzAwzD+k6vlGDTreH92CW3O7OND9MOQ5Mwz9IGikZdVdeIvJNYuDCcekPynEfsL16U8a2eRSKp4FuVdcVGYfOPX6X75ZNIXwjRoKM50+9BToRin4Sp87YiVkB5kroxGz3XZzped4OHA+dUerImVxjz+092XUNIB6VshBw8YrV7K5J4vOaZ5Xf5UXE6iCYRRFe9ShMvLuQQX6v8jC6SnB9a4HDoICvp68NxbCF39qKr0Yf2bV2GiMkSquLQi9nLSHjHtLJLaNYXLmk5Ok+5A1leNOboqCDG1oU2WR5PFmSQa5OAVJ71o7tRqNAXzwShTijKFO1DEXTTAjFIFA86Nt3YrMzdd9DVZVRtH4a0Rpcg+s9u9Cr8/BZwWNR+fNJrOfxlkZ6Z2MYmW9et8JhYt6REnYyqpHvJC8dtT1uNnmnw9nG0QiD0s1bnvN+ptEW5i+OnD/VsLkhGOMMGFA/QzzKZ30dYfIWKu9sLiw/08Vsete4EbiULEg7B3zrl0YewYZqX1MubyOjVe6J7pC5okG/N5hKbA2hL89IRIfJ9P0VDo2PnLPpV8z25Dd+tdE0+eW2bD3sj/Y5D/OyczP39NBkRfm/0HoPLUB7vAjKBHwpB6L/4BhbeRmVPKDWoK0lBZp8BJ/vx0somTpTtkya4yp3TKOxFaQWFzY/Fz/d6pVyYlGgxD2h8bAVGS6mmSx77OPJRfu076mDFk3hMvJ4p08VPQHSGiCtNcmx5f5BsteceAB/qt+vZUGYXN/hGuw9+KevwBa+8WgCt82Wlyb1lcOeIHec5f0a/TBvhtcjEnogsqHq1zTBl9e7sTnY6IJE+CiwZGRXFMz16C8J06m+/2mVIwMLP5ERxAbHTLwWqVJqWMYPCRiwtHSea1Apv7ZTmT0roMMVJIcY+XsC3AGY2VcCZQ8sQUlMA7lrFIHT2fBNSbCzGgQ15QL3w58qUDqMnL6eFP6+vgCKhamksu9EPXyT1p//SXJogZInqhsATmYcsiqQGViN4UJgxVL5tXLlYAE07JbkiLFc2W+2KPHuRut3hUEpuCJto1WubHKLy8O+2llUmaq4AOfKBMhydihn5QLTqIXYdnFPOwuhCL9h6X9Yy9l/mwj0uupj3krI0RlmWzfSQdYm5TqxCuxT8YeqPUrwmqZZyX0AfVPQ4eBCcDdpXHyUAw7rttVq5WVoV4slEmfrrAAjjQGphFOFfjRG6Mps4bmfN4YJwEBK5YuYKvPx2m91Ax0yIsszLvIEIVUwihXFZEATBIzK6Tk/MUiRwCEs/08TFl0xncbtXj7jaeGER+1VJ3C5BEIZHdr6mujfDZIrBn8I+JpVkCytNnuzJ9V8vNh8LaKvk+kkWvi9YwBRMV//h8eWzyHeG4V3FT4WteN5oURkZqxhNROLTO88UKGFxPctLjU9agTbMO7mu39k0pEKoA5p2u4XQzGUZw6DhbVpuQBb/G/CvFj00DqQh3rrlJz7qdY9VyHv+pM06L426oBajJt/L80LGU1/qVB1+yndUCEKbvXS60Mqv3b4Imm1LrDNsKPS/vGM10c5IEC0Zy6qUh6PqsTdttbgMH1lgn5+qc0QJGpwGx16HnH+BB+f8qcJeVlJoz4LRMd59K6j3aieoIS7XeL3AVg7ezPEiFp+cO16u+6FUHyrVLVdijwQzGDJOaBR6IAzbBg+FDLxUIRlQRQhiBGUtVvBTvmxSbx5s+Uz1o5L9ijHpLw2AwfuUeZNFGAegMOxVJClfsAEq+zjL1kNFdIxad4oMMXtHfa+nXT5WWT/CMDSAw9Os5F6hjdMfgGAf6Qm+WHISkHVQ4N0Ku/ZTwbizxO1DsgnsHKc4TmkQNpF3w2ILF/iehdeKxsmKJ0ZzzHBhwOaB5skRbV5ve+Tbyg7DxSdLTxpYQFSKk9lddG3bXN+8v85cXOY+DIU7bZX49KxzKApP9HxluTQJF19KzCl98xrxtOeD49B52ypqsOqEVnF1uzk5URUJOuifM2ZI1EU1nQUgNasKLvQ+cfn68AVXnaSDM8mDj9ZnOifeFDMGUyF2LXL7mgrixNtw7MRHnCPanr5SH0ZlF6oHo6l7kJXI5gcUKTXxsU3N6Wzy+NYftyAV7lu0H6JSNgzERABsOk/z6+/npjA6zkZ8iifu5dlZ7nxVzLP0/OwNydNjcdKPmgBpBxH6VXpStPvphI0UphVnB5AW5gPILjn/JVhz8irzwKMejWKUDiXEFyO9MQYmuu/mU/GGbaEHVuv2rB/pe6CdgUgYNI0GyqcuBgAx2sewXvHbdAlUxKrBqCSVzw1LmVRuaNmaaO2pGxhD31nw+jLu9e7bSXk/Q/G20VhIM6wyAVIkvJ85AcjEyb+Tguic5bQwZ8feRG4c2pAamcw6CjSxDiN+fXA8pum0pS3QBdHO5KBc2kdZyN/U3J9D2+2NVxbxmyKdI8nVXRhsZZxNu4+B3XY/C+/URu7wLx6rTh9erldQXlQM+PcX6wRu9VWxIqHlX/uOEhtiJUqq4b/g5fkd7s4XHdSWrcK3Z1rFSiDfBmFtQCITW/JyUItGgx+EXZ7QfIea5d/Rc6cGSabhi0+N8+64nDVRBo87yZMDv82v7/x8MB3CorNo/IRQeVn0a/0LvVx5Y8h6R2D/KbS68eXrd9gEUdumiGa8g/fTwNLGAS16huaLKHPBW5GiaAbzW7m+rGCgDPZpypJmtHZk/lMWCDfTUoz+EODP3Hpm+ddJRP76YuNW6tk7/BuI8LUf8uehreyvo4jCQa4hHkjVpX+dRAf1pXj/M32T9P3XBYfQWuXCbrOwB/Q+L+f51I2SpSlDYmfnBXlpwKmyUyVUPdYMf5fsZsSMxHqrxZoteMzbY3voDcTOXtWp7LOu6srJkwRy0XFFHqz9DTl5AW5STDNPqGC1N6lZh7wnWm+GTE6rr1tuKa5IOE20zpGqh0Bd51+Qpt0+xT91trHhcn0BX48rhmnq6uezQ147FQ3EfvgH3XFf1qfkfAXlqx9IOUVJsVBJ+OsBNEoj/0K0OIboE9SxluxLefX9t8o5vnj8NOzmVz19Rvty8Aj8yRKq21EybUqVr4B6DbVbUQeuurK1fuW+/Psc1tRBtZU73rCxI5idlYsvG5YC4njveXB/nVkO9et5+/aNa/31vFXXdlaK1Fw8fwDReQkKB4BwP3eVx+pPyeOBB2JEAfrrvaOJpyg4bI2wFGnKeeVfRJF+1fXehS7YUL0c64sv3N1y5WauJjhSZGJojDlCRYNn2JISYKDjxYn6TsRxBnAiHEXj0MO6cNdDcElVjcPAy8I08rYT6sbEc8zzr0kI4PkLEZzZYEBLlmVhwJhgaQgXLh7fqvx5N4mA0NmJ7Br8f68zbGUkCzCSRIT57XU4FsTpFJLpMv9TlMCbuVGp8zyRrlEFoLS1njpzSt/2EJs95zkH/QomRvRv8j3ShhUo7SfrDsxHEKmE4kVlwrmFxot0ejPSndgIxLlTsISpJgBkOMH/AP/QQe/+k0yr4ALM57WzPOPjEcNUsw9iUB4g4nvVYd5j2yWHZJNx3HewQapQMcA2qN7oji42HcGdKX4+h2X1qFwH5ev88j6/vC1fNI+/6JvS3Z36dy8BKdNPEK/+Ni+hYDYIGbaBOuSk2vw3tEbXnktvDEO/+cNXFCo4YU3TwsDvvE4/XtO6Xk/RSNochAFI18FRdDsj5SNxdtCeVI7/nz/Fty4SI02gcwcVPc+zTA3F9o2jco3CUPTFjY9nqHYeQ9NZ9GO4Ub6BJxnKWRzXyw6qh7yTyXsUnaz3WVbLquvaWT3OZNn9J4itwfB52qHGEAPqDteOz6qm4a1er+oxbrX9rgrmIVHUC45f5pdIo8xUQNsVOpZFJ7TT+mjtC6M1vqp1NpIJoCsRy4Kd/LCTQpkjNOx7WRxM7gn5+yNdDwsEICVXaxfdcl4bKJkDaoUBVoYq+AXLWStERfmroKnM0yga8YkqRoVl15fi8ksGXOOCDYBrgObp4lF1qqlUYWdozuhKRQ6ik7OQpWmcQZKovhxK/JIC0D1LVXapim9fEjO/etfmJombRaDevxNFgIc0mul88Ym9KcajLOWYiP+wgcN5EHUHHM7moFp7/+v5yUBHpFp0zgHMySceeCOlGi+EohkJpY4EP5bDjKMoeM4Of5/U3Npr04X4n+Rw183g30dBfRbv3QT7TvAdilUzl3MqX5rG5FWtJMh+TzQQBMdeDYPOrjnhgaYFfRgPZmtLiqg4a9NeWzhRLzfsnSOFbLX2I5pWBLMsHdsSQV9qRDHi0xkYieSe+GWgSzMHqm+O7DxpT0euwyspwgLjybJS+7pzRGxupPs2FLRbfKTyqCmu3uFutuOAvvvx6MsudmsfQ2btpW0lUlO+NxXPYDNCDndRUgdgY/q2YlQZcZTOsf3NAzZUOr219UzWH35FyyCJIx8VYYoitYH05kDFFYNufCECPZxYfFP790FDcYo8nokugPrNxqa5X78iBOopq/VW3Ysf/N8Dw/GivJi/I+z+cS6gHsF9X9bAPVZCQCjF2CnM3JT9yPP6ljj2QSiYwxVPF7KCFDCSyOydTxvULyJmBTcqQQkZFIh5i7ES56owH+7b6vpre3ln5R9rZt6KWNYhVfjBxWqzRSewIeW+2rRphUTWbLkELidn/KwwVWcrL9QEIQjImXMUKO9NxF0PRAJto/3QDJTEJSZHvp1rPVIMJcK3rQDiK27fVRLwlop2NBjLIFMxAP0X5XmXByFs+2wtXr97vtbsqwr5B9iW5MGqoCRjWKUkNd/PKDZogUkUXO5OfDbjJ5J+zLp/SVxzYMES9+bbGcxqR3H4sNIMFkAGJnnOURk0JZY8JwnsnZ9atyZdHMOI8WVZONEKowFbtu87Ftl63bSXSd8JJTEbaCdCyney1/fZFPSI4akWh1SiV7vlK/L7yH3HCjnehDcxLxC2/sa7Q67ZCvpghJMVcbonoZIOFRgrLd+WwgmZP4L4oQ/h1T63Vm5AgEQpHc4XYxmpmLhpjqjT41RmzILDyIf7RgNWt8o6aB7B+8R57BkUYk+kSdmdoMdXW/1oLAJXlPgJhFioFhKPTcXcol5nrbW9C0M1gkXqaOBmTardBQNNLx36oT1wyqRKW91xhhQOrVLH/TxQs4Dhb29StwahnmXxdfAyTQSn6cN4vPXyGZOpTey7+9mydccMgeNhapRA6y/y2udPpMQS5L3LRcEvV1mfcy+KblQ4XNmI4ce14dbpI/v8Kpm++/0ReK7+0Ok9LJfw+7Yx/yFlqsqzDyOUWoyjA2qPLWbPUC328hatzOK+Mn/iSO+mtkDUONYPmvRNEIo1copXgDqTKMeiqcba4rp8Y/l87lrTShClM3c48J9Pj3UHwnFHg9fzR+k9AReNuIm/aLiFMfLjO2zma3PkXMaMmuJFlwyfE3mgRXSwwOs1bmblK/jXg/geLrP5tIsliBd/pP7//Lqm5LuqpKaQ5W2jI4Ol7txStTWvxk5/KwnLa4CZF2fKpcdsxXsqWKV/F60UDvQyB34DM8EoiNtA3ztXAmRdLoJR3eq9uaPmbHCXe4DSpBDtQQ/eQT/oOl6+8XN+PC1YBTXFYIsitfvEvQiOO8faKXVrKDNv+sK3hwyC9yMHWEl5s5u21nwwbowaUSZPTDRUQiOYR9HLLUOMQSEJ8gYBgfYmxG9+slnvvSRJ3NDt03It/u4x2fm8SI9dwIgxr+tykQA7kkz51ceT+PL1HCS+Oaucm04OZ0foLLc/N27Pgco29TZZznHiIgR2pGP/o2821D4VtboyK632nX6qyJfnvBgGEdK+69wdP8blboBulxYnpv9jIa8J1LFgD2cb+r8WnuJLK0bro89j+8PVMUu+I0dmUrsZzHYxuEdaAMJWjhYkMq5zoIP16WmTb+RN5P0Ujxc3xJMWQfTUzne4ughf1Mg3V5xeDLhGu8oo+kh+Vg+cWkWXFuPo305lmqVnPgJNwZhwz1m9E6v7+TEopbWAyGaLfdPEtSysMHSIAki4c4pD4FEU9St3IQQH7MrDKDYPAeVm3CBftsACFGW+MDn/+Wyvzr0oSsHka3k6EIH/L0EB2tcm0J0To/D5LLf75F0A5nqN8foSyo9FiPnNIqbEdYP9rv8RjWu7RcP3/8tzLOPxCCnq0fDVjoEaiVUmLNgJoLtWS9QUVmlF50bg2Yr8KhIo0kqdNE0N825s7fvTx3DgFbZdI2z73M1JWzjdyMifJh3d0rzNSoK9IGrTnkHv4rQO3Wg+E6qdnO7+qcMcLnqh/6teuq5YoBkQhz5C1Xvr+BcFKs4wggBk7T5/qhCn+d96CQ4FWfAouwBriiUiGmfZmCex8qLa3SLtu5cnmVAMg0r7XUMx/FJ0nf9VZiRpZWxm4SraiD7DNBtBv8sczp8zi4IgSdSpYajHn3rPdToPpRDzREWeicytK/58ItQb1nB/ClX822X+xRHgdHWHJ/kZmx0CHRa3zyJPAY81EDVTkcJ0PYDAcy+1R649969butRFKfjS8wTpfQCPd/k3QJp2f/JMOCyyX1Ockdv59Vy+zvpa38vlOxFB1V382t9ojeYkSZ8PHwCDuYw1Ty85eUZZSrcW6a3pmeuElzGcTIggFP7ieFEEi8abRXsBeqN/pWFU/Lo1gHgadEwtdqyhtVwGrupeuHwPJjhc4rfaMZVJ5Y8wYC8elD88tfeQaqVDkOWy/5NfJvQeTiqg/bpnISRuf1/xJl/7d6P3pBhHHI7prb0M3p4HF90G+ALQx7GBkymqCGJaqmYEjcUy55biXqEM6pKPA0OZvfbLnwr+21IMJYrN3VpBUd3luayxwFtePnN76M3v5UrH5gz0tavZCydwLDxcpG7xEARqgsuGVcly0QfoQQZA3pqCeDH8z08YvrJ/4AXon1HK8Hxbf2d0qu1Du06CKdAgZx8R1wqRJFMzmdP1yAdRO/hi7M+jti6KmqM4rlGfd9ZuKE2MZe4rPE+TgMAnsIQew8WNhoxBQTotjX+OEu7uEm739NPjFIfmekkwWfXfSXyLdc1oGQqbj439Xkh73Qu55vUOv+yu8djCzwXTbpzkn+ECF5zllco7yufPaIo8S5jAwCo1mT8G841evPEEaWwyAYbbb0DbVYVJyqc0Nrql8mBnRLHQVKn+/s+es1HHsf3HlBzrV2UoiNzr8m7OYULdTzNHfBG31qJhaX35p5IpAf36vi7HC6Xcta8UatScIOaPwGJv7X0jd0B3LPPmPkts/k79louSZuxOUKYfeOrx9LxgWfLJDanLGaT3iG2eqfygi3qKXUz+6l7uzGANR7kxSGDLup2CIs+lUxlR6mNtN+XGGaBITRVljNDMLSWOpd+uSLGD+9Lj4ucxk050Rm3VwpLVLv4M4fIWp3QquII0ELIMmekwxmL/Vl7yrWpZstNqXvttpvHBV7WCjrbQyn/zB/6qX+GLmcKt+P2p7ow/tU+3aV7YbzbASwBKnhj7qobXLy5U09Nl0c2jt1yH+VMkfYF9rDqDpgf6fiaaIYEOYo+v342j3GWd8ZcGsU9NFSXpT8vrDxklobH8Vc2av719oXD+/82aqJdTh4XDkHKz6fP3kO5zsPG+aYGRINxxFBPXog2IqAKO9u5fny/ONE3dBdW8tV/yp91qvFjr947305sn2+qD5njhnNv8xT7711LNs7g5A//BLrdvndXRPaV8+9VYUxeaevFPDXLeYbOqr/hEIqq4Sc5tfTnKIL+ff2dbNCSFZfuSlQr6qEPKZF3OjNGNUJbsP9qi8sTyVIO6oDRzkAc1P5y55kQx+KetakrMxLLF8B27yeVi6J+iackmoGc4FfzP77eJ7HUF2O/0Crzk71YIADt6xYOjf4iTcoILcQovvjldzrjxJgrwJjpzfwpjIC5+vcd4jzF3+RywLZp4fS4vmo+k+NuF/FdpflKu+/hbS5pn8vn68DfjhasZHj9wtkLP1Yh7rjSSH7plFXahSFD7AgqyYomzBtAzJ0MCx2QNBB2TNyIh72vzPI/SZZyjfKlIc2+mNICkezW0KBISWnmNLSFAVRTb94Gzeqe2JkwI7OvIje4TexQ5JfdM2rzzxJ/+4t31Zn8HTX8bTuINrthgrtJFpuFf+F1hGeOw3u3J/p3nPc+3uGXYXDKefn3tfl9jlObrPH09V96aPM/5xcut3HxlBoDRxUBWGaaZwXWi850DrwwCQ/pMf4pm6Wb+KXiRQjY4hWXm3Jhj5HWYf36uZgj2oa5fHDJqyYH1PeBM6/UZ0b+cA5m3+QzpEEGbDSeVIVZIB0K8hvzXE1Abw6tOq7n/YpRkmKb3H429rQMKwt3y7Jw/hP0BMf4w5gebdf9fvsbKgxfQ0sRAdpZ35YCyDtPFu985ugz3rSSf/XgbAFwWFM3lVqqMhgNIJ4tLqCHJcorx98OCyhtK6i/YI3+0hkOvY31rmIuvN2YOFJBBLdgmHd0SJrq4VWfqyzV6O88kNoDikhlBYDhO7nkRLrTvpi00f5Gx84QGmSuhi2TPMB7utxbVE748KK2IKoumvAgXf03/STG92hqi77pMsGiy+2ntzhqY9I3DT0nT4bXrOhALZIocPu/a4sK9p0hUe7LqqkpORbHv5XVi9Llal0X59a5x+lsGwftlWTBRAhf8DKVN+VXQA6bHjg8scksttn3UcpVfSHsCGjjkQJIkPq65+XiwrAAHYmPBp1jT3CK23gTL7EIGTde17bXfT8BPYV2c+coUu5hwLs8NrtWy/VlULs+6E4NWMFHBltppiAP9Mk1h3cvPbhPNW8nLhw0wxiGjWdrUJ/C752K+fougacsyxbSZnl+bgoVxHA24AcRQer+h8cYcnzm4298hAJvgC8z23i53VE1qlRnHOtN+eubecF0yNh0M3yBRW5SPhxNni/sgZJbrkWiAOk+WpJhjrM/jdLFVumlsiLDxGRDYg3KigkLdteTf+dE7Y4Cv89yvSTxN8/49cTzKsggoGVUuz+75hQY4VLke5Z2VuS5YxDypWXw4k4CCsExcmsZolAry9RU4VNbs2k0QsmbZB+mfwRfCw2Vd816wTSOthlz98xHVa+Zio0OepMBCX0WJG77YQWSR3+dObXuVJlJP9fYqS1NJ9xvw4P2M9xt2Vfifpg3oNVUW7xtrQrpnP/g2eMJlVBzrqZwI0dJfgjPiXxeMSBHim1kyg4OsMpqwBJT/RiajnhSYiCKxgOboDJMsH9clv5cn2qu/3oMTQVFRg/G7yVt4lYUz4v/VtrvHUYXVuCyaOp39hhWgzHq2L9COj0tZxiICohxvSfpD4tOF7Waq5uzOC4IgxYL9nWZr0/AnRizMhN89ZmQ9K9X1H92+qXaxvmNd0GnweT1yy5ASTHjY28/3fVVD7HlIE92C1ouNBLpdA6R1VTFRlKL7wYTl5HIG8wzB8OOTvB3CEfo2I+ufzfK2jm0b5Rt6kNW5ulhe3i/8fBz7Fx2QKv8u9HKfd56tT4ee/34eywUGeieSfh7YkC4G018exxLrNYq3byMHjFX6r7/M5y8XLMOwtip/uc/zl/vE3i79+st9Xk7U96YpSYO/3Gd3Yx/usAf29WfbQTfSGPhP4vxlCRntBkNy3To+98MCAsBH1rFDWPdDMvxPeFxQ1W/+h2B0XwSYtcIRTf4bF2UeTA9BJ6i/yY/3P21G2AqNmeOuPy9ZYC5BWMb3n0ClkprD0buRTPeDD5oT0+3xw+jmfHdRRTPp2v96yFrQQyYtlBncN1j5wgCfTTN88/nHvYT++ssFAhpPvODH3U/0ifJbewfLIOc3wcq5/v8Xe1ly7dwIKY/eqsGGRSSn67+1SaDvyMnfzSHJf2qnLci8X3DAm82frk8DlihZrIVxvx4xfrMtfUzMw6MUPoRhG1lb74HCn1NdermuqbmXWqyZV1ILmjgN1HDxvXfzGcEIBChZZvMzQthsOi53WJ7kDBcxmCtaaj7Db6YJ5A7cq7CenhECXCVI1Eb5RRG4Bkj5cXTvmlkpJpSmhcCCMj5fGzAbexWZTxb/8pXtN7FqgPiqaD69M31ghJEQCjZ/91DnhdE9LtDir157Qk8y9Xy/mFINli+uY/8Qaw+KRRxZxEiOmvm7g6+hV1MRxZd/7dBFvjsmFqHXpzLdulfgj3cLq9LP+8Z6bNA4gdRzB1qZeYW9eyuY6vm+Ac2DCa9nHPCHh1qHFaQLbx7Ix6tDUife8QGIHqcydGTOOWBfdVVfbwA1iFScDP2ctFF3vs3nz3+RMycIZPtMPFCCTWEXmWYGGZzIIr8whr6BIJZSwsEYJ/XePdf/5LQWDMKwvoUzvTvO9278m1zwfl3vo423Nf/uyUUHxEB5fGepqNR10qYjwVsFJh+Xcz4/oYevhEd81aj9wvj22VOJ30sBV22tRe9xWi3fycpe8bEwVqMfKScUAOwAqz0YwgkRwYwCqRsi+mgxiSxBky1nP6Wwgs3wQQSoAqMgYosswko8g2cIe143vsl5P8H0phhJtMogtLqzMWy6s39MMMNNqQsVNAGPKC8H0Dw8/Vjji1S7G2DGca+4GkwPQ20jbs8HPtM++n7d+lu38OkKmu6pW8AOJJCuf+vP922XZZPmbVXityRtjyXTqmk5/QO8IuemvZVPCQhoBrxihqjxwavNVYpQdrE20cliIgayJTHyW4Afn/XwnACHdYQuAfMVI0fz8A6Q8FuIp+yNaioHlcs8fbBhJLkUK5Ip8NcuBVCHGu0jAE8AjhAFZY4Sju9UMBKTEq3gQ/nCVzp9+jczbFbiNCWO/9H5OI8DRsAcmYWMCQzD9goUOE70hbyaEsC+iVRVPSEII9FGZwgKc2OpLQJKFjGaqzwSQtcnoYaH2Ep3zz7gVmnkim2LkFOxL0RHrn/4TvDAM5YS6HQ2944fiywBRu+9scH2Pa6wNPAb1VidIqn410HvBj0Mq7DyyBUWbOqLPxEyFYQij4AzDc5BTXW/ILCo1RY9QP9Ujp7edVK26KsHmqFctOX3f5OjILUIFKfe7ZExjngvc1KMqcg+pBKq+9HPnC22jSPrgm8pr6/f8ogMQW4SgTjm6F2org70bB1vdfYInT5yJEXROL6PPl6j54XA8dAUH1k4cHyFK3lV4QbooLP1T8U7RLWuuqYhQJzfDoottyLN01oyxRP4CT0XEK18/o3m4nw4IlFC3LHjnvGTZ/bUcHrnKuCKgiQ4b76FDOWel4/n8fjyokxUmCZUYFTxodywFVXK0FFR9zTyzZgDLHmF9wiKZ3/Hz4kIlY4Ly4JEuqjbFRJAI6yiNjyTx5d/ItnxiZJkhr7Jq2AyteLX+CRHSOkCHaKdsTmCcAYajVFlmzNc/anubCX7TeygO3toKKBbxQYGCYmLMMTskiJ3iQiXrisCf/AsGxrnB2jB77XKUOW7Muku3mj95qOURlEjVsGCoh26pj7RAxNVK0kXVR6ifHItJLbbBFbx6VqmiI44cma++IxnKnimoYWMFXoGdGuknNDdfKLkZZ56X/h/M2qg6UrgJDTM3nm+tJ+iEUYyopAgKJDcvOfK+SwZ+T8+mL3eEQCenGYvZYQ+FBJT6V44EZfh1e0B8QXS2V/9gzB7u6dp2HtIDzJpJL3Jemg2UJU60RN946Xc0i9fE7QitqwLLMmvLFsrCmgOo09b9P04CSZNYBBQ2xnRWr2ll4P9Tis/kS9FEMFNWMqKjAfJ4+e4cWKWuIz0+b3wPXVBYX6HUYQ+9TpZEv0zMfifWuFCMBMoAuKYsTX43oqfbWKfVsgi3EfvIn9nHn69WBZagmFWmnbxBvxO+iIewzAfNM2V7gtq9pdQLuNlBFq60dgsyLiuDR7+qZhdFvliyj5QwaIgzkNwsnNXPXFFMUjCD++YebJ1tRwivRqSc2cM53MA9fbxLIPAOpWBui/lc6iitS6hACbZcXughAqfhI8wUVEnECWUgJshwpjSyEqL0y0IPhx3bu1heJUATfZv6DW05PuMWtMoreJR+SLGkZZ/Gci5s9ce/hrSi1w17POLcbL1G9Os2Kjr8Yv8096XogtszuaX1R+9yVlhkJ4vZEODnmCJcergVZxxaVxB3HRwGZGH+DwmvaZ2332DEXAvY7mw2Ud9Bndc+uvK92u82DJtbKwBbfUHkHK4iIY1UYtsN/X5xCZNzO235U4XrLCAxr/EWdc6BpR8HV0a4giiV+BKuFRAbGeVxZyj28R1psc8DwrBj7qDkz8vN3WJy/VmrfUJgWleXqHHWgrLsBJHD6xg3DMG3k1R08MA0rdWWNgSA8Tcf2rqR8PSg1lyMz4oBBYm5Q9JUxSLUaakVS14xu4PFTI5wOSlLhvdL7Dl3mjz+56zxoxvXIECPUBxPB9/uQyOMUCGkY46zPVuDmFJ9ycrGJPemoBv4/G63vMCStWU62K/CFChgU/WflkU/29FROWTu4lChr7CGvtrUscdlfIKGsxcAPN0JCL93kVjfeg+7dgLCOMbqeUmWmTKvDNIerDcL1ZwLSUhsb7UVN8qvItk84tkJ1lsCZiuF3bIBuITtX/f/ZIolc6eKqdY7qC+SoVd/O8BBcY9m8Lg6ianMK6DA/rR+s4gmjz9br5GDzEgxnPRldw+dfNwnhGQkZdEW3YFxwHxzLfIKU/18+B9V/nIpqJ+I+39m7MVvPvXe7WGqtagD7Q2P97K1nT6eTj1cFTfb/v6i0ZcyN9SOUH6VuUg/WMrNXgWb5G9QPcDQS4PBYrw3tSgTI53s4Vws64fxqPJW5Zxt9hQ332d11VnqvFuf/9oAfKu4Sj4kv+q1drrwJylc39F+4Bz+8JDknzi+DjBIRlOpxGWzF8Qr341NaZCQmmA207EfiRPX9PcN1O1xsJ5AlmvaVksc9BecaS2O5hWk63IjN/gDO+RwjH5nb/MYXVezBVEQ6LMGy7MbtsVsKX9ZQKWi3Z1CbCr83wxd7IRBASqA2zfi3qVksG9SMcJrLwufEauPv80xgHY7HtQ2HHdifkD808c/wQdCP+kkOCSWmfjIBp0qwNEt1b8hYIQ4K+J1Q5/sVeeEN8vW0SdJCFP09EP+333yVDUKNDMevlGBaGNi3ToAsvCyEWOHNifLBHleeT+retKLgxzYl/1s35mDqiDd3Zfw7N3R9gAfqQyk4jft7dtoG0zPjBBoPiCwE52PU472Iq2UrvqC6Dmo647LQShjHA2vtsJs7re1TXqm4smSdLdOWyf514wy5Idv0xQtZnHVlaGD6dOrkkaJ6Km2e+oM3A0la6X/09J8I1RZRgVM1/o6OieWpeA8Oz0IxmpZSNWnoeirWnLNIVke/Qu4GROEwR2fpJLewGIEcbDGajrn/Nw98KRPPW60JXA7NHYNdyygjFSF7w6fXLG8Ai6uyuFjkca1holDb7jAECCRLOA5hxaSqkam+ZE46WZF7F6CO/wuxWDKjcHCvvK6/UEspBn9UTk5hy98YJfwi2zBdgXAfTAL5CWB9zrjoYgFKGOURD0KxAEOzADri7CO45RdLQwuqAXQkO6N/9GdoP/27v+2jt/sHPY65rqFPIKqdmMOAUrs2VP4Qc3XKfGTVLaybjIv34EXVxre0Igq0uBGrFmlaH6MCXk+W1u3aP058VxdxzHusXs4984EqAJpvGGd12GLL8cXaaEBh1py3yh0fM88chRi7Sl5hxfmF+RLpyk6wc2kyzTBLqNV1/g2eU1NNH8fEaJFMXsX74o2EQMPfGDFViqiQEBpy5Xo1SZMbhKx5/XsWUWBTij8uZYtw7DylwMAGE2e3vJg33dkVion+HjtL6tW8rXfSBJuUCi/37HOyq45+a+re+rPKPxOqGf4f3dY7mv07Qcr7MP14+juazNR/+wlBYnFlAu5TDrZ226mtrsWclALUcaDSuuLQFHr8rgmkU5lnUfBa9dap/VwW4gfhRS3Xu2D8oXOzf+pXcUSrksopf/0/t5lyAv5xd28Ddb5PqrYKj/hG4LwJFF0wzZX84MzD0xwOT5P/vairRiFCWn/ssH3wIv8F9yumTpOP8WHPc1/mJzYPoKhP6P/Iu407zowP+VfzEfzL98+rW6gy5Z51P8orwgGlQLXv76/+ParRxhH4Xl28XdwsvN6NeVjlByy5P4p2QgyWF2XcnLC0VBx1z3lWvPoAoOjC0j5aPxKigjTnUK7qTVIZpjD/xy/ozyhcrCfnJnjDMdB06zdz1A7Ym1UcMeQG8ETJSPYvr1csPKzOHbDGjqWO7+Ov8mB892lmWqfnmHpgIxnym/VjdWW2PNHtxf96dtb/W15/MMNuyu7vJGwEIv1+KAmjUO+fHie24AqMdubz3XAYLxeeXlN9vL3VtywljjmSximLsnSPk+IpV4gqqDcGTwWtr5CxZTxkW7sEYcAnDCn5hA92xrM68PqDQDaguPZ9GzxlhiIGLRtbFaI/1egejFome6omCLbcbHw2M3v/jXvK6ygUKROxT2bjKBTtQCCPk0d6Oj0oMYZ+Z5POprYJBshyOX61w/A8dWxiYAyZHELPoaKFcmE3E9wCpePC1xChSNJeZyuWmCVZNJJJK1DmbznfUNRBRg5Fptmi7N7eXAptjvhm7uil/2lUfbRrfXoxm9YIJT3PE5T/iyz0VB7ql2d2+DThXp/PoKvWGujvmctpK0KUNGM707z+S5KrW4ZgPQL+arqJ9gW/TMHkvRHhamBpQ4zd6Cy5iq/YYPTOsIJIEoHkTrnWsFPfncg2nbUlr/lJ5+WHNlcaxbgjnCsUQbB4L+m2eSJ8tyEfftXFwIgppw1X+KykAnxfbUZtVomn7u0Qxj8DP8eEsn6QL42rR3WEzBBhDA0SQZ/zfVFp8hnMSTC+5ALha3TjJYTd4sFXtclOwt/ukukgfGhxxJxI5Xu5695rmji8KxOFhOaAy1f7A0WqJ09SY8eYQbLv6tdyjuapuzQhBPykSh62rWXZCoh+W8m2jTuzM7/x1gcgeaLjcvcheIcj8Xe+qdAlSFoSiKw+XnOh5gUhPHXcgXqZw3hoqs0Mlyu8nXU24nz+vKQv2c/uMNdU+VLuxBwCbxQndTiVl3UAgD1XedioGbVJUlHnwflojfulPeWMYrd8A8LxQ+Et/KvNCtDFu2APfkKw85YqC8KoeKLoDWXqdkIJENLxkrwxlFlW6V3PXuiA+06sHNoGtC8JDHw4x6Pz0vFqvdBfviN90JdS6KHZn035PAKhgmySS6jgqKXOgyVKNUZ2MOQgkncqYhkXjofTzMgH9D6Pci94OZQ/csOfFrH3xbKQLyTPjYsKvrWWpgErNqn+oZ/6fD1lU/e7gcsU+m6iukUt9Pde+LgnBFVFUnmZ5CqhtVtW0vfX6wiv5vTCaAKZhx9pvcm0YPVBim3JGv7aseUbH7yAr00FUfh56kJ3fbPeh92x7IBeOYAXXP9HDC57n5lDcaNyLurt+n1P/58O0Mduargm8Do0nuGnBeVxpeWi4Pjgfcp0tXoYF30/U09d7j356R4OjEUY+m2ney/JRV7E+G1WRfe+KLjA0P0/pWF6opzNTnSWXa+BnNNQa1cSmBzP6xGY3OALmEGczxTJzwMp0rPJZP9mFhaUivrRCUI6ru4ipbkXME60Zhv0XzAg5qBBFqU3CLADT2a5d0V92q/M/Pcn1clGzs7hN1rINHsHQllDToji/cfrlLJ479zNTAtFfumRrXYaa+XqKdSKjrWbCZOeuxes17jql0w/sFg1FR5zrPb1XO5svQn22myhCcYehlLgd0eimaprx8zasyLU+rLk44Zj0ZPH6zOHOdeV6vS1SqgYM4fwoWQhVL/3kG2Ahyg0nYftl7DtCjEJW3jLQX8Qr7t9Gs3qY8q8tDaXpssDufouwcmOgZfI+R2nwGZEljLtljvhnb/J6LdGG3nRbt3fz0pDCrJBpYAPHCeDB7Rnx9Y3nwciPeM+OcqDjiAigJf2zZiqtet82c/0v7X3vJvxWJ1h/HymcpljCqLKH+zmQEbP51MMFIckLDXLCOIn4F4oNqRG2aq25b1m0/QGSnLFAGP46+qCT299GrNHWwrkOESz0s6TwXhXnp5BZ0YmyWVBYHioCx5P7xmqq5o8JUhWVTBE56ACoz9aRDd0kBSaA1WyNiczsBVC8EX5HOfU3c6gzP0nRYbu7ih9rr9bnAKMzfcbVhlD25Zi1yJqjDhNFOMvWdJGcgP88lLHyK0ANPeog9Ez+Q8ZBDOVrsmWtnbhksI6Qsp4mlzEpbUBQg9jsVzZepgr9k0672Vvu1/cbHzNQJX01fI4mKZbbwHIftI/350I/3z/ZQmqZ39iJnHhiaxFVnmqH4TnxDRRNrltwayK8/eoNz1b4HUbAMagsiIjoGQLDUQOARVFR9euryCFW29KhT0r0TjbVsIHLfq2bMV9AR0O/4I1Z4a6GM7Jl8x7zR9mBDUul07bKFz+PxQHE5BfzwgpQ9SCdNu3H5alCFJIvbC52d6yhE9h1DInq6RhQJBVE77XwMOjK0MMXdeTqJljwC+FoNjzm9nWNrqsXzcjvKCj6VVIo9jUDCVPA/XAh3d/0FyLK+KvNDnruYm+cAc2jWbdtGJiMZjxtSdT1/QUbHd3laaddMmFF0FZ7qS2bwofAXl2uDZttH53NCFW+BNhsLLNR+fqzmcdC8sAhBUfcgwIA1T7+BOZeD0cdzEiVhO3rhURNkYtpRL3n0Z/+AQ9o+TpmorWJZR12QsNdDj+L0W8lBqZqyVxo7rUtfSXoYtGZDFgzUswOoDEjH2IqNwYs7ecNKE9Uq3KNXdX0B/WSx7uXWKp+s1MEL19Gl9uYt6xZWA3qHiKGHK3J2v9Px2HcMP3Uc/3MkT0UZBcWyzdQsza++7dSSxjGK7rV/6C2ANgqdDx4Ein/fu55ZkqNfVBaWKwu1+swr7kwXOdvqYvM27jrAYsrqsHqY/ISaeeWMuWVyFu7vAMxSTPN0AWffFhN1g6DAETM0a9mvflbgotzqFCvmh766f+FHJxWazHQvkHypnddI4VVH3f1dJSX7ImmBIqZPs2c6fRDFRqD8xzHtgglLORjeZpkeVUhaXKi/ddVRLeUt9CcNHiUC8Np/KyzAXmEjwpKNa4sNHOvf6roFKveqrfg08jIVHirXZYdICzE/CoiuFjmCVOHCPgK94nZJXJmSQWKN2Jbm7e8PVPtkCYs2CAG9QQKWICrpu8GqECou+iz85jUWzIsMBqkHc324CyZ9+5f0WoQsSYLk7D27f0wOSbKBDv/N47GZz2p8W93IZH5wmWEUBslgUAtUExrhWsBEAOXA7/rJJhfrXFyoHJLqt2b9VYAvdyWDhMAO/1fFK03so304cTdCjhvdzSnGX6fBr195Ujk3+CmNiiboNDD9QPiLy75+HdWvKkSHv47qCST9p7j6Ln9ViuKvSnFKro/5q7T/VSk6kfAXe3nRqf1YUEKaGPjwbuNHHrV5vbfcmFV+CwSgoWiJfmAjNx/AN+XHdQLo97XjzodHVvhvOu7kgJI+Z3uyyV/E915PcWdKh/FXA8GBkg/opRTIj+V+5dcbvCtZl5Esf9HjCjDZio3h4pfZ3kr/eg/j48A6hX+/dQefvXvU19x+FSotCjpY/p/WQ1OiKFyuJosAE41U+JnEAgOXOEPpVghGKSOUM7pzeXHPZQLj5/sLrUyFROGU8OLZI9XVFkyWaQcPjTRhac7pCLgDCgPlnz7aPk+x84EGVWCe88XEgtbuz3Yi8m9DyznoOFigp3tx/N5o3I/Y0xXFXyiKb+SLIELmwE/8yzQwfAeln4AZkpAMj4xkYf4EiiGQxA9BTFV9FbT8vGvJsv+Zpw6S84TE0U8IFPTBiBdFcLRjBHz+/qiDmoHmGK5dWKaFQXKMhkVQ+rBfbOML+4SvwDhV5ORncOMZhnlWkKB06cTlBL2+HBFgM4k9w+cczc/Lt6Ds6D6fD9AzD4PJoWAlZ0CQMAaCdOH3BGaUTJIMdC/ofBXC30EpkwFBcHLBadG6KO3HS6MoSyBdoUvanikBkOJYgZDLExQZjS+91dj9WI0slZmQ1NhI5AZfR+SSg3iASqhMQZ6mcF9hP7l1OKHhzdFncqBMXqH7P74sNAY0gPBj8G8iWZeUhN3p5WeVo9Tpyf48dS/reKLndIYtLzj5WtzS5JuTTaXBsF+8AmdaIvbkvkPIzA6uNP+iCPjwaI97/tNrCWjBhp6zvLysWFU39rFHyffRX88Upcq7Z5797PFJV+QdvzdrSW73ReTRScCvffv0SMWxF+a9ePQCLS9l9C60fgZYvhlFDhKC426o36jgvz0urt85Mt4xBF+7+2o6MIYqf2LCs2QJmzSdrP91LccmHMcXS2H87SWYxIrQawyM2lZ27+t7yOvx3+oDHHKx8BzJ3POpZp0WGcpT5xRGeIibzA2cYMME9/ZxC9TjFDcfA4/cz7jDIdOL1LD1zoyEK4PHj8QlkwX2o3jVNqNo9BazeXIBCNjqP6NWlv4j9zmPsVO+C6bBu48x6Q2uzDUlFHEFzsXv41CSPi/EyxfNh2LRRlnTKG2fCNRoK4T8R/SJC5/C2Xn9+FD5mTuE6m1hOlmlII4xgjHmLc5aWPfdhVeiHG/dtF8fRFjmz+hI53Pa04n5xhyxf/us1r88EUqffEWxQtKeF/D8v9p7j21XkWBb9GtO9w68oIn3RlhBDyS894ivv5nau+rc8b7gNWpVZ0slpDSREXOGyQADy3gGMaS71Y9AFriDeu63dOrPly+qdc20es7zpu+ccwWdU56k+k2dI+6aU474qW/OVuQmVs9Rut/2eM4ar3vGKbbDkHv7lgpGte/P13DAKMHn3N/UZDiixrdy6YrreLKU86gq0/MmsG32c30rIwHAcPv+eQbI7PNlIfQkkNj8h4PkaRbXr3/xKF6QlsWgwcNpftdkMLt7oShYapivkem+hMRVrohCV67krN0JQ2jukzf8laPfzDGTLCQ6NolAS+r8EmZq4u2yC5aPTomcke1iVwlRrPF6S+oZv8VAognMXHDEBVLG3/om1QttB8X1rq5SXFrArhwLw4XPu8fJVvklJbt2/Mpg6LyAcMOvFhwOX/YUCPuiZqL3Lt6UPY8Z2SZRmyn7l8UDNSFcazG5BQZjhC8vKB7IuypDTjnaie1tXWChV17UkxIRaErl8hZCbKL3jtM6C2VXxbqJkaP9I0mouw6HiQyWrr5yCbGwPSZouUD7doQnRgDK+6XmxPviKMbxe3DavMcUjkQVFAyBvJxLK3GCBtQHZhCQJs6chhGtPRtQTqcwB5Gob777p2+Y0g5/ggNcRruCLfa5IBWBdeRIMm4u+zLcT27I8tjYuxJ71zRBml88NlVsnu7Uv4X/+ac5WGazbY0RxyI0r+1EvwKnZmXnSF8hxSSJeIJF5CaVe70hzK+yVRLK1nRf/DS2cf742/0ZknHo+sw/+AfLh0+PffpPXmCfeUBumHpT5AdM8OZMHAax8LQ4HrN5FA1HqGP1FRfzDfjqygOoyWztKJWN+afrkRztzf46vGblYRMuVuUFTYby0pBlrmJJ2ZepWJ52/tQIh2j0zImDJSrkRGOmJ5VnHjVMCt0DygRwnCMINBRt+ezGL6MfxbnSyfMzX0YOj0FTlgxMO1M2gzyWeChjMtYLYdQog+UArL69c3j4dosFa2jcFt02mvM8WOG0v4WfS7OfmOJdQosJ72gBDPU7DObpbPd9N9txwG3LsKpRsEQARh7zP2smOq/qWwN4RtDs02SfahCMS4M3bPH1n2ypv17DNA2csEGXLv6anAGx9DEaJI80/tKCqL/M9jXgsExswnoU1zFhgHKaALuaELunwo1pz6JJk5wPm5YELG8yxwY6AeBg87woKDfWvWcBtXP2aipW6uZwRa0kq7q0sOdRrn9E81ldX6BDTcaxe/li5m6p0W+yfCWDAeovrub1TVr9Ha/2shHDYRPFG9bPqZLu4M/3U6vPtGxX66gdeKttqbW05kHWgTKBBvZG2RImaw4zEBt6KWRyKdBEpbZwETWx67oSDqK+vsb2uTPfgBWB2tDix72iTvq6Dxstnh1S8WVbyP0E2/SQRnQIWIDmn2JZvqPMxI9XSh3YN2eetOjofG2EUb59YnIVOYDio1oKuFSGvnQzhBoOLxK8QGm6/oWglwTC7CN/+I93kQy/35dDZNrn1+I+dmdP8S/0Per3+I994Qi/ZoYAftfadSRlZ68FEQWG9XDdra9Jf4pWZ8jTrekvud+XJkOsYqvi9fgosTQyzrG/SvZJ1QhXlN29+JlDQjWKSuMn4Q4yZznefZqjFNWIpjgKGtDlh8W/AXioNbVSkmKfm/IvWGiR5bOAZIOouVmhpfmgYtnR0p6SGPvWknun6rUin4koG2DN9fuIOAlszIGPsKm/HbkHvxSxY1FSA0wgBaElWFET+JizFswiFxKoj5Tj/afCwjv/mk6BabVCDzBOsxYeLlwzkeZvco5QQBt9trmRj+kTqVcKjPJEoNMDSjEDVURawtu7uXTUPDDFzBNL3XiK84Xppah5OnvzXqkPbou7X4Tjg7rUns+gSpJyC83fXIegY4O8MXehm39zrZFq5ME66vvHa1le3CuTHSc+oTMrCDaWrbSRf0rS6IXxI3yCuXNscoG5C+OdjWTKVvrzKa6qzo7bGc2DOPEsJ8w+HFf/km6U058l66bPlnNTtotsMmLhWvRwXG/K7Ck7+q0fp/J1qxmrNU8c/yzLNwEMRIUk2/nens9WVeVIlBvYEYEqzv+9nXL7BsLW/Zmb5jrEHUGZRFADj76x/sdLvqAr9RZ+EgO/h9PALqU2n2CzCVABzzV1VOqKu1Lcl/j9diwOI1+ajmQ9OkD4NIu/OIBCmhL7sD/Zep5AMQVVLA3pSHst+Iw9pWAF+Gt8OeVvDdg0L3X1pMV7C55QhgRFBEtvNwkidOdPhk7OgjKUzz6qP1nVo6QSrIHaf+x/GQhGxM5wEY6z/KmyVv54HezB6Wtg3V9DWb1l8mB223GI1HGOedxQE7omPxVQsMjrOO7vl8wKifl7JQzU9qbpOIzHoI+iKKoDrinsWYxHzvYwLjT8FILL0LQoLhmmFOJequxsfpjFQa2PBrRA2zcCjmEK9kF3hOBTLoFnL/r1r1G7FiGoNLRfsP/cssu+fxMaTZ+3/ygW58AR5CHg+LAry+NB6esJncWf5nih9wcA0AvJ872uVoBnayJWFPw8X8orDCO568JsZjLN95u2M51kmkgMu/C2xX3HtnnrXRxDR7yJx+O8cFwB2FrjzTxxXXfvYIVkz6L/+Fvluv4Suk59z2d7KO9fuuELjJP+pWBSDgyRnI0vNFUlKxSV5k5Wq15CAoS3NAQxHsLrVXyknALsiDgx7HtXcdv3dZn/E6U1aCNJSPpzPQZVHXd4/+znQwvX4fxqfTiFBn8OvCMt/fYKC9kpobiJEhu+6/aHTmLZ/WH2TRIJhvl4z0sXk/tiCEJVfpHIovg8dvFSKcv6JDH+mWGaZpQlSfK23As8YtivT4I6SGsCXFt/clvX4t88sbpkSnMKYefAAGIdBLyfyc5zYHra+1fuZB1sYpqdA7CW7pvMDEWSdsXzvrQF+2D4g/eJf150uh0JeE8U25/RSxG4t50mCR2HXP4+M0wMkWBqkEP4051Ogr66RYvwNaK1TcC4qzE4FdA1ap6XqkMxzIv0J7Qg4ljEijzL45bv+/59rP/bCiL+lctBvxoH5GhaftgESP66DgqQ9UjB8dfbcS+STJTmj8wX+aNpcJLUnAreZV9DTXqzywI7VvCAJ2xSRu+7zfOIlc+16ivgv2F45fZ81kEBJkiWZetimfSCTs9vjNyaKFSRnbZv5h1nGeE2QGljDRykKCKE+niAQTRnOCB9O5EoFvWCdBnNBcezdhAdCjx/QlSOXvPyq8L6ZI8HmcKsWjgh7XHhYFJOLrPsSai38E1XOG+avJkr9IEAKgCLIg0VE+flVq4k/zBif6KSZlRvII3fm01WBEXP7yp0f6NCeRj73QR2/iUIzbG2BtLNiAthfv5rFkSM1/liIvWqGufwn8/HJlaf+vOmCfJT7Cn8yIP8tQd0HPSB9yri6WrIRkQL74XJTH3vF1Oey7Dr1UcGTsqubIJRFvbyuHlSA79I+JWRZ4ZjnSVYN46T1Oeg7YBWyljxhWoPQwK0Wtf3i1cJIFw1ZXiJIJakiCjDgyAyAUW7XTGy4m2uxArvx/5gr9AlaWZaAFP2vubT99FrEkK0eV6cutc+n7Cl9E0+EOOqgtZJli27sOiRdJ041m5h+LbD6Kl6i5xfz7sqp/jlvoHDN19+Ht6DyP7tYnI+qlgt3GiZYaDPa+g2aqc8pTA0rIC+FRQBZjLJUA8oTroVtextjV/kOau4/juZS7QjxY6yV2LSu5WUfjEb3ARyiGCbxl10Av3/yqc4jo39Gl/m711ER7CttgIP3kcpyHZcDEWW9y7CQ2JKqJCZZAJ6bMSY51xpxKBGhyl8c2AixAxY0+dx6SoeUwnknY6iXIgrlLXLGYYCtLy0DxTZZMAqksnb+dM5OceuNLdlKUa1oVrr9QO77JwnGIHrH1yeQc99GLHsum2A+tS0K6u7li9/xsvl2/XwnHwqvIhs02tpctcO6phEmz2/+7w7s4fqVuBRzWmmadvlEmy2ye44HQO9+T1hHuTEmAK7Qm3JvVsniF7Y8Lp3ex8G6+sXkEsczBI8CiQM0dP1dMP3BcvC5HlZ3sb7/Wva+HG/Nq3vKO+l8WjE09K64JzLcikL7ON4PH6XNQ2TiW5Mj8coWdH0OgiSZJmiDy8QggoSWDHwD2v5A165k54mgLpbvoDEDXqneaXs1CC2vytz9J+CV+QtIUpsEAKtE79lFkQVlrIn9nWn1dXiPI9CgrF47kKRwMi/vBeExzx1KkUaJpMCuwbDzHH7nDRmCRXbzn969rFYur12uD2hmi4Ow1DXWOUXLfl0lZG43/QjiX4uH+nJuOo8pP90ulZgGHrz87N9HfGcm7PWuYK2M2Rvtf3LbB/2YwFGveR9yIym8TOqkc7wYY/6l8E0zbHLOvVXDCdWj0nRf/f6HcVmEPHdU2+VscCZB07/2o3hR4Hk7uxkPkw8PiAbMaT0klNwxuNR0qH2CFGwgNGJLyoHELyDPUtGjflnjRhpfWoE79Be4MOmxeUd1GS/+U3x+ShKFGVAkebV9/tFXhRThJ9Kdd1VAxLbvGPKAWho2wwbCIYD7FDyYsU7ZDJgCk34l3euNsPT5VW9+nzNVDXI3WUyMcZVToxpb01iIkihA2xHBwblJv4izkT1VC1A9Skh7xew9O+2krNTvhF68oN0BhZ6GTy4NMi/noP7hrUADlrUfCrPgZs0bvYqimNDrdAZ/D9SJEiQ4E1qCYBkHFTdAkz6sENWqISfQemE29J9r/ASlz1c8mEDdKvSBZvNcrqLeOt98SYCxpJr1Zub97sT99hMrSBLZq5tvvH5R9/vTXVV29EorxHr92QM1ULD++dHG66AGks+tF4HZBNpiunepHkB7MtiV6rDRwVB2+KFffwc8uTkkW5RML3KtsuLiiQBUgNmXTeZ592zkc8Yom69S9nyj5ZGCIK4rgMg8lllfHmXtCF0HzDc9glfTPt6vtkzWRe00VqKz7a8j+87z7xIIxKV0C+5aQBGgtf6U0v1x+KQlILN36mnEkLFK0lROAV3ljryI36PMJ7jckjmOfUuiSfdTTBfOvX2WYe14u+ZBtIU1hNzy3zdb4Ra3pr/0j36+YDSUKsQfGGzjr8BAQ7OFfCdmQz4+YTWKGdnbcFSizQWBAFY8TJp31gi7G6W5dI/mKeoMRlnwCatx7EXk3TEB7N1w/AtvUwRIo9MAwUtPw9DUZRRTpjO9X03+nOxWxFWeuDjJczZgx0o9w3Td0fSJeQMvqlLo9GGP5zvGPsCDOed7/GmscI+wgfqDJr09eDhwlbHxQcpkyuDJFlvoe9AwHIckIMe+3691znzhszzt/U4qvmKovxjxSPiI121KEs8QTzTvABcllKbWKE3jH/7dM85of46ZPEWaFWn1qwx5Gui4R5y3opo1UIvJcJ/Pw85W4Bgz+ZLKy1TEZ5A411lp9EAlB80E8r5IQ4ojiQVk74xD1V7qkjR+XOPsmIMdPp0TCah4HJIrK9FLeAefAxrEWb5oLFFORWcvMNOd4/KGHsVG+Yvvr4kG2amWw06HPCmQuVCv8tWJAh0/Xh6ICn3Cz+2LbT8L0ombsElxPBADRu7LGdLRtQVa2/VjEaatvIrIoY00PTfu4waOcL32brvWNtYm0sgYMuAeB5bN06WtO4UNEisLihX+GY8P1vyNRqzmYBrI4s0P6Ve/O39sBjG7FeoiprNQ/fleza7Y/ddYWzIlxcE74/03uKm8C2rQFBXAO9CMONsij5+qNu1ugQOyRcx9NM6scfILSJonDXNU8h9VTbzPvCm3LS/5H4Qe11seLaKtGs2XlEiANMNv+ymTraFV3lxXwiVpKwfUKQundPTo3pAOFJ4MYCLZ4r6rrmOulHRfJVHN/YJs1ToZ0Y/2lYUd372iE7IGIQ18/gKRNIAFg8w1k6U2MAb66/TTj9PQQtIPMKzo0/aPw8MAF7SGD9HivrDtLkSkPoS4+nm+Zexc08J6Im1IX8MONXAwxtbwgwYzod8PWBVzk1lUbf35sfNn7GGOkwmnGiOWxtUTjK8n+QJSbgm8F+O4YuBe2QvMcNQn/MJ+9ZT+CT77BCpVAyXREIDeS/gtTrqgSipUUQs4f3zhkyo9JSUcVFKMvozx02DxJ7zjuHnDTFGg9X1Z19IX0b6M2YeujbkLrfR988bIr/dgHtSOkrZYQmZP2dAr4zxDhfygt6QJ1UTYJ2k4Xovz5/3AKBINng3sIEk4ImAGL6dBon/MvamhP8ofL/3LcWCMf7zLHfuje/3n1o983UjSJAzg2+MgPbAC7TZAoVEEXWy6hdCkRX8QcRBLZ8ZWkAEomY9bpEha19/8CJHUtAi+RtzfT2yAhptECBORzcf8hUO0GC0ggpFgBYFtzHqTz2rxV0IIkpVD+g/D7kaRuExjjI0XTYN4b1eOEE+jPu+d+W6LtqxGIZ+3w2GyUAj5Tdg4rT9q2qC2rarwDzCxyAWnlMAQgcZkiSeUyXLwIi/DdhP7n3R18+NwQTMphkGERrG0rYK7HnjIdnZAE3LWTm0HDiqHFjxXsdpzfF/+SPtoCiKv4qm76ELvmaw/TheSQzrK84SwXFOABjRtpWtgOUyO0CkSbIP0AncrulVXfetCMBk5hLsv6eKJ1Ae8ALYid+PLuAUESuXIBbaGUAeuUs+n8+LQJ93wl0koerff5BUxViSsis+IIqFYqxxmlJXv+/T9VgMw6h2WXCPaHzgLtfDIL1mCgCkrfvDwHG8l+c5jXUD4NcYMM9bcJpcKOJYteT9Wk6a+rug+8wWlG1jW13TaV4U5WLVW/mLWJ3e32bZvzg7dcHY6SOXAfqmNJhiJ8H85grmKAnotu9RCL5dlM4FcNJqJ29AQF+yHIZhqsXI+loHTYj0PI7HJbBk/vjT5bOYqr+zHQTfLz4SzPQzTI5icluzuIkTE6DU4tiODNYrpep0YPmj/AI7/86p+6O6dTc17rGUJRRmWRPrX+t4qRxwHIt8WEXs1L7y0PX0bcLsrOyTd/79IBOnGWDsgk5gdfx9NwbEObqu08af/kKkpXq8zmjhSxf7f0Ujygia/DNkXAiMIQJi8AbqATvdH5jFD+L1x9Y7xe8+SLltEUL/+Rrs70sz/rb7OnaFBWJhiieQZw/wr94ctneSTGWVGQDP9Ao4SX9iADexFnpZKC9VjQtRemI3FfV972UY4JGSRMSaNpWlydqHPK+QkUww6NHP9+tVmECi1mlqwo9sIioBpll9ihfYxyy/BgRIsu9hTgF4/ClGsNt3zJ8f9ZnA2i8ktn83//V1c30+CJ0ARFe10Qu/L0rcZo9iTvVKV0Dcob+LUBbyJy4o2AVPTLNlmsrdcY5pfKkK2722oMnXY9/rOp2cQwvZd5nFHpdXvt1rUeMLp/vR9km7xLVmyWlTLT5RRdfUtOuAN+4i+N3OyRYChvkML7TtePso3m/KPB+A1vm7DI4sZZDxjWjYOKX4DdYvv2Rk0njTAPtDf9qreb1jBkzaO6j6YNpPtXwvbcrsH3tCiEAmtcRsJxPX/vEEQA+L8ozE3k9WAVp6mlZKW/MGPXnwaaJp2jl7vl/NvkIBrT+G4m6atk15+44SiYQZ0MD7XSPAHKewKAfYhRElY+gPiBudoddxUekjpb611sbX/dkYwKFDsQRqwt20vhseohh8xLi4wwUnQmvr2pZgiZDFr3Hzwp6O7o42493P+erN+UU3DcPrxQ+KY8uJwyjDsYoe9XgYRhzWq4oDrgCdM/OPsRoDC8iEJrE12Au/cp9iMmSm18JsscNmDFlL0nTuzj8egeqPW/FmTDiKwOOjSNa1k01qs4vVf25OKLSKAfi2Ec/e7P34dp3YidjBtCzLpENyPSb9CYhnVbmxXkiSrEbSF+aoL90RtwBT894nxSy4JRQBhnKTw6LAYsB+tcQqkjEMHJf7Y5pbP00brdJdxKDm05OzECzi5daXb+aW7cywfgQYMYDkDFv21josc/WLJlnmhpKeIAY8rk0YwdCJJqliPe33MXFGwF0MLco8g/XzcrmBwbZuIiRy/DZVbX+MwwnNBrZB/bd1H1PXKSiP17tS6kshxDEzmqralZ8+4mz9W+n+WY1OcOAlsh3F5j3QJtYa/hSxLg768mUaWppY/M5Bv5DtE9vFxzeZam2nCMR+vKIItfzBwTwY7YLMfyZh8w/m7rh2WlFhxuhh1j4X0kiFHgSwa5yH6hlMi+c8llbkomsXG8cHAFl62AR37BdzXZT0UskR6OgwLMRJQzyH1DCgcV7UPx4ae0o/c4+HDH3PcWZY1nYgnpmq4Lf5cEeGI82Rk55amH/lIZjrUHryHepV7Grr5yCnaRTla6gtV8vjxSPsUVIsaGSyyhqmrnae6klrt9hB2T7Xz7sFnIrbqkBKJi/gGdXXR0Ss9dy8m2EYhAZAF/e5fyVNSKgh6qow5JGOTVSVdxfD8RMKQLrXNai2iTARD63ZABvyceeAXC7B+oj2l98PvcsLfDnF8blor4f8PNqmiYfmx+8ZP00JPPQppGqF76e8+Et5GECmj9dQwWu/yPsrqlEBMBRAOUrVCZic53mqrXhPxIdrEDO8g0Ua7M51gUI3YuhabTfTKwjIQV+qd3QfZooxhUvHDLmBnZDNFUlf8VskmJlOTZgYVCtroJPg76G2Zs9pT5W6hiGOa1UbJY/3fexrtiWyy5PCATLtlZkVOSubcE+PV1VXR93o8YDopD7rtzt5RTwMActyROIFLhca4+0lNQQBDZABzvY1PWiT+UAbe1Pl2p3DclVDlMb+4MrrYQRylL/FEPbhDk7o4b+OESeTPgwB77l31ZXV6yvEmlu47n1V3dCovd9WvFbjqiHT82ophpxxJ3di0NOTUhMq7rH7G8HUmzxZkdkDaMldAdYIkrf5EnPDx2Gn8Vq2Bfhenhe/tZn9Qpiu/hzXu/ju8C3GaF5S+CAV0ZHB0blTXXw87PSuh2GZwWR0Ck+q4Rd+MvJg5LTGedqzoBHjOObwPj2wulpqZOF4/VzGlZdyAdtjaNCz/TeEKTJK23al5qowGk0lDYH5393KF6PjJ/Vd1Z2YPOjzU6tJwdQQfdS+hVqjsmjB1u/zaWOG22rr0kwvJIHRWvPzYc7r1wc9l2GB0PJpFmBrG2LiE1vdcyPoyi+17cHbKRlefbiAtd74vuSZ7X83SWei1zFLD8Dr7+QSU+qtJnXw+STvN42M4cBVAhXhReKnQLxNnQLWfWS+krcaxfIWzvczhnl/H11d+7keLEqGUQVKwgKaqdVYIQzZKffFPL0oNxT3fuDl2SbYw7WCzKpu/win+ePhy6WO1ZsdHQjn7GJfjCihaC/ZziLBsub4RCpU3wNqEBj9cF1wRq1CZADO6fAs1EvhevgaPwQzLb0etGj2Vyi8fp4gaKFEhsECzGNgPfD7h6dOQv59H+PH2N13CIA4VO7P2oXOS/59Tc7ehmNqmOGxE2kDvQARTNBa1bohTS3pe1+TvQ4Bemv/TtBmEVZ/0HQ1L3qTMtYT8NhTzB0o93K/4kWWhV5qJG+U/cbU0gf9L3eZlGIG7UvG/kTLzLwxeiK+7rYsnhx9gW5imhi/5aotEgCElt/lKPfuPsTTp2IqRFBJUi5fi1QcvX1vN7tQK/IztXAtD92BFsvn+5mL3us9tX2ZqNpmhfqYu6yg+0em1XfQmygy1pdFiKUq1Z90PxtCtOyHc3xrJpDmh1dJJvT9XHt18QFfzlrEqy4dszswm23yAPDjGDdIoatjpl+PkbzFucMhuEaELCzwjuZLBCsfsNcPJ/cni/+yGniWE+MnwCby6vCfH9vOVU1nF6k4nyLk157MimwQVhx7FF7A8gCQ56WuwZs7xA/QP79Yvwy9Au4inMMvbq/UkP1eL1rkwA8All1yrCTFz6eBAwXPVuo4tAJktespFehl/rIneJ4rvQh+77EcpxNT45JH9RPqdyzU/T+RfMVWe4GcDaf+kXXw2KZ5f8i6Eu8/fm+Yqs7OH8ljFLDzmqfzHYzkd7uOvk/I7/UPIOsupV+EHY4/n4ThRTB5IVhI1PhF5fkTZiYAsn64vwyJvgRkPaqmV7O92B+/H8SQDex6y5su/mVIeCdMXuhpeP9hBu/p5QoFsnjuAOyUyIcBJyiIYVKVhDcp8tKZQayC/eFN1fhPhWKNB2fGYDAS6P4pd5Mcw3hmzGHeBQ1vwjl7YAk/makFWPE3dAt+IXm9hr6j8p0GM2hEQoY+xqSHq9zyFJkk2Tl/77uaAAfGsKmGqvnzOUnAp6sWw4emybZfxHbvByLlBaGEpNQyI4h0s/UDTcueQd6jGTdNr9vPJyyMfz0WQw0FK0+z8wHmux1LAivsFSkuild4UrphbEsDLynForxQijoL2EaAHF90kzy3RYLsJFlOXzBdJnZC2vr/8PzY+n7h3flN9f7FqGB75BKGqj+fgy+u+RH8uBbUWZ1TEGMNPVeGphFvwFnKcmiAxbdsRXllsRqcgHN4KMyXvOsH/Qb69Tqxvh+qJ8D6PGcCjiAev9YBNsmx2qWNOiqyhN4PQzVFEGNBexaYCAt0GM+vv5wTKi58oA4rBOZPX9+HLPf7mizLDIjtFwweCAWSobPNIDFYlKOsBsXQ9VyGISnjb2Ej8yGreZ4Hvv7+3VmTAADwJADYxDBdcJcyow99P45mKXGqbqwlgUEq2JUJgSfFjWE7JqkHg3Tjfnj5nq7eGmz/8GuQAFUIV0z2pziWiQ0VFnq1F0FwXc7+Rc0BWUcJ4aIB372vdaURjCqeXH6hf3bZ+aWWA9FUMIqi0vTEojQTQpiO9qk9zq+NZ8THsabSzxL9xfbPeOeX6C+/f8XaE7UmwOyQF7MB/vkQ/mXPfyJZW76efLwmVA44zkXZjmOotAOWCH9//u4oEA2NRIIDyyMfMzLYWoZjiO0tSej9NN4SCmPDT2SoYZ+4Rt/tNM0/1nhiWF9VMNLAS9u6BhHBMOQvGPgC3B6IhmRTQN8eB0Hz7Tq52eO+3O47mQbh+01Z/eX/khQUwuO6OKcRtHcC4/mODGk99Wq0liy2dzpfBfVDJn89DKPDFHVOf6BXAr2kA7csiHAQ2yJVlu7fncdrGza2Mfbr0iFJKeTpJ7Az15fQqB8LJ+EszV+f4Yp9MoMiy957/dOdmkjB7s+LMRj1NTbHebJWo5j/9u+SvixA0I1vl1DKJCCjNKxtI1J4Exlc9PEkhkNG23pazVyy2qrUeOUZziobIJNys/GUPrg5FrNPzf8tlLDVanmev0pdaJGh3+XZ4tjzrZ3tzWMu1tRlq/oANSmmQKbp57ONk2GAczwq38DXPYIHOoyD6HtKXJGP/GXJ3naEAUWVQdHPe4qajoXC+v24Pt2eBW3dPFFOZSXP+jUXhXEzrngdoqB1lm1HQUnHQPkutSvkn69QR9PXSzODozLI6on2+77U3JWUvnhUwZ/zIL0dAtOl0zwZeGgggwSqEb9Sl4LVCZUBkFy9PZNl+H7rA3oL3Mphnru7yHlATLBBe5tr2SN9w2UhiDiJw2k7z7JvcuHXbFEjbyw/uLZr5N+tariAQWxKPiI4foZ56+AHieyBQDzKOzLQRERiLMs4rqURBB+E1p0IRsv3QwjDX++eAgpbzSy/JraEhKk+X6ObukMmuBS2petspJQFzDd8COK6beETCvnRNATyTTi1ZX8ZAKNindgT6MaqHOTBBoK/rnGfOrPBPjS+3WDeP5ECnTpO+HhY4aV9Fw9DEFHnDHr8aOK9X88vgjckv09J4rx7Cm2bXRNQ2MNHimzopreSNOFHa5vGEa0p7OX7fm6ttjPIgjFTA6GxOCCCQyRb1idVx3XRNaeHRnL3ANGXb0a7Pu93EJafVwT9DeIjS9N5PRnmuj8krjccXJItTA+0pBemRqNIlqVXf6ALxz5pBGhzz8U8spvPZ/ASNGB3+97k3rr2ukqaqoBN5kaTDRwV7bphyIQKUH35Tx9BW6ewaarqeimVMGeoi60F30cLqkOb/nRlrdQ2/VysZ0UC/SHVSK92J/99iiWf3Vb05YNeWbAlqe3Nm6NmntNi32tMA2Y72yu4OsVqrczPT75R5tg1jKvtjDtB36T4v6yf/nF+eHLV6HMcRRg5JiRZrvXKYc4IPAvoFrc5AjT5PHe1/ixcFT9eGJv9LUXed4II8Q3eSgQxOMyg5m++Kjp6iYarwNAUkOE579wI+veTMIrmIcVth4F5LLKQl+Jnanmer592q9HhBgDO6+8tlSNJhVceuUGrEsnkxgAqkmRLZkKWLXsX4DM9LUgIa2A5/6m2Px8JMFdlJfrJJElZMnXdCb0BnDXk2pPrOSnKCqDloY8KKIzPO5ZCd0uerMezbcrfOGbXwH4Q2fceTLvtzEyde1cyfrObcz+09ID0NArmdFMWZrBcvci7ZNTJ9w5uxo5t8fEFoA+oH98trAOQbDyQWxUHJvp2eal2MB96Uw/Hg0b+xlv1SVPj+XCm72RpDo8h0JPEZVXNOMffVqG7BmP8L7l3XTeb0JXIWedxWpISijb2R9XYwQHO11e0g86avMVt9u5Y/Kt+NF9SynYilmZcqZ0GEG3LSUTCrNtS0ConN2mBU9w/Pr0sCzDWKd0Rdbd+lPNfwk/uTCqWvnnR24Iw752W+qpiexFY0XuqXi3DA5yuQtTEdqozTMjXBNXY02MKoTop60T9/cDWpOk0r7vhXdqOMP+iM9AJ8rF+kEF/02eOxdGAwVedvD4Fc8y/UQqgU0y82nHQeFSPSDeO5KFtFMTl2G+W52lCkaQ8xy7TKFNn1mSJrmgzYre+ViPQnlVD1DEeE4JAwhxXsJC2wXRL845YUX0h22qyPJ33ddprcmHVYjIkhVXEXM05+MTeCt8305Tk+7AZm1MoHR2GYm8yPxxNw+K+rzwbQvI9X/jDhTTPio5iizbYOYtznzyQx37uWo2gqJVu5qdnkuLCq4Lc7oOKtpYXeQDW2/ENLPTuyeYfmV9hYAkY88cDbEsZP5J87a6uC1811fOOU6TqirI59IHiRiQOK4ws0YWi/1wC2zHpb/migpYZRN8OH1DKfAzLxBJm3HC0CGN3s5Xj3WH1tGsogAygHcXYkSa87u+nxIVOMJV3olnWOU3LUg0SXz/WsfCwBjW2+ZEFFztqdNB0duulQMf6UW9Lp03bk2wH1FYuX3dQmrupa658uCrGSF0y1vzFrNbgPEiDTWawb/t8Eb3gD4MVB1Hp6ZrC6gJzsCNJPIVLKJXpxEbBGCy92bN3mkPQAo8B82GDOpuYJZqGX08C5BVjhtaRaL1xtFb2fCrlArCazm2tYXkxA1Cv1VG8O7JdNc+hFYgWM6jY6vhojhizCAKzm2s4iPhdKBVz++/+RWG7hZ92eYspWFPEKsDXn/w2dMYKTOx99/ZFxvOOv3GLUUV+Xfg6TF1tdE898Mn9Sg4Ljygk6GvkZT3tPq+JbtrHYwcK1gonw19eM7VE1qHnYlvEVDD1a96hB5ymTbtrovT5QP3Cbg7MLBxyToXkJeJbwKtcd43v0LOYt3l50bcHY9WO0MdpsYyhL+CXzp4mKLTaBezgaGasD2xbcK6r+NEhsIpNOe+ufm5FMAi2ldhVWBJMLNiGvEtI1IJsPrco3ZG1GXCScGR3QDhXJyLXIb8CgUkUYBIuzDyNMIuRRjg69MR/YF2Rk2JiiGMzIiEjAMkZLWm0sdypxvV6YM6f6DngFBb7hcls7CMbEPnr/mHh4ly+FXUmJr7LmuNPXcLzKmMi4hKCRbxf5F2NbVYVxCTPwqF3N1E+CZk7MdT5eR2s+cU6PTKib320A/eipXMccIEyy19UH7rnVJ60FIAEPTrxdqlSpeHqkEvjoDvvDyuvbfAd9/FgHgjzhRAKAuL1WfRWUj1tGDOuM7u4wub5a+TeQpRXOGL6pwrClNgwlgsWjb5pqWchJ5buapOHPlCz8GdWQV2+n+pmXDTqVL7zczjwPDd64AH2QjkDxnZ/XhM/rmFNReCz77r81RxU2syOghKIh0H/nrJqvdQN1zW5r/57JhV7WHPweXr0cf7WFVPBM5sknoo6/v/zlzb+qTts4RQ3vIdCcMLsMKCXRWR532XLjIQ2qQfk995hDLuE7OXf0mouoz6f+8/KVmXN9s+NPJjlPjNxDK7rOu/CObYNcdZhyf/Wbkg9r/H4b3etSLTrE+Lfq7EmmvmEr7vwt939ZVi0pvgUv1bRZoWGm0z61C2hAqrXFPEHQFO5NLo9NdTr77d5tix5213oIQVaaHIcdL0lXLyp7rl6xdw8V/z9xc3fhntgWdj+nFtyneQEAnnua6q43k1BztbZSlnyk5snEXq29OdpsYDdtM2lslr+MH9dWReYZ/ENdPRJ0v+s1q8JKlcd78CtqBcTPP944xQV1hS95c/6+21JrNjAK0/KGFd4plQRaNd37a4h8vM6lbCm5qkLy7r+skreJXjtngn2p+4p8ESgEQRh3OsTzLssXaAhvpp8vX71KTwHPWPsaO/fX5aK+4WvFW7IfnvkuupTEkfTrikDrgPPKU9wLNtM+vyyXlQRjpUvqYfx39j/G/t/Y/9v7P+N/b+x/zf2/8b+39j/G/t/Y/9v7P+N/b+x/zf2/8b+39j/GTusGhOoAt43dDiOGX8UFv6JneS33v7sef5/cO5/YGAeOfJly6//wQXwEv3z1pQu+bD9P2/h4v/gfH/J+djn2/IFH/n7AEUh/wcmqsGnvn8fIJA/r8/6s1V/3sMp+v/Ay4jh21Vel9XfL6cZ6s+b6frnjfLfX/hFtH6/C119F5933b8vl3Hc/p//Jy/pVJnjJ4ef+L8=</diagram></mxfile>"
  },
  {
    "path": "eslint.config.js",
    "content": "import js from '@eslint/js';\nimport globals from 'globals';\nimport reactHooks from 'eslint-plugin-react-hooks';\nimport reactRefresh from 'eslint-plugin-react-refresh';\nimport tseslint from 'typescript-eslint';\n\nexport default tseslint.config(\n  { ignores: ['dist'] },\n  {\n    extends: [js.configs.recommended, ...tseslint.configs.recommended],\n    languageOptions: {\n      ecmaVersion: 2020,\n      globals: globals.browser,\n    },\n    plugins: {\n      'react-hooks': reactHooks,\n      'react-refresh': reactRefresh,\n    },\n    rules: {\n      ...reactHooks.configs.recommended.rules,\n      'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],\n      '@typescript-eslint/no-explicit-any': ['off'],\n      '@typescript-eslint/no-unused-vars': [\n        'error',\n        {\n          args: 'all',\n          argsIgnorePattern: '^_',\n          caughtErrors: 'all',\n          caughtErrorsIgnorePattern: '^_',\n          destructuredArrayIgnorePattern: '^_',\n          varsIgnorePattern: '^_',\n          ignoreRestSiblings: true,\n        },\n      ],\n      'no-debugger': ['warn'],\n    },\n  }\n);\n"
  },
  {
    "path": "lerna.json",
    "content": "{\n  \"$schema\": \"node_modules/@lerna-lite/cli/schemas/lerna-schema.json\",\n  \"packages\": [\n    \"packages/*\"\n  ],\n  \"useWorkspaces\": true,\n  \"npmClient\": \"pnpm\",\n  \"command\": {\n    \"run\": {\n      \"npmClient\": \"pnpm\"\n    }\n  },\n  \"changelogPreset\": {\n    \"name\": \"conventionalcommits\",\n    \"types\": [\n      {\n        \"type\": \"feat\",\n        \"section\": \"✨ Features | 新功能\"\n      },\n      {\n        \"type\": \"fix\",\n        \"section\": \"🐛 Bug Fixes | Bug 修复\"\n      },\n      {\n        \"type\": \"chore\",\n        \"section\": \"🚀 Chore | 构建/工程依赖/工具\",\n        \"hidden\": true\n      },\n      {\n        \"type\": \"docs\",\n        \"section\": \"📝 Documentation | 文档\"\n      },\n      {\n        \"type\": \"style\",\n        \"section\": \"💄 Styles | 样式\"\n      },\n      {\n        \"type\": \"refactor\",\n        \"section\": \"♻️ Code Refactoring | 代码重构\"\n      },\n      {\n        \"type\": \"perf\",\n        \"section\": \"⚡ Performance Improvements | 性能优化\"\n      },\n      {\n        \"type\": \"test\",\n        \"section\": \"✅ Tests | 测试\",\n        \"hidden\": true\n      },\n      {\n        \"type\": \"revert\",\n        \"section\": \"⏪ Revert | 回退\",\n        \"hidden\": true\n      },\n      {\n        \"type\": \"build\",\n        \"section\": \"📦‍ Build System | 打包构建\"\n      },\n      {\n        \"type\": \"ci\",\n        \"section\": \"👷 Continuous Integration | CI 配置\"\n      }\n    ],\n    \"issuePrefixes\": [\n      \"#\"\n    ],\n    \"issueUrlFormat\": \"{{host}}/{{owner}}/{{repository}}/issues/{{id}}\",\n    \"commitUrlFormat\": \"{{host}}/{{owner}}/{{repository}}/commit/{{hash}}\",\n    \"compareUrlFormat\": \"{{host}}/{{owner}}/{{repository}}/compare/{{previousTag}}...{{currentTag}}\",\n    \"userUrlFormat\": \"{{host}}/{{user}}\"\n  },\n  \"version\": \"0.10.4\"\n}"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"chameleon\",\n  \"private\": true,\n  \"workspaces\": [\n    \"packages/*\"\n  ],\n  \"type\": \"module\",\n  \"scripts\": {\n    \"prepare\": \"husky install\",\n    \"clear\": \"rm -rf ./node_modules\",\n    \"lint\": \"lerna run lint\",\n    \"build\": \"lerna run build\",\n    \"changeVersion\": \"lerna version --force-publish --conventional-commits\",\n    \"changeVersionPatch\": \"lerna version patch --force-publish --conventional-commits\",\n    \"changeVersionMinor\": \"lerna version minor --force-publish --conventional-commits\",\n    \"changeVersionMajor\": \"lerna version major --force-publish --conventional-commits\",\n    \"toPublish\": \"npm run build && lerna publish from-package --force-publish --conventional-commits\",\n    \"toFastPublish\": \"lerna publish from-package --force-publish --conventional-commits\",\n    \"toPublishBeta\": \"npm run build && lerna publish --conventional-commits --dist-tag beta\",\n    \"run-tests\": \"lerna run test\",\n    \"cz\": \"git-cz\"\n  },\n  \"lint-staged\": {\n    \"packages/*/src/**/*.{js,jsx,ts,tsx}\": [\n      \"eslint --fix\"\n    ],\n    \"packages/*/src/**/*\": \"prettier --write --ignore-unknown\"\n  },\n  \"devDependencies\": {\n    \"@hlerenow/git-cz\": \"^4.9.3\",\n    \"@lerna-lite/cli\": \"^3.12.3\",\n    \"@lerna-lite/list\": \"^3.12.3\",\n    \"@lerna-lite/run\": \"^3.12.3\",\n    \"@lerna-lite/version\": \"^3.12.3\",\n    \"@lerna-lite/publish\": \"^3.12.3\",\n    \"@lerna-lite/watch\": \"^3.12.3\",\n    \"@types/jest\": \"^28.1.6\",\n    \"clsx\": \"^1.1.1\",\n    \"conventional-changelog-conventionalcommits\": \"^5.0.0\",\n    \"cross-env\": \"^7.0.3\",\n    \"husky\": \"^8.0.1\",\n    \"lint-staged\": \"^13.0.3\",\n    \"prettier\": \"2.7.1\",\n    \"sass\": \"^1.53.0\",\n    \"sass-loader\": \"10.1.1\",\n    \"style-loader\": \"2.0.0\",\n    \"ts-jest\": \"^28.0.7\"\n  },\n  \"engines\": {\n    \"node\": \">=16\",\n    \"pnpm\": \">=7\"\n  },\n  \"resolutions\": {\n    \"react\": \"18.2.0\",\n    \"@types/react\": \"^18.2.0\"\n  },\n  \"dependencies\": {\n    \"@eslint/js\": \"^9.22.0\",\n    \"eslint\": \"^9.22.0\",\n    \"eslint-plugin-react-hooks\": \"^5.2.0\",\n    \"eslint-plugin-react-refresh\": \"^0.4.19\",\n    \"globals\": \"^16.0.0\",\n    \"typescript\": \"~5.7.2\",\n    \"typescript-eslint\": \"^8.26.1\"\n  }\n}\n"
  },
  {
    "path": "packages/build-script/.eslintignore",
    "content": "scripts\nbin/*\nlib\nexample\njest.*"
  },
  {
    "path": "packages/build-script/CHANGELOG.md",
    "content": "# Change Log\n\nAll notable changes to this project will be documented in this file.\nSee [Conventional Commits](https://conventionalcommits.org) for commit guidelines.\n\n## [0.10.4](https://github.com/ByteCrazy/chameleon/compare/v0.10.3...v0.10.4) (2026-01-17)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.10.3](https://github.com/ByteCrazy/chameleon/compare/v0.10.2...v0.10.3) (2026-01-17)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.10.2](https://github.com/ByteCrazy/chameleon/compare/v0.10.1...v0.10.2) (2026-01-17)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.10.1](https://github.com/ByteCrazy/chameleon/compare/v0.10.0...v0.10.1) (2026-01-11)\n\n### ✨ Features | 新功能\n\n* **build-script, demo-page, engine, render:** 🎸 update build script dts & optimize modal root selector ([024d5ab](https://github.com/ByteCrazy/chameleon/commit/024d5ab26d19fc5f50172c328638a551fb99c129))\n\n## [0.10.0](https://github.com/hlerenow/chameleon/compare/v0.9.3...v0.10.0) (2025-12-25)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* 优化构建配置，修复 ES 模块外部化问题 ([866f0ae](https://github.com/hlerenow/chameleon/commit/866f0ae39472625137d73d143625d88d873f6c00))\n\n### 📝 Documentation | 文档\n\n* **build-script:** 添加完整的 README 使用文档 ([fcd6172](https://github.com/hlerenow/chameleon/commit/fcd6172e381364f010986864593264b160380342))\n\n## [0.9.3](https://github.com/ByteCrazy/chameleon/compare/v0.9.2...v0.9.3) (2025-07-20)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.9.2](https://github.com/ByteCrazy/chameleon/compare/v0.9.1...v0.9.2) (2025-07-13)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.9.1](https://github.com/ByteCrazy/chameleon/compare/v0.9.0...v0.9.1) (2025-07-13)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.9.0](https://github.com/ByteCrazy/chameleon/compare/v0.8.6...v0.9.0) (2025-07-13)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.8.6](https://github.com/ByteCrazy/chameleon/compare/v0.8.5...v0.8.6) (2025-06-21)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.8.5](https://github.com/ByteCrazy/chameleon/compare/v0.8.4...v0.8.5) (2025-04-13)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.8.4](https://github.com/ByteCrazy/chameleon/compare/v0.8.3...v0.8.4) (2025-04-13)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.8.3](https://github.com/ByteCrazy/chameleon/compare/v0.8.2...v0.8.3) (2025-04-12)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.8.2](https://github.com/ByteCrazy/chameleon/compare/v0.8.1...v0.8.2) (2025-04-11)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.8.1](https://github.com/ByteCrazy/chameleon/compare/v0.8.0...v0.8.1) (2025-04-10)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.8.0](https://github.com/ByteCrazy/chameleon/compare/v0.7.0...v0.8.0) (2025-04-10)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.7.0](https://github.com/ByteCrazy/chameleon/compare/v0.6.0...v0.7.0) (2025-04-06)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.6.0](https://github.com/ByteCrazy/chameleon/compare/v0.5.2...v0.6.0) (2025-03-30)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.5.2](https://github.com/ByteCrazy/chameleon/compare/v0.5.1...v0.5.2) (2025-03-30)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.5.1](https://github.com/ByteCrazy/chameleon/compare/v0.5.0...v0.5.1) (2025-03-30)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.5.0](https://github.com/ByteCrazy/chameleon/compare/v0.4.0...v0.5.0) (2025-03-30)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.4.0](https://github.com/ByteCrazy/chameleon/compare/v0.3.21...v0.4.0) (2025-03-29)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.3.21](https://github.com/ByteCrazy/chameleon/compare/v0.3.20...v0.3.21) (2025-03-26)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.3.20](https://github.com/ByteCrazy/chameleon/compare/v0.3.19...v0.3.20) (2025-03-26)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.3.19](https://github.com/ByteCrazy/chameleon/compare/v0.3.18...v0.3.19) (2025-03-26)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.3.18](https://github.com/ByteCrazy/chameleon/compare/v0.3.17...v0.3.18) (2025-03-26)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.3.17](https://github.com/ByteCrazy/chameleon/compare/v0.3.16...v0.3.17) (2025-03-25)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.3.16](https://github.com/ByteCrazy/chameleon/compare/v0.3.15...v0.3.16) (2025-03-24)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.3.15](https://github.com/ByteCrazy/chameleon/compare/v0.3.14...v0.3.15) (2025-03-23)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.3.14](https://github.com/ByteCrazy/chameleon/compare/v0.3.13...v0.3.14) (2025-03-23)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.3.13](https://github.com/ByteCrazy/chameleon/compare/v0.3.12...v0.3.13) (2025-03-23)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.3.12](https://github.com/ByteCrazy/chameleon/compare/v0.3.11...v0.3.12) (2025-03-23)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.3.11](https://github.com/ByteCrazy/chameleon/compare/v0.3.10...v0.3.11) (2025-03-22)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.3.10](https://github.com/ByteCrazy/chameleon/compare/v0.3.9...v0.3.10) (2025-03-22)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.3.9](https://github.com/ByteCrazy/chameleon/compare/v0.3.8...v0.3.9) (2025-03-22)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.3.8](https://github.com/ByteCrazy/chameleon/compare/v0.3.7...v0.3.8) (2025-03-16)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.3.7](https://github.com/ByteCrazy/chameleon/compare/v0.3.6...v0.3.7) (2025-03-16)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.3.6](https://github.com/ByteCrazy/chameleon/compare/v0.3.5...v0.3.6) (2025-03-09)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.3.5](https://github.com/ByteCrazy/chameleon/compare/v0.3.4...v0.3.5) (2025-03-09)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.3.4](https://github.com/ByteCrazy/chameleon/compare/v0.3.3...v0.3.4) (2025-02-16)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.3.3](https://github.com/ByteCrazy/chameleon/compare/v0.3.2...v0.3.3) (2025-02-16)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.3.2](https://github.com/ByteCrazy/chameleon/compare/v0.3.1...v0.3.2) (2025-02-16)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.3.1](https://github.com/ByteCrazy/chameleon/compare/v0.3.0...v0.3.1) (2025-02-16)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.3.0](https://github.com/ByteCrazy/chameleon/compare/v0.2.4...v0.3.0) (2025-02-15)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.2.4](https://github.com/ByteCrazy/chameleon/compare/v0.2.3...v0.2.4) (2024-12-08)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.2.3](https://github.com/ByteCrazy/chameleon/compare/v0.2.2...v0.2.3) (2024-12-08)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.2.2](https://github.com/ByteCrazy/chameleon/compare/v0.2.1...v0.2.2) (2024-12-07)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.2.1](https://github.com/ByteCrazy/chameleon/compare/v0.2.0...v0.2.1) (2024-12-07)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, engine-website-app, material, model, render:** 🎸 fixed RGL component and optimize project struct ([99f0679](https://github.com/ByteCrazy/chameleon/commit/99f0679d93a2fd696034ebed8f1abcd9d9e601d4))\n\n## [0.2.0](https://github.com/ByteCrazy/chameleon/compare/v0.1.1...v0.2.0) (2024-12-07)\n\n### ✨ Features | 新功能\n\n* **build-script, demo-page, engine, engine-website-app, layout, material, model, render:** 🎸 upgrade vite to 6.0 ([bcac2b1](https://github.com/ByteCrazy/chameleon/commit/bcac2b15b83b41a7042ca37368c1b45302ad81d5))\n\n## [0.1.1](https://github.com/ByteCrazy/chameleon/compare/v0.1.0...v0.1.1) (2024-11-11)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.1.0](https://github.com/ByteCrazy/chameleon/compare/v0.0.46...v0.1.0) (2024-09-07)\n\n### ✨ Features | 新功能\n\n* **build-script, demo-page, engine, layout, render:** 🎸 add lang switch ([29da65e](https://github.com/ByteCrazy/chameleon/commit/29da65ee1aa09550d910ddfbbcb9d8b4db983373))\n\n### 👷 Continuous Integration | CI 配置\n\n* **build-script, docs-website, material, render:** 🎡 not build docs ([37aa10a](https://github.com/ByteCrazy/chameleon/commit/37aa10abf0324f3465a6921a9a014875e9500f8f))\n\n## [0.0.46](https://github.com/ByteCrazy/chameleon/compare/v0.0.45...v0.0.46) (2024-06-30)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.0.45](https://github.com/ByteCrazy/chameleon/compare/v0.0.44...v0.0.45) (2024-06-30)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.0.44](https://github.com/ByteCrazy/chameleon/compare/v0.0.43...v0.0.44) (2024-06-30)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.0.43](https://github.com/ByteCrazy/chameleon/compare/v0.0.42...v0.0.43) (2024-06-29)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, layout:** 🎸 remove scss dts generate ([9ba7af3](https://github.com/ByteCrazy/chameleon/commit/9ba7af30601804f94e90a0408745fd48de38d8b5))\n\n## [0.0.42](https://github.com/ByteCrazy/chameleon/compare/v0.0.41...v0.0.42) (2024-06-01)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.0.41](https://github.com/ByteCrazy/chameleon/compare/v0.0.40...v0.0.41) (2024-05-26)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, layout, material, model, render:** 🎸 optimize build script ([0a60cc4](https://github.com/ByteCrazy/chameleon/commit/0a60cc4f5e5635d9e544b5141eaed5b8433901a5))\n\n## [0.0.40](https://github.com/ByteCrazy/chameleon/compare/v0.0.39...v0.0.40) (2024-04-28)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.0.39](https://github.com/ByteCrazy/chameleon/compare/v0.0.38...v0.0.39) (2024-04-27)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **build-script, demo-page, engine, layout, material, model, render:** 🐛 fix flatObject collectVariable refeer pos ([1c4163a](https://github.com/ByteCrazy/chameleon/commit/1c4163aeeb89176a1ff5b50f76930889df7751fe))\n\n## [0.0.38](https://github.com/ByteCrazy/chameleon/compare/v0.0.37...v0.0.38) (2024-04-27)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.0.37](https://github.com/ByteCrazy/chameleon/compare/v0.0.36...v0.0.37) (2024-04-27)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.0.36](https://github.com/ByteCrazy/chameleon/compare/v0.0.35...v0.0.36) (2024-04-27)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.0.35](https://github.com/ByteCrazy/chameleon/compare/v0.0.34...v0.0.35) (2024-04-27)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.0.34](https://github.com/ByteCrazy/chameleon/compare/v0.0.33...v0.0.34) (2024-04-27)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.0.33](https://github.com/ByteCrazy/chameleon/compare/v0.0.32...v0.0.33) (2024-04-27)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.0.32](https://github.com/ByteCrazy/chameleon/compare/v0.0.31...v0.0.32) (2024-01-30)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.0.31](https://github.com/ByteCrazy/chameleon/compare/v0.0.30...v0.0.31) (2023-10-17)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.0.30](https://github.com/ByteCrazy/chameleon/compare/v0.0.29...v0.0.30) (2023-10-17)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.0.29](https://github.com/ByteCrazy/chameleon/compare/v0.0.28...v0.0.29) (2023-10-17)\n\n### ✨ Features | 新功能\n\n* **build-script, engine:** 🎸 optimize css editor ([66c0541](https://github.com/ByteCrazy/chameleon/commit/66c0541eaee12b2eb0ceb4fb3a7748e1bf69768d))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **build-script, engine:** 🐛 recocer build-script bin file ([5a2ca69](https://github.com/ByteCrazy/chameleon/commit/5a2ca69c4e5c48b3b0686478f6e5c40cd21c08ad))\n\n## [0.0.28](https://github.com/ByteCrazy/chameleon/compare/v0.0.27...v0.0.28) (2023-08-29)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.0.27](https://github.com/ByteCrazy/chameleon/compare/v0.0.26...v0.0.27) (2023-08-28)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.0.26](https://github.com/ByteCrazy/chameleon/compare/v0.0.25...v0.0.26) (2023-08-27)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.0.25](https://github.com/ByteCrazy/chameleon/compare/v0.0.24...v0.0.25) (2023-08-27)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.0.24](https://github.com/ByteCrazy/chameleon/compare/v0.0.23...v0.0.24) (2023-08-27)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.0.23](https://github.com/ByteCrazy/chameleon/compare/v0.0.22...v0.0.23) (2023-08-26)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.0.22](https://github.com/ByteCrazy/chameleon/compare/v0.0.21...v0.0.22) (2023-08-24)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.0.21](https://github.com/ByteCrazy/chameleon/compare/v0.0.20...v0.0.21) (2023-08-19)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.0.20](https://github.com/ByteCrazy/chameleon/compare/v0.0.19...v0.0.20) (2023-08-19)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.0.19](https://github.com/ByteCrazy/chameleon/compare/v0.0.18...v0.0.19) (2023-08-17)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.0.18](https://github.com/ByteCrazy/chameleon/compare/v0.0.17...v0.0.18) (2023-08-14)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.0.17](https://github.com/ByteCrazy/chameleon/compare/v0.0.16...v0.0.17) (2023-08-06)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.0.16](https://github.com/ByteCrazy/chameleon/compare/v0.0.15...v0.0.16) (2023-08-06)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.0.15](https://github.com/ByteCrazy/chameleon/compare/v0.0.14...v0.0.15) (2023-07-01)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.0.14](https://github.com/ByteCrazy/chameleon/compare/v0.0.13...v0.0.14) (2023-05-10)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.0.13](https://github.com/ByteCrazy/chameleon/compare/v0.0.12...v0.0.13) (2023-05-03)\n\n### ✨ Features | 新功能\n\n* **build-script, build-script-example, demo-page, engine, layout, material, model, render:** 🎸 config code style config ([2325504](https://github.com/ByteCrazy/chameleon/commit/23255048fa4a3d4fc8f5cfa1312db5abd6cac70d))\n* **build-script, demo-page, engine, layout, model, render:** 🎸 update meterial schema ([14f4e5c](https://github.com/ByteCrazy/chameleon/commit/14f4e5caf0a4f6eca131e139d0c34ea21969dc0b))\n\n## [0.0.12](https://github.com/ByteCrazy/chameleon/compare/v0.0.11...v0.0.12) (2023-04-23)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **build-script, build-script-example, engine, layout, material, model, render:** 🐛 fix can not find vite/client.d.ts problem ([8a53540](https://github.com/ByteCrazy/chameleon/commit/8a53540f75737707f69f93d2f701203dbf88084a))\n\n## [0.0.11](https://github.com/ByteCrazy/chameleon/compare/v0.0.10...v0.0.11) (2023-04-21)\n\n### ✨ Features | 新功能\n\n* **build-script, build-script-example, demo-page, engine, layout, material, model, render:** 🎸 optimize build-script output file format ([eda44d2](https://github.com/ByteCrazy/chameleon/commit/eda44d255bd3c5af19013bdd61342e1f0817413a))\n* **build-script, docs-website, engine:** 🎸 add material develop doc ([0aacca0](https://github.com/ByteCrazy/chameleon/commit/0aacca0f726bc13606b814f8890b4e8ff8982142))\n\n## [0.0.10](https://github.com/ByteCrazy/chameleon/compare/v0.0.9...v0.0.10) (2023-04-17)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.0.9](https://github.com/ByteCrazy/chameleon/compare/v0.0.8...v0.0.9) (2023-04-17)\n\n### ✨ Features | 新功能\n\n* **build-script:** 🎸 build.config supprot es module ([275c97d](https://github.com/ByteCrazy/chameleon/commit/275c97d861b6394fbdd13b92fd1049c208f72b39))\n\n## [0.0.8](https://github.com/ByteCrazy/chameleon/compare/v0.0.7...v0.0.8) (2023-04-16)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, layout, model, render:** 🎸 optimize pack package ([78d99c5](https://github.com/ByteCrazy/chameleon/commit/78d99c507ad65ca39101f0239467737d2b445c87))\n\n### 📝 Documentation | 文档\n\n* **build-script, docs-website:** ✏️ change quickStart md ([f43945f](https://github.com/ByteCrazy/chameleon/commit/f43945fdd7eaf03b4ad3099980d7f3fb0a949b4c))\n\n## [0.0.7](https://github.com/ByteCrazy/chameleon/compare/v0.0.6...v0.0.7) (2023-03-29)\n\n### ✨ Features | 新功能\n\n* **build-script, build-script-example, demo-page, engine, layout, material, model, render:** 🎸 external monaco eitor and remove style panel ([494d898](https://github.com/ByteCrazy/chameleon/commit/494d898fd75dabe84d867ff45e84ae63f9b59c5e))\n\n## [0.0.6](https://github.com/ByteCrazy/chameleon/compare/v0.0.5...v0.0.6) (2023-03-28)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.0.5](https://github.com/ByteCrazy/chameleon/compare/v0.0.4...v0.0.5) (2023-03-28)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, layout, material, model, render:** 🎸 complete package info ([86b095d](https://github.com/ByteCrazy/chameleon/commit/86b095da6c955946300591b1775f59c1a141c065))\n\n## [0.0.4](https://github.com/ByteCrazy/chameleon/compare/v0.0.3...v0.0.4) (2023-03-27)\n\n**Note:** Version bump only for package @chamn/build-script\n\n## [0.0.2](https://github.com/ByteCrazy/chameleon/compare/v0.0.1...v0.0.2) (2023-03-27)\n\n**Note:** Version bump only for package @chamn/build-script\n"
  },
  {
    "path": "packages/build-script/README.md",
    "content": "# @chamn/build-script\n\n基于 Vite 的构建工具，用于构建库（Library）和应用（Application）项目。支持多种输出格式、TypeScript 类型定义生成、开发服务器等功能。\n\n## 特性\n\n- 🚀 基于 Vite，构建速度快\n- 📦 支持多种输出格式：`cjs`、`es`、`umd`、`iife`\n- 📝 自动生成 TypeScript 类型定义（.d.ts）\n- 🔧 灵活的依赖外部化配置\n- 🎯 支持按格式配置不同的 external 规则\n- 🔍 支持构建分析（bundle analyzer）\n- 👀 支持 watch 模式\n- 🛠️ 支持开发服务器\n- ⚙️ 支持自定义 Vite 配置\n\n## 安装\n\n```bash\nnpm install @chamn/build-script --save-dev\n```\n\n## 快速开始\n\n### 1. 创建配置文件\n\n在项目根目录创建 `build.config.js` 或 `build.config.ts` 文件：\n\n```javascript\n// build.config.js\nexport default {\n  libMode: true, // 是否为库模式，false 为应用模式\n  entry: './src/index.tsx', // 入口文件\n  libName: 'MyLibrary', // 库名称（UMD 格式需要）\n  fileName: 'index', // 输出文件名\n  formats: ['cjs', 'es', 'umd'], // 输出格式\n};\n```\n\n### 2. 配置 package.json\n\n```json\n{\n  \"scripts\": {\n    \"dev\": \"build-script\",\n    \"build\": \"build-script --build\",\n    \"build:watch\": \"build-script --build --watch\",\n    \"build:analyze\": \"build-script --build --analyze\"\n  }\n}\n```\n\n### 3. 运行命令\n\n```bash\n# 开发模式\nnpm run dev\n\n# 构建\nnpm run build\n\n# 构建（watch 模式）\nnpm run build:watch\n\n# 构建（分析模式）\nnpm run build:analyze\n```\n\n## 配置选项\n\n### BuildScriptConfig\n\n```typescript\ntype BuildScriptConfig = {\n  // 是否为库模式，false 为应用模式（默认：true）\n  libMode?: boolean;\n\n  // 入口文件路径（必需）\n  entry: string;\n\n  // 库名称（UMD 格式需要）\n  libName?: string;\n\n  // 输出文件名（默认：'index'）\n  fileName?: string;\n\n  // 样式文件名（默认：'style'）\n  cssFileName?: string;\n\n  // 通用的 external 配置，对所有格式生效\n  external?: ExternalOption;\n\n  // 按格式配置不同的 external 规则，优先级高于 external\n  externalByFormat?: {\n    es?: ExternalOption;\n    cjs?: ExternalOption;\n    umd?: ExternalOption;\n    iife?: ExternalOption;\n  };\n\n  // 自定义需要排除的别名前缀列表，这些路径不会被外部化\n  // 默认值：['@/', '~/', '#/']\n  externalAlias?: string[];\n\n  // UMD 格式的全局变量映射\n  global?: Record<string, string>;\n\n  // 输出格式（默认：build 模式为 ['cjs', 'es', 'umd']，dev 模式为 ['cjs', 'es']）\n  formats?: LibraryOptions['formats'];\n\n  // 自定义 Vite 配置\n  vite?: UserConfig;\n};\n```\n\n### ExternalOption\n\n```typescript\ntype ExternalOption = (string | RegExp)[] | ((id: string, importer?: string, isResolved?: boolean) => boolean);\n```\n\n## 使用示例\n\n### 库模式（Library Mode）\n\n```javascript\n// build.config.js\nexport default {\n  libMode: true,\n  entry: './src/index.tsx',\n  libName: 'MyLibrary',\n  fileName: 'index',\n  formats: ['cjs', 'es', 'umd'],\n  global: {\n    react: 'React',\n    'react-dom': 'ReactDOM',\n  },\n  // 模块格式（es, cjs）使用严格的 external\n  externalByFormat: {\n    es: (id) => {\n      if (id.startsWith('.') || id.startsWith('/')) return false;\n      if (id.startsWith('@/')) return false;\n      return true; // 外部化所有 node_modules 依赖\n    },\n    cjs: (id) => {\n      if (id.startsWith('.') || id.startsWith('/')) return false;\n      if (id.startsWith('@/')) return false;\n      return true;\n    },\n    // 浏览器格式（umd）只外部化 react 和 react-dom\n    umd: ['react', 'react-dom'],\n  },\n};\n```\n\n### 应用模式（Application Mode）\n\n```javascript\n// build.config.js\nexport default {\n  libMode: false,\n  entry: './src/index.tsx',\n  vite: {\n    base: '/my-app/',\n    build: {\n      outDir: './dist',\n      copyPublicDir: true,\n    },\n    plugins: [\n      // 自定义插件\n    ],\n  },\n};\n```\n\n### 自定义别名配置\n\n```javascript\n// build.config.js\nexport default {\n  entry: './src/index.tsx',\n  libName: 'MyLibrary',\n  // 自定义别名前缀，这些路径不会被外部化\n  externalAlias: ['@/', '~/', '#/', '$lib/'],\n};\n```\n\n### 自定义 Vite 配置\n\n```javascript\n// build.config.js\nexport default {\n  entry: './src/index.tsx',\n  libName: 'MyLibrary',\n  vite: {\n    define: {\n      'process.env.NODE_ENV': JSON.stringify('production'),\n      __VERSION__: JSON.stringify('1.0.0'),\n    },\n    plugins: [\n      // 自定义 Vite 插件\n    ],\n    build: {\n      minify: 'terser',\n      terserOptions: {\n        compress: {\n          drop_console: true,\n        },\n      },\n    },\n  },\n};\n```\n\n## CLI 参数\n\n```bash\nbuild-script [options]\n```\n\n### 选项\n\n- `--build`: 构建模式（默认：开发模式）\n- `--watch`: Watch 模式，文件变化时自动重新构建\n- `--analyze`: 构建分析模式，生成并打开 bundle 分析报告\n- `--generateDTS`: 生成 TypeScript 类型定义（默认：true，设置为 false 可禁用）\n- `--sourcemap`: 生成 sourcemap（默认：true）\n\n### 示例\n\n```bash\n# 开发模式\nbuild-script\n\n# 构建模式\nbuild-script --build\n\n# 构建 + Watch 模式\nbuild-script --build --watch\n\n# 构建 + 分析\nbuild-script --build --analyze\n\n# 构建 + 不生成类型定义\nbuild-script --build --generateDTS=false\n\n# 构建 + 不生成 sourcemap\nbuild-script --build --sourcemap=false\n```\n\n## 输出格式说明\n\n### CommonJS (cjs)\n\n适用于 Node.js 环境，使用 `require()` 导入。\n\n```javascript\nconst MyLibrary = require('my-library');\n```\n\n### ES Module (es)\n\n适用于现代打包工具和浏览器，使用 `import` 导入。\n\n```javascript\nimport MyLibrary from 'my-library';\n```\n\n### UMD (umd)\n\n通用模块定义，同时支持 CommonJS、AMD 和全局变量。\n\n```html\n<script src=\"https://unpkg.com/react@18/umd/react.production.min.js\"></script>\n<script src=\"https://unpkg.com/react-dom@18/umd/react-dom.production.min.js\"></script>\n<script src=\"./dist/index.umd.js\"></script>\n<script>\n  // 全局变量方式使用\n  const app = MyLibrary.createApp();\n</script>\n```\n\n### IIFE (iife)\n\n立即执行函数表达式，适用于直接在浏览器中使用。\n\n## 依赖外部化（External）\n\n### 默认行为\n\n- **模块格式（es, cjs）**：默认外部化所有 `node_modules` 中的依赖，但保留项目内部文件（相对路径、别名路径）\n- **浏览器格式（umd, iife）**：默认只外部化 `react` 和 `react-dom`\n\n### 自定义 External 配置\n\n#### 方式一：通用配置\n\n```javascript\nexport default {\n  entry: './src/index.tsx',\n  // 所有格式都使用这个配置\n  external: ['react', 'react-dom', 'lodash'],\n};\n```\n\n#### 方式二：按格式配置（推荐）\n\n```javascript\nexport default {\n  entry: './src/index.tsx',\n  externalByFormat: {\n    // 模块格式：外部化所有依赖\n    es: (id) => {\n      if (id.startsWith('.') || id.startsWith('/')) return false;\n      if (id.startsWith('@/')) return false;\n      return true;\n    },\n    cjs: (id) => {\n      if (id.startsWith('.') || id.startsWith('/')) return false;\n      if (id.startsWith('@/')) return false;\n      return true;\n    },\n    // 浏览器格式：只外部化 react 和 react-dom\n    umd: ['react', 'react-dom'],\n  },\n};\n```\n\n#### 方式三：函数式配置\n\n```javascript\nexport default {\n  entry: './src/index.tsx',\n  external: (id) => {\n    // 不外部化项目内部文件\n    if (id.startsWith('.') || id.startsWith('/')) return false;\n    if (id.startsWith('@/')) return false;\n\n    // 外部化特定依赖\n    if (id === 'react' || id === 'react-dom') return true;\n\n    // 其他依赖打包进库\n    return false;\n  },\n};\n```\n\n## TypeScript 支持\n\n### 类型定义生成\n\n默认会自动生成 TypeScript 类型定义文件（.d.ts），输出到 `dist` 目录。\n\n如需禁用：\n\n```bash\nbuild-script --build --generateDTS=false\n```\n\n或在配置中：\n\n```javascript\n// 注意：CLI 参数优先级更高\nexport default {\n  entry: './src/index.tsx',\n  // ...\n};\n```\n\n### 类型导出\n\n在 `package.json` 中配置：\n\n```json\n{\n  \"main\": \"./dist/index.cjs.js\",\n  \"module\": \"./dist/index.es.js\",\n  \"types\": \"./dist/index.d.ts\",\n  \"exports\": {\n    \".\": {\n      \"import\": \"./dist/index.es.js\",\n      \"require\": \"./dist/index.cjs.js\",\n      \"types\": \"./dist/index.d.ts\"\n    }\n  }\n}\n```\n\n## 开发服务器\n\n开发模式下会自动启动 Vite 开发服务器：\n\n```bash\nbuild-script\n# 或\nbuild-script --dev\n```\n\n默认端口：`3000`\n\n自定义端口：\n\n```javascript\n// build.config.js\nexport default {\n  entry: './src/index.tsx',\n  vite: {\n    server: {\n      port: 8080,\n    },\n  },\n};\n```\n\n## 构建分析\n\n使用 `--analyze` 参数可以生成并打开 bundle 分析报告：\n\n```bash\nbuild-script --build --analyze\n```\n\n这会生成一个可视化的 bundle 分析报告，帮助你了解打包后的文件大小和依赖关系。\n\n## 常见问题\n\n### Q: 如何排除某些依赖不被打包？\n\nA: 使用 `external` 或 `externalByFormat` 配置：\n\n```javascript\nexport default {\n  external: ['lodash', 'moment'],\n};\n```\n\n### Q: 如何打包所有依赖？\n\nA: 设置 `external` 为空数组：\n\n```javascript\nexport default {\n  external: [],\n};\n```\n\n### Q: 如何自定义输出目录？\n\nA: 在 `vite` 配置中设置：\n\n```javascript\nexport default {\n  vite: {\n    build: {\n      outDir: './output',\n    },\n  },\n};\n```\n\n### Q: 如何禁用类型定义生成？\n\nA: 使用 CLI 参数：\n\n```bash\nbuild-script --build --generateDTS=false\n```\n\n### Q: 如何配置别名路径？\n\nA: 在 `vite` 配置中设置：\n\n```javascript\nexport default {\n  vite: {\n    resolve: {\n      alias: {\n        '@': path.resolve(__dirname, './src'),\n      },\n    },\n  },\n};\n```\n\n## 许可证\n\nISC\n\n## 作者\n\nlevin\n"
  },
  {
    "path": "packages/build-script/bin/run.js",
    "content": "#!/usr/bin/env node\n\nasync function start() {\n  const bScript = await import('../lib/index.mjs');\n  bScript.run();\n}\n\nstart();\n"
  },
  {
    "path": "packages/build-script/client.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "packages/build-script/jest.config.js",
    "content": "/*\n * For a detailed explanation regarding each configuration property, visit:\n * https://jestjs.io/docs/configuration\n */\n\nmodule.exports = {\n  // All imported modules in your tests should be mocked automatically\n  // automock: false,\n\n  // Stop running tests after `n` failures\n  // bail: 0,\n\n  // The directory where Jest should store its cached dependency information\n  // cacheDirectory: \"/private/var/folders/l9/th_r5d_12wxdj16859_mctjw0000gn/T/jest_dx\",\n\n  // Automatically clear mock calls, instances, contexts and results before every test\n  clearMocks: true,\n  extensionsToTreatAsEsm: ['.ts'],\n  // Indicates whether the coverage information should be collected while executing the test\n  collectCoverage: false,\n\n  // An array of glob patterns indicating a set of files for which coverage information should be collected\n  collectCoverageFrom: ['src/**/*.{ts,tsx}', '!**/node_modules/**', '!**/vendor/**'],\n  testMatch: ['<rootDir>/tests/**/*.test.ts'],\n\n  // The directory where Jest should output its coverage files\n  coverageDirectory: 'coverage',\n\n  // An array of regexp pattern strings used to skip coverage collection\n  // coveragePathIgnorePatterns: [\n  //   \"/node_modules/\"\n  // ],\n\n  // Indicates which provider should be used to instrument code for coverage\n  // coverageProvider: \"babel\",\n\n  // A list of reporter names that Jest uses when writing coverage reports\n  // coverageReporters: [\n  //   \"json\",\n  //   \"text\",\n  //   \"lcov\",\n  //   \"clover\"\n  // ],\n\n  // An object that configures minimum threshold enforcement for coverage results\n  // coverageThreshold: undefined,\n\n  // A path to a custom dependency extractor\n  // dependencyExtractor: undefined,\n\n  // Make calling deprecated APIs throw helpful error messages\n  // errorOnDeprecated: false,\n\n  // The default configuration for fake timers\n  // fakeTimers: {\n  //   \"enableGlobally\": false\n  // },\n\n  // Force coverage collection from ignored files using an array of glob patterns\n  // forceCoverageMatch: [],\n\n  // A path to a module which exports an async function that is triggered once before all test suites\n  // globalSetup: undefined,\n\n  // A path to a module which exports an async function that is triggered once after all test suites\n  // globalTeardown: undefined,\n\n  // A set of global variables that need to be available in all test environments\n  // globals: {},\n\n  // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.\n  // maxWorkers: \"50%\",\n\n  // An array of directory names to be searched recursively up from the requiring module's location\n  // moduleDirectories: [\n  //   \"node_modules\"\n  // ],\n\n  // An array of file extensions your modules use\n  // moduleFileExtensions: [\n  //   \"js\",\n  //   \"mjs\",\n  //   \"cjs\",\n  //   \"jsx\",\n  //   \"ts\",\n  //   \"tsx\",\n  //   \"json\",\n  //   \"node\"\n  // ],\n\n  // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module\n  // moduleNameMapper: {},\n\n  // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader\n  // modulePathIgnorePatterns: [],\n\n  // Activates notifications for test results\n  // notify: false,\n\n  // An enum that specifies notification mode. Requires { notify: true }\n  // notifyMode: \"failure-change\",\n\n  // A preset that is used as a base for Jest's configuration\n  preset: 'ts-jest',\n\n  // Run tests from one or more projects\n  // projects: undefined,\n\n  // Use this configuration option to add custom reporters to Jest\n  // reporters: undefined,\n\n  // Automatically reset mock state before every test\n  // resetMocks: false,\n\n  // Reset the module registry before running each individual test\n  // resetModules: false,\n\n  // A path to a custom resolver\n  // resolver: undefined,\n\n  // Automatically restore mock state and implementation before every test\n  // restoreMocks: false,\n\n  // The root directory that Jest should scan for tests and modules within\n  // rootDir: undefined,\n\n  // A list of paths to directories that Jest should use to search for files in\n  // roots: [\n  //   \"<rootDir>\"\n  // ],\n\n  // Allows you to use a custom runner instead of Jest's default test runner\n  // runner: \"jest-runner\",\n\n  // The paths to modules that run some code to configure or set up the testing environment before each test\n  // setupFiles: [],\n\n  // A list of paths to modules that run some code to configure or set up the testing framework before each test\n  // setupFilesAfterEnv: [],\n\n  // The number of seconds after which a test is considered as slow and reported as such in the results.\n  // slowTestThreshold: 5,\n\n  // A list of paths to snapshot serializer modules Jest should use for snapshot testing\n  // snapshotSerializers: [],\n\n  // The test environment that will be used for testing\n  testEnvironment: 'node',\n\n  // Options that will be passed to the testEnvironment\n  // testEnvironmentOptions: {},\n\n  // Adds a location field to test results\n  // testLocationInResults: false,\n\n  // The glob patterns Jest uses to detect test files\n  // testMatch: [\n  //   \"**/__tests__/**/*.[jt]s?(x)\",\n  //   \"**/?(*.)+(spec|test).[tj]s?(x)\"\n  // ],\n\n  // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped\n  // testPathIgnorePatterns: [\n  //   \"/node_modules/\"\n  // ],\n\n  // The regexp pattern or array of patterns that Jest uses to detect test files\n  // testRegex: [],\n\n  // This option allows the use of a custom results processor\n  // testResultsProcessor: undefined,\n\n  // This option allows use of a custom test runner\n  // testRunner: \"jest-circus/runner\",\n\n  // A map from regular expressions to paths to transformers\n  // transform: undefined,\n\n  // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation\n  transformIgnorePatterns: ['/node_modules/', '\\\\.pnp\\\\.[^\\\\/]+$'],\n\n  // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them\n  // unmockedModulePathPatterns: undefined,\n\n  // Indicates whether each individual test should be reported during the run\n  // verbose: undefined,\n\n  // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode\n  // watchPathIgnorePatterns: [],\n\n  // Whether to use watchman for file crawling\n  // watchman: true,\n};\n"
  },
  {
    "path": "packages/build-script/package.json",
    "content": "{\n  \"name\": \"@chamn/build-script\",\n  \"version\": \"0.10.4\",\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"description\": \"\",\n  \"main\": \"./lib/index.mjs\",\n  \"bin\": {\n    \"build-script\": \"./bin/run.js\"\n  },\n  \"exports\": {\n    \".\": {\n      \"types\": \"./lib/index.d.ts\",\n      \"module-sync\": \"./lib/index.mjs\",\n      \"import\": \"./lib/index.mjs\",\n      \"require\": \"./lib/index.cjs\"\n    },\n    \"./lib/index.d.ts\": \"./lib/index.d.ts\",\n    \"./client.d.ts\": \"./client.d.ts\",\n    \"./package.json\": \"./package.json\"\n  },\n  \"scripts\": {\n    \"start\": \"npm run clean && node scripts/build.cjs\",\n    \"build:tsc\": \"tsc ./src/index.ts --declaration --emitDeclarationOnly --esModuleInterop true --skipLibCheck true --outDir ./lib \",\n    \"build\": \"npm run clean && npm run build:tsc && node scripts/build.cjs\",\n    \"build:w\": \"npm run clean && nodemon --watch ./src -e ts,tsx,js,json scripts/build.cjs\",\n    \"clean\": \"rimraf es lib dist types generated demo coverage output\",\n    \"test\": \"echo 'skip test'\",\n    \"test:cov\": \"jest --coverage\",\n    \"test:update-snapshots\": \"cross-env jest -u\"\n  },\n  \"author\": \"levin\",\n  \"license\": \"ISC\",\n  \"devDependencies\": {\n    \"@types/fs-extra\": \"^9.0.13\",\n    \"@types/jest\": \"^28.1.6\",\n    \"@types/node\": \"~22.10.0\",\n    \"@types/yargs-parser\": \"^21.0.0\",\n    \"esbuild\": \"^0.27.2\",\n    \"lodash\": \"^4.17.21\",\n    \"nodemon\": \"^3.1.9\"\n  },\n  \"dependencies\": {\n    \"@vitejs/plugin-react\": \"^4.3.4\",\n    \"concurrently\": \"^7.3.0\",\n    \"fs-extra\": \"^10.1.0\",\n    \"jest\": \"^28.1.3\",\n    \"jest-environment-jsdom\": \"^28.1.3\",\n    \"merge\": \"^2.1.1\",\n    \"prettier\": \"2.7.1\",\n    \"rimraf\": \"^4.4.1\",\n    \"rollup-plugin-visualizer\": \"^5.12.0\",\n    \"ts-jest\": \"^28.0.7\",\n    \"ts-node\": \"^10.9.1\",\n    \"typescript\": \"~5.7.2\",\n    \"vite\": \"^6.2.2\",\n    \"vite-plugin-dts\": \"^4.3.0\",\n    \"vite-plugin-eslint2\": \"^5.0.3\",\n    \"yargs-parser\": \"^21.0.1\"\n  },\n  \"config\": {},\n  \"gitHead\": \"dc3e55fdeb903a8012f6ebd3ebc018ed61ad89db\"\n}"
  },
  {
    "path": "packages/build-script/scripts/build.cjs",
    "content": "/* eslint-disable no-undef */\n/* eslint-disable @typescript-eslint/no-require-imports */\n\nconst _ = require('lodash');\nconst esbuild = require('esbuild');\nconst concurrently = require('concurrently');\nconst argv = require('yargs-parser')(process.argv.slice(2));\nconst packageJson = require('../package.json');\n\nif (!argv.format) {\n  buildAll(process.argv.slice(2).join(' '));\n} else {\n  buildFormat(argv.format, argv.out || 'dist');\n}\n\nfunction buildAll(options) {\n  concurrently(\n    [\n      { name: 'build:cjs', command: `node scripts/build.cjs --format=cjs --out=lib ${options}` },\n      { name: 'build:esm', command: `node scripts/build.cjs --format=esm --out=lib ${options}` },\n    ],\n    {\n      prefix: 'name',\n      killOthers: ['failure'],\n      restartTries: 0,\n    }\n  ).result.then(\n    () => {\n      console.log('all done.');\n    },\n    () => {\n      process.exit(1);\n    }\n  );\n}\n\nasync function buildFormat(format, outDir) {\n  const extMap = {\n    esm: 'mjs',\n    cjs: 'cjs',\n  };\n  try {\n    console.log('building %s...', format);\n    let config = {\n      entryPoints: ['src/index.ts'],\n      outfile: `${outDir}/index.${extMap[format]}`,\n      bundle: true,\n      platform: 'node',\n      target: ['node10'],\n      format,\n      sourcemap: true,\n      sourcesContent: true,\n      define: {},\n      treeShaking: true,\n      external: _.keys(packageJson.dependencies),\n      minify: false,\n      legalComments: 'external',\n    };\n\n    if (argv.watch) {\n      config.watch = {\n        onRebuild(error, result) {\n          if (error) console.error('watch build failed:', error);\n          else console.log('watch build succeeded:', result);\n        },\n      };\n    }\n    const startTime = Date.now();\n    const result = await esbuild.build(config);\n    if (result.errors.length > 0) {\n      throw result.errors;\n    }\n\n    if (result.warnings.length > 0) {\n      result.warnings.forEach((warnings) => {\n        console.warn(warnings);\n      });\n    }\n\n    console.log('built %s in %ds', format, ((Date.now() - startTime) / 1000).toFixed(2));\n  } catch (e) {\n    console.error(e);\n    process.exit(1);\n  }\n}\n"
  },
  {
    "path": "packages/build-script/src/config/base.ts",
    "content": "import path from 'path';\nimport fs from 'fs-extra';\nimport { loadConfigFromFile, type LibraryOptions, type UserConfig } from 'vite';\nimport argv from 'yargs-parser';\n\nconst cliArgs: {\n  dev: boolean;\n  build: boolean;\n  watch: boolean;\n  analyze: boolean;\n  generateDTS: boolean;\n  sourcemap: boolean;\n} = argv(process.argv.slice(2)) as any;\n\nexport const CLI_ARGS_OBJ = cliArgs;\n\nexport const PROJECT_ROOT = path.resolve(process.cwd());\n\nlet customConfigPath = `${PROJECT_ROOT}/build.config.js`;\n\nif (!fs.pathExistsSync(customConfigPath)) {\n  customConfigPath = `${PROJECT_ROOT}/build.config.ts`;\n}\n\nexport type ExternalOption = (string | RegExp)[] | ((id: string, importer?: string, isResolved?: boolean) => boolean);\n\nexport type BuildScriptConfig = {\n  libMode?: boolean;\n  entry: string;\n  libName?: string;\n  fileName?: string;\n  /* 样式文件名 */\n  cssFileName?: string;\n  /* 通用的 external 配置，对所有格式生效 */\n  external?: ExternalOption;\n  /* 按格式配置不同的 external 规则，优先级高于 external */\n  externalByFormat?: {\n    es?: ExternalOption;\n    cjs?: ExternalOption;\n    umd?: ExternalOption;\n    iife?: ExternalOption;\n  };\n  /* 自定义需要排除的别名前缀列表，这些路径不会被外部化\n   * 默认值：['@/', '~/', '#/']\n   * 示例：['@/', '~/', '$lib/', '@src/']\n   */\n  externalAlias?: string[];\n  global?: Record<string, string>;\n  formats?: LibraryOptions['formats'];\n  vite?: UserConfig;\n};\n\nexport let CUSTOM_CONFIG: BuildScriptConfig = null as any;\nexport const isBuild = !!process.env.VITE_TEST_BUILD;\n\nexport const getCustomConfig = async () => {\n  if (CUSTOM_CONFIG) {\n    return CUSTOM_CONFIG;\n  }\n\n  if (fs.pathExistsSync(customConfigPath)) {\n    const customConfig = await loadConfigFromFile({} as any, customConfigPath, PROJECT_ROOT);\n    CUSTOM_CONFIG = customConfig?.config as BuildScriptConfig;\n    return customConfig?.config as BuildScriptConfig;\n  }\n};\n"
  },
  {
    "path": "packages/build-script/src/config/vite.build.ts",
    "content": "import { CLI_ARGS_OBJ, PROJECT_ROOT, getCustomConfig } from './base';\nimport { getCommonConfig } from './vite.common';\nimport { visualizer } from 'rollup-plugin-visualizer';\nimport path from 'path';\n\nimport dts from 'vite-plugin-dts';\nimport { mergeConfig, type LibraryFormats } from 'vite';\n\nconst generateDTS = CLI_ARGS_OBJ.generateDTS;\n\n// https://vitejs.dev/config/\nexport const buildConfig = async function (specifiedFormats?: LibraryFormats[], keepOutDir: boolean = false) {\n  const CUSTOM_CONFIG = await getCustomConfig();\n  const commonConfig = await getCommonConfig(specifiedFormats);\n\n  const plugins: any[] = [];\n\n  // 只在第一次构建时生成 DTS（非 umd 单独构建时）\n  if (generateDTS !== false && !keepOutDir) {\n    plugins.push(\n      dts({\n        entryRoot: path.resolve(PROJECT_ROOT, './src'),\n        compilerOptions: {\n          skipDefaultLibCheck: false,\n        },\n      })\n    );\n  }\n\n  if (CLI_ARGS_OBJ.analyze) {\n    plugins.push(\n      visualizer({\n        open: true,\n      })\n    );\n  }\n\n  const config = mergeConfig(commonConfig, {\n    mode: 'production',\n    configFile: false,\n    build: {\n      watch: CLI_ARGS_OBJ.watch ?? false,\n      emptyOutDir: !keepOutDir, // 如果 keepOutDir 为 true，不清空输出目录\n    },\n    plugins: plugins,\n  });\n\n  // 合并自定义 vite 配置\n  // 注意：如果自定义配置中设置了 build.rollupOptions，它会覆盖基础配置\n  // 因此建议在自定义配置中明确设置完整的 rollupOptions\n  const finalConfig = mergeConfig(config, CUSTOM_CONFIG?.vite || {});\n  return finalConfig;\n};\n"
  },
  {
    "path": "packages/build-script/src/config/vite.common.ts",
    "content": "import path from 'path';\nimport react from '@vitejs/plugin-react';\nimport { CLI_ARGS_OBJ, PROJECT_ROOT, getCustomConfig } from './base';\nimport { defineConfig, type LibraryFormats } from 'vite';\nimport eslint from 'vite-plugin-eslint2';\n\n// https://vitejs.dev/config/\nexport const getCommonConfig = async (specifiedFormats?: LibraryFormats[]) => {\n  const CUSTOM_CONFIG = await getCustomConfig();\n\n  if (!CUSTOM_CONFIG?.entry) {\n    throw new Error('entry not find');\n  }\n  // 生产模式下默认包含 umd，否则只有 cjs 和 es\n  const defaultFormats: LibraryFormats[] = CLI_ARGS_OBJ.build ? ['cjs', 'es', 'umd'] : ['cjs', 'es'];\n  const formats = specifiedFormats || CUSTOM_CONFIG.formats || defaultFormats;\n\n  // 根据当前构建的格式来决定 external 的逻辑\n  const getExternal = () => {\n    // 开发模式下不使用 external，打包所有依赖\n    if (CLI_ARGS_OBJ.dev || !CLI_ARGS_OBJ.build) {\n      return [];\n    }\n\n    // 优先使用按格式配置的 external\n    if (CUSTOM_CONFIG.externalByFormat) {\n      // 如果是单个格式，直接使用对应的配置\n      if (formats.length === 1) {\n        const format = formats[0] as keyof typeof CUSTOM_CONFIG.externalByFormat;\n        const externalForFormat = CUSTOM_CONFIG.externalByFormat[format];\n        if (externalForFormat !== undefined) {\n          return externalForFormat;\n        }\n      }\n\n      // 如果是多个格式，检查是否都是同一组\n      const isAllModuleFormats = formats.every((f) => f === 'es' || f === 'cjs');\n      const isAllBrowserFormats = formats.every((f) => f === 'umd' || f === 'iife');\n\n      if (isAllModuleFormats) {\n        // 如果都是模块格式，优先使用 es 的配置，然后是 cjs\n        const moduleExternal = CUSTOM_CONFIG.externalByFormat.es || CUSTOM_CONFIG.externalByFormat.cjs;\n        if (moduleExternal) {\n          return moduleExternal;\n        }\n      }\n\n      if (isAllBrowserFormats) {\n        // 如果都是浏览器格式，优先使用 umd 的配置，然后是 iife\n        const browserExternal = CUSTOM_CONFIG.externalByFormat.umd || CUSTOM_CONFIG.externalByFormat.iife;\n        if (browserExternal) {\n          return browserExternal;\n        }\n      }\n    }\n\n    // 如果用户自定义了通用的 external，使用自定义配置\n    if (CUSTOM_CONFIG.external) {\n      return CUSTOM_CONFIG.external;\n    }\n\n    // 默认规则：判断当前格式组\n    const isAllBrowserFormats = formats.every((f) => f === 'umd' || f === 'iife');\n\n    if (isAllBrowserFormats) {\n      // 浏览器格式（umd, iife）：只过滤 react 和 react-dom\n      return ['react', 'react-dom'];\n    }\n\n    // 模块格式（es, cjs）或混合格式：使用更严格的过滤规则\n    // 排除项目内部文件：相对路径、绝对路径、别名路径\n    return (id: string) => {\n      // 相对路径或绝对路径：项目内部文件\n      if (id.startsWith('.') || id.startsWith('/')) {\n        return false;\n      }\n      // 检查别名路径：使用用户配置的 externalAlias 或默认值\n      const aliasPrefixes = CUSTOM_CONFIG.externalAlias || ['@/', '~/', '#/'];\n      if (aliasPrefixes.some((prefix) => id.startsWith(prefix))) {\n        return false; // 不外部化，需要打包\n      }\n      // 其他情况：node_modules 中的依赖，应该外部化\n      return true;\n    };\n  };\n\n  const commonConfigJson = defineConfig({\n    root: PROJECT_ROOT,\n    build: {\n      sourcemap: CLI_ARGS_OBJ.sourcemap ?? true,\n      lib: {\n        name: CUSTOM_CONFIG.libName,\n        entry: path.resolve(PROJECT_ROOT, CUSTOM_CONFIG.entry),\n        formats: formats,\n        fileName: (format: string) => `${CUSTOM_CONFIG.fileName || 'index'}.${format}.js`,\n        cssFileName: CUSTOM_CONFIG.cssFileName ?? 'style',\n      },\n      rollupOptions: {\n        // 确保外部化处理那些你不想打包进库的依赖\n        external: getExternal(),\n        output: {\n          // 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量\n          globals: CUSTOM_CONFIG.global || {},\n        },\n      },\n    },\n    plugins: [react(), eslint()],\n  });\n\n  if (CUSTOM_CONFIG.libMode === false) {\n    delete commonConfigJson?.build?.lib;\n    delete commonConfigJson?.build?.rollupOptions;\n  }\n\n  return commonConfigJson;\n};\n"
  },
  {
    "path": "packages/build-script/src/config/vite.dev.ts",
    "content": "import { getCommonConfig } from './vite.common';\nimport { getCustomConfig } from './base';\nimport { mergeConfig } from 'vite';\n\n// https://vitejs.dev/config/\nexport const devConfig = async () => {\n  const CUSTOM_CONFIG = await getCustomConfig();\n  const commonConfig = await getCommonConfig();\n\n  const config = mergeConfig(commonConfig, {\n    mode: 'development',\n    configFile: false,\n    server: {\n      port: 3000,\n    },\n  });\n  const finalConfig = mergeConfig(config, CUSTOM_CONFIG?.vite || {});\n  // const fp = `${PROJECT_ROOT}/.temp.vite.config.json`;\n\n  return finalConfig;\n};\n"
  },
  {
    "path": "packages/build-script/src/core/devServer.ts",
    "content": "import { createServer } from 'vite';\nimport { devConfig } from '../config/vite.dev';\n\nexport const doDev = async () => {\n  const server = await createServer(await devConfig());\n  await server.listen();\n\n  server.printUrls();\n};\n"
  },
  {
    "path": "packages/build-script/src/core/doBuild.ts",
    "content": "import { build } from 'vite';\nimport { buildConfig } from '../config/vite.build';\nimport { getCustomConfig, CLI_ARGS_OBJ } from '../config/base';\nimport type { LibraryFormats } from 'vite';\n\nexport const doBuild = async () => {\n  console.log('start to build .....');\n\n  const CUSTOM_CONFIG = await getCustomConfig();\n  const defaultFormats: LibraryFormats[] = CLI_ARGS_OBJ.build ? ['cjs', 'es', 'umd'] : ['cjs', 'es'];\n  const formats = CUSTOM_CONFIG?.formats || defaultFormats;\n\n  // 将格式分为两组：\n  // 1. 模块格式组（es, cjs）- 通常使用更严格的 external\n  // 2. 浏览器格式组（umd, iife）- 通常只 external react, react-dom\n  const moduleFormats = formats.filter((f) => f === 'es' || f === 'cjs') as LibraryFormats[];\n  const browserFormats = formats.filter((f) => f === 'umd' || f === 'iife') as LibraryFormats[];\n\n  const hasModuleFormats = moduleFormats.length > 0;\n  const hasBrowserFormats = browserFormats.length > 0;\n\n  // 如果两组都存在，分两次构建\n  if (hasModuleFormats && hasBrowserFormats) {\n    // 先构建模块格式（es, cjs）\n    console.log(`Building module formats: ${moduleFormats.join(', ')}...`);\n    await build(await buildConfig(moduleFormats));\n\n    // 再构建浏览器格式（umd, iife），不清空输出目录\n    console.log(`Building browser formats: ${browserFormats.join(', ')}...`);\n    await build(await buildConfig(browserFormats, true));\n  } else {\n    // 如果只有一组格式，正常构建\n    await build(await buildConfig());\n  }\n\n  console.log('build finished.');\n};\n"
  },
  {
    "path": "packages/build-script/src/index.ts",
    "content": "#!/usr/bin/env node\nimport { CLI_ARGS_OBJ } from './config/base';\nimport { doDev } from './core/devServer';\nimport { doBuild } from './core/doBuild';\n\nexport * from './config/base';\nexport * from './config/vite.common';\nexport * from './config/vite.build';\nexport * from './config/vite.dev';\n\nexport function run() {\n  if (CLI_ARGS_OBJ.build) {\n    doBuild();\n  } else {\n    doDev();\n  }\n}\n\nexport * from './config/base';\n"
  },
  {
    "path": "packages/build-script/test/demo.test.ts",
    "content": "import { a } from '../src';\ndescribe('cli', () => {\n  it('init', () => {\n    expect(a()).toEqual(1);\n  });\n});\n"
  },
  {
    "path": "packages/build-script/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"module\": \"ESNext\" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,\n    \"strict\": true /* Enable all strict type-checking options. */,\n    \"moduleResolution\": \"bundler\" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */,\n    \"types\": [\n      \"node\",\n      \"jest\"\n    ],\n    \"skipLibCheck\": true /* Skip type checking of declaration files. */,\n    \"outDir\": \"lib\",\n    \"forceConsistentCasingInFileNames\": true,\n    \"declaration\": true,\n    \"esModuleInterop\": true,\n    \"paths\": {\n      \"@\": [\n        \"./src\"\n      ]\n    }\n  },\n  \"exclude\": [\n    \"test\",\n    \"lib\"\n  ]\n}"
  },
  {
    "path": "packages/demo-page/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "packages/demo-page/.prettierrc.json",
    "content": "{\n  \"trailingComma\": \"es5\",\n  \"tabWidth\": 2,\n  \"semi\": true,\n  \"singleQuote\": true\n}"
  },
  {
    "path": "packages/demo-page/CHANGELOG.md",
    "content": "# Change Log\n\nAll notable changes to this project will be documented in this file.\nSee [Conventional Commits](https://conventionalcommits.org) for commit guidelines.\n\n## [0.10.4](https://github.com/ByteCrazy/chameleon/compare/v0.10.3...v0.10.4) (2026-01-17)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.10.3](https://github.com/ByteCrazy/chameleon/compare/v0.10.2...v0.10.3) (2026-01-17)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.10.2](https://github.com/ByteCrazy/chameleon/compare/v0.10.1...v0.10.2) (2026-01-17)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.10.1](https://github.com/ByteCrazy/chameleon/compare/v0.10.0...v0.10.1) (2026-01-11)\n\n### ✨ Features | 新功能\n\n* **build-script, demo-page, engine, render:** 🎸 update build script dts & optimize modal root selector ([024d5ab](https://github.com/ByteCrazy/chameleon/commit/024d5ab26d19fc5f50172c328638a551fb99c129))\n\n## [0.10.0](https://github.com/hlerenow/chameleon/compare/v0.9.3...v0.10.0) (2025-12-25)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* 优化构建配置，修复 ES 模块外部化问题 ([866f0ae](https://github.com/hlerenow/chameleon/commit/866f0ae39472625137d73d143625d88d873f6c00))\n\n## [0.9.3](https://github.com/ByteCrazy/chameleon/compare/v0.9.2...v0.9.3) (2025-07-20)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.9.2](https://github.com/ByteCrazy/chameleon/compare/v0.9.1...v0.9.2) (2025-07-13)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.9.1](https://github.com/ByteCrazy/chameleon/compare/v0.9.0...v0.9.1) (2025-07-13)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.9.0](https://github.com/ByteCrazy/chameleon/compare/v0.8.6...v0.9.0) (2025-07-13)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.8.6](https://github.com/ByteCrazy/chameleon/compare/v0.8.5...v0.8.6) (2025-06-21)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **demo-page, engine, render:** 🐛 fixed action flow failed node not connect correct ([e235a2b](https://github.com/ByteCrazy/chameleon/commit/e235a2be2d1e1935dfc40b56936de763efc4fb67))\n\n## [0.8.5](https://github.com/ByteCrazy/chameleon/compare/v0.8.4...v0.8.5) (2025-04-13)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.8.4](https://github.com/ByteCrazy/chameleon/compare/v0.8.3...v0.8.4) (2025-04-13)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.8.3](https://github.com/ByteCrazy/chameleon/compare/v0.8.2...v0.8.3) (2025-04-12)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.8.2](https://github.com/ByteCrazy/chameleon/compare/v0.8.1...v0.8.2) (2025-04-11)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.8.1](https://github.com/ByteCrazy/chameleon/compare/v0.8.0...v0.8.1) (2025-04-10)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.8.0](https://github.com/ByteCrazy/chameleon/compare/v0.7.0...v0.8.0) (2025-04-10)\n\n### ✨ Features | 新功能\n\n* **demo-page, engine, render:** 🎸 update run time var for function ([d8fb90a](https://github.com/ByteCrazy/chameleon/commit/d8fb90ac72a233d7fdb5fcb36e0b7963f83c5acf))\n\n## [0.7.0](https://github.com/ByteCrazy/chameleon/compare/v0.6.0...v0.7.0) (2025-04-06)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.6.0](https://github.com/ByteCrazy/chameleon/compare/v0.5.2...v0.6.0) (2025-03-30)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.5.2](https://github.com/ByteCrazy/chameleon/compare/v0.5.1...v0.5.2) (2025-03-30)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.5.1](https://github.com/ByteCrazy/chameleon/compare/v0.5.0...v0.5.1) (2025-03-30)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.5.0](https://github.com/ByteCrazy/chameleon/compare/v0.4.0...v0.5.0) (2025-03-30)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.4.0](https://github.com/ByteCrazy/chameleon/compare/v0.3.21...v0.4.0) (2025-03-29)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.3.21](https://github.com/ByteCrazy/chameleon/compare/v0.3.20...v0.3.21) (2025-03-26)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.3.20](https://github.com/ByteCrazy/chameleon/compare/v0.3.19...v0.3.20) (2025-03-26)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.3.19](https://github.com/ByteCrazy/chameleon/compare/v0.3.18...v0.3.19) (2025-03-26)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.3.18](https://github.com/ByteCrazy/chameleon/compare/v0.3.17...v0.3.18) (2025-03-26)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.3.17](https://github.com/ByteCrazy/chameleon/compare/v0.3.16...v0.3.17) (2025-03-25)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.3.16](https://github.com/ByteCrazy/chameleon/compare/v0.3.15...v0.3.16) (2025-03-24)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.3.15](https://github.com/ByteCrazy/chameleon/compare/v0.3.14...v0.3.15) (2025-03-23)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.3.14](https://github.com/ByteCrazy/chameleon/compare/v0.3.13...v0.3.14) (2025-03-23)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.3.13](https://github.com/ByteCrazy/chameleon/compare/v0.3.12...v0.3.13) (2025-03-23)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.3.12](https://github.com/ByteCrazy/chameleon/compare/v0.3.11...v0.3.12) (2025-03-23)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.3.11](https://github.com/ByteCrazy/chameleon/compare/v0.3.10...v0.3.11) (2025-03-22)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.3.10](https://github.com/ByteCrazy/chameleon/compare/v0.3.9...v0.3.10) (2025-03-22)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.3.9](https://github.com/ByteCrazy/chameleon/compare/v0.3.8...v0.3.9) (2025-03-22)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.3.8](https://github.com/ByteCrazy/chameleon/compare/v0.3.7...v0.3.8) (2025-03-16)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.3.7](https://github.com/ByteCrazy/chameleon/compare/v0.3.6...v0.3.7) (2025-03-16)\n\n### ✨ Features | 新功能\n\n* **demo-page, engine, model, render:** 🎸 support inject eng inner env to runtime ([baa5c11](https://github.com/ByteCrazy/chameleon/commit/baa5c11d389019a7e4e4b8e000433a99038b4ae3))\n\n## [0.3.6](https://github.com/ByteCrazy/chameleon/compare/v0.3.5...v0.3.6) (2025-03-09)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.3.5](https://github.com/ByteCrazy/chameleon/compare/v0.3.4...v0.3.5) (2025-03-09)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **demo-page, engine, render:** 🐛 fixed action flow only run first node ([c4bdb85](https://github.com/ByteCrazy/chameleon/commit/c4bdb85d0ca6ed09c6c66d15847de5bd14da556e))\n\n## [0.3.4](https://github.com/ByteCrazy/chameleon/compare/v0.3.3...v0.3.4) (2025-02-16)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.3.3](https://github.com/ByteCrazy/chameleon/compare/v0.3.2...v0.3.3) (2025-02-16)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.3.2](https://github.com/ByteCrazy/chameleon/compare/v0.3.1...v0.3.2) (2025-02-16)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.3.1](https://github.com/ByteCrazy/chameleon/compare/v0.3.0...v0.3.1) (2025-02-16)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.3.0](https://github.com/ByteCrazy/chameleon/compare/v0.2.4...v0.3.0) (2025-02-15)\n\n### ✨ Features | 新功能\n\n* **demo-page, engine, model, render:** 🎸 action node use link struct ([599ece4](https://github.com/ByteCrazy/chameleon/commit/599ece4927523f7c0e330cac722c9c9e6976ea3c))\n* **demo-page, engine, model, render:** 🎸 add event panel ([e8c5648](https://github.com/ByteCrazy/chameleon/commit/e8c5648017b40cbae42c576267d1e3b9d9660918))\n* **demo-page, engine, model, render:** 🎸 support TActionLogicItem prop ([e1b9d1e](https://github.com/ByteCrazy/chameleon/commit/e1b9d1e150ae810750249322ddf906b62eee9969))\n* **demo-page, model, render:** 🎸 optimize afterResponse ([b4060ba](https://github.com/ByteCrazy/chameleon/commit/b4060ba0a0a01960ae5f8dffea159e2d5ea680b6))\n* **demo-page, model, render:** 🎸 support ASSIGN_VALUE node ([74c395b](https://github.com/ByteCrazy/chameleon/commit/74c395baec55911de734e47d4a270f9cf016ee21))\n* **demo-page, model:** 🎸 define action and event type ([f7ca5d3](https://github.com/ByteCrazy/chameleon/commit/f7ca5d3b8ad7610f840845f555bfcdb2adddae0a))\n* **demo-page, render:** 🎸 getMethods support get methods from ref ([0a26967](https://github.com/ByteCrazy/chameleon/commit/0a2696739eee29c3e30b805d537605a06496ebc5))\n* **demo-page, render:** 🎸 support eventListener attr ([fa0977f](https://github.com/ByteCrazy/chameleon/commit/fa0977fab37b961d1b7a0b04c8e528e43ab9503f))\n* **demo-page, render:** 🎸 support ON_DID_RENDER and ON_WILL_DESTROY inner event ([b4743e8](https://github.com/ByteCrazy/chameleon/commit/b4743e855766b69a8c5bc58ea3849694948bd501))\n\n## [0.2.4](https://github.com/ByteCrazy/chameleon/compare/v0.2.3...v0.2.4) (2024-12-08)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.2.3](https://github.com/ByteCrazy/chameleon/compare/v0.2.2...v0.2.3) (2024-12-08)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.2.2](https://github.com/ByteCrazy/chameleon/compare/v0.2.1...v0.2.2) (2024-12-07)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.2.1](https://github.com/ByteCrazy/chameleon/compare/v0.2.0...v0.2.1) (2024-12-07)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.2.0](https://github.com/ByteCrazy/chameleon/compare/v0.1.1...v0.2.0) (2024-12-07)\n\n### ✨ Features | 新功能\n\n* **build-script, demo-page, engine, engine-website-app, layout, material, model, render:** 🎸 upgrade vite to 6.0 ([bcac2b1](https://github.com/ByteCrazy/chameleon/commit/bcac2b15b83b41a7042ca37368c1b45302ad81d5))\n\n## [0.1.1](https://github.com/ByteCrazy/chameleon/compare/v0.1.0...v0.1.1) (2024-11-11)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.1.0](https://github.com/ByteCrazy/chameleon/compare/v0.0.46...v0.1.0) (2024-09-07)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **demo-page, docs-app, engine, engine-website-app, layout, material:** 🐛 fixed material meta not correct ([55b755c](https://github.com/ByteCrazy/chameleon/commit/55b755ce0c833e594b46447b2d6608cf56f7a593))\n\n### ✨ Features | 新功能\n\n* **build-script, demo-page, engine, layout, render:** 🎸 add lang switch ([29da65e](https://github.com/ByteCrazy/chameleon/commit/29da65ee1aa09550d910ddfbbcb9d8b4db983373))\n* **demo-page, engine, engine-website-app, material:** 🎸 add designerSizer and fixed GridItem bug ([4665aed](https://github.com/ByteCrazy/chameleon/commit/4665aed300d54c77be4abcb9a8cc0f1710ac2145))\n* **demo-page, engine, render:** 🎸 add getGlobalState and optimize node udpate ([4d3934f](https://github.com/ByteCrazy/chameleon/commit/4d3934fd8febe616a44e5d39da0e10964f3c800d))\n\n### 📝 Documentation | 文档\n\n* **demo-page, engine-website-app:** ✏️ demo app add switch page ([c098997](https://github.com/ByteCrazy/chameleon/commit/c098997e2c53bbe135ce263fcd1912ddc53146fe))\n\n## [0.0.46](https://github.com/ByteCrazy/chameleon/compare/v0.0.45...v0.0.46) (2024-06-30)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.45](https://github.com/ByteCrazy/chameleon/compare/v0.0.44...v0.0.45) (2024-06-30)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.44](https://github.com/ByteCrazy/chameleon/compare/v0.0.43...v0.0.44) (2024-06-30)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.43](https://github.com/ByteCrazy/chameleon/compare/v0.0.42...v0.0.43) (2024-06-29)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.42](https://github.com/ByteCrazy/chameleon/compare/v0.0.41...v0.0.42) (2024-06-01)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.41](https://github.com/ByteCrazy/chameleon/compare/v0.0.40...v0.0.41) (2024-05-26)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.40](https://github.com/ByteCrazy/chameleon/compare/v0.0.39...v0.0.40) (2024-04-28)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.39](https://github.com/ByteCrazy/chameleon/compare/v0.0.38...v0.0.39) (2024-04-27)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **build-script, demo-page, engine, layout, material, model, render:** 🐛 fix flatObject collectVariable refeer pos ([1c4163a](https://github.com/ByteCrazy/chameleon/commit/1c4163aeeb89176a1ff5b50f76930889df7751fe))\n\n## [0.0.38](https://github.com/ByteCrazy/chameleon/compare/v0.0.37...v0.0.38) (2024-04-27)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.37](https://github.com/ByteCrazy/chameleon/compare/v0.0.36...v0.0.37) (2024-04-27)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.36](https://github.com/ByteCrazy/chameleon/compare/v0.0.35...v0.0.36) (2024-04-27)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.35](https://github.com/ByteCrazy/chameleon/compare/v0.0.34...v0.0.35) (2024-04-27)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.34](https://github.com/ByteCrazy/chameleon/compare/v0.0.33...v0.0.34) (2024-04-27)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.33](https://github.com/ByteCrazy/chameleon/compare/v0.0.32...v0.0.33) (2024-04-27)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.32](https://github.com/ByteCrazy/chameleon/compare/v0.0.31...v0.0.32) (2024-01-30)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.31](https://github.com/ByteCrazy/chameleon/compare/v0.0.30...v0.0.31) (2023-10-17)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.30](https://github.com/ByteCrazy/chameleon/compare/v0.0.29...v0.0.30) (2023-10-17)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.29](https://github.com/ByteCrazy/chameleon/compare/v0.0.28...v0.0.29) (2023-10-17)\n\n### ✨ Features | 新功能\n\n* **demo-page, engine, model:** 🎸 add ant color setter ([830de46](https://github.com/ByteCrazy/chameleon/commit/830de46babdf40a740248c37d8e6dc2043db1434))\n* **demo-page, engine, model:** 🎸 add ColorSetter ([e72b7f1](https://github.com/ByteCrazy/chameleon/commit/e72b7f15dd8bba498b776226d5debd07c8fd4234))\n* **demo-page, engine, model:** 🎸 add radio setter ([e6f9b1d](https://github.com/ByteCrazy/chameleon/commit/e6f9b1d9dba7cd3a9a82116bf5a1068c06a5a1d6))\n* **demo-page, engine:** 🎸 add cssSize setter ([74bf136](https://github.com/ByteCrazy/chameleon/commit/74bf136047000a67795e1360f2a923902366a513))\n* **demo-page, engine:** 🎸 add slider setter ([0fe1d29](https://github.com/ByteCrazy/chameleon/commit/0fe1d29257f214e69ca9fed6e5e4c11ccccb56bb))\n* **demo-page, render:** 🎸 compatible new style schema ([8165453](https://github.com/ByteCrazy/chameleon/commit/816545302e4d88a0976e8fdf3b17ed8a9a2978bd))\n\n## [0.0.28](https://github.com/ByteCrazy/chameleon/compare/v0.0.27...v0.0.28) (2023-08-29)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.27](https://github.com/ByteCrazy/chameleon/compare/v0.0.26...v0.0.27) (2023-08-28)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.26](https://github.com/ByteCrazy/chameleon/compare/v0.0.25...v0.0.26) (2023-08-27)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.25](https://github.com/ByteCrazy/chameleon/compare/v0.0.24...v0.0.25) (2023-08-27)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.24](https://github.com/ByteCrazy/chameleon/compare/v0.0.23...v0.0.24) (2023-08-27)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.23](https://github.com/ByteCrazy/chameleon/compare/v0.0.22...v0.0.23) (2023-08-26)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.22](https://github.com/ByteCrazy/chameleon/compare/v0.0.21...v0.0.22) (2023-08-24)\n\n### ✨ Features | 新功能\n\n* **demo-page, engine, layout, model:** 🎸 add disableEditorDragDom config ([1779f44](https://github.com/ByteCrazy/chameleon/commit/1779f44aff20370ea3336e72352032c6416f7dd3))\n\n## [0.0.21](https://github.com/ByteCrazy/chameleon/compare/v0.0.20...v0.0.21) (2023-08-19)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.20](https://github.com/ByteCrazy/chameleon/compare/v0.0.19...v0.0.20) (2023-08-19)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.19](https://github.com/ByteCrazy/chameleon/compare/v0.0.18...v0.0.19) (2023-08-17)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.18](https://github.com/ByteCrazy/chameleon/compare/v0.0.17...v0.0.18) (2023-08-14)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.17](https://github.com/ByteCrazy/chameleon/compare/v0.0.16...v0.0.17) (2023-08-06)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.16](https://github.com/ByteCrazy/chameleon/compare/v0.0.15...v0.0.16) (2023-08-06)\n\n### ✨ Features | 新功能\n\n* **build-script-example, demo-page, engine, layout, material, model, render:** 🎸 support direct import render from package on dev mode ([6bb38e2](https://github.com/ByteCrazy/chameleon/commit/6bb38e29678a8a4d08a7fcf9548fa548399259b0))\n* **demo-page, engine, layout, model:** 🎸 support  advanceCustom drag and drop hooks ([fb21e15](https://github.com/ByteCrazy/chameleon/commit/fb21e1501f6829e4f13a35ea7be942ff72b0be91))\n* **demo-page, engine, layout, model:** 🎸 support toolbarViewRender and ghostViewRedner ([43ced37](https://github.com/ByteCrazy/chameleon/commit/43ced371cfb7dbb58a96fc15e1bf635092307fa8))\n* **demo-page, engine, layout:** 🎸 support DropViewRender ([1c025a6](https://github.com/ByteCrazy/chameleon/commit/1c025a6d1f57f450b2de262a787375f3f4891008))\n* **demo-page, engine, model:** 🎸 support onDelete onCopy ([b6a76bb](https://github.com/ByteCrazy/chameleon/commit/b6a76bb244bbe2c050de17157bda97cb7ad21abc))\n\n## [0.0.15](https://github.com/ByteCrazy/chameleon/compare/v0.0.14...v0.0.15) (2023-07-01)\n\n### ✨ Features | 新功能\n\n* **demo-page, docs-website, engine, model, render:** 🎸 optimize assets load ([3ee6d58](https://github.com/ByteCrazy/chameleon/commit/3ee6d58a88e5af3fc631723240783d5c97a273b0))\n* **demo-page, engine:** 🎸 setter support initialValue ([794d807](https://github.com/ByteCrazy/chameleon/commit/794d8072518cb3b1a897b04a2e96f5ca53fdb365))\n\n## [0.0.14](https://github.com/ByteCrazy/chameleon/compare/v0.0.13...v0.0.14) (2023-05-10)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.13](https://github.com/ByteCrazy/chameleon/compare/v0.0.12...v0.0.13) (2023-05-03)\n\n### ✨ Features | 新功能\n\n* **build-script, build-script-example, demo-page, engine, layout, material, model, render:** 🎸 config code style config ([2325504](https://github.com/ByteCrazy/chameleon/commit/23255048fa4a3d4fc8f5cfa1312db5abd6cac70d))\n* **build-script, demo-page, engine, layout, model, render:** 🎸 update meterial schema ([14f4e5c](https://github.com/ByteCrazy/chameleon/commit/14f4e5caf0a4f6eca131e139d0c34ea21969dc0b))\n* **demo-page, docs-website, engine, layout, model, render:** 🎸 node support methods config ([e080b20](https://github.com/ByteCrazy/chameleon/commit/e080b20cea3cea27b95def196e078c1c225d3a83))\n* **demo-page, engine, layout, model:** 🎸 finish customEvent config ([84640e3](https://github.com/ByteCrazy/chameleon/commit/84640e3d06b79858590b9fe92ef4764fbe3f4f7b))\n* **demo-page, engine, layout:** 🎸 outline support cancel drag by node material ([cd428d3](https://github.com/ByteCrazy/chameleon/commit/cd428d362a8bfe7d5b6e58ed2b6290e19c00672b))\n* **demo-page, engine, layout:** 🎸 support cancel drag and custom node drag event ([825717e](https://github.com/ByteCrazy/chameleon/commit/825717e063846b72f95b0e41d8cba279ef5270c8))\n* **demo-page, engine:** 🎸 complete advanceCustom material feature ([98cf273](https://github.com/ByteCrazy/chameleon/commit/98cf273543f2fd534c254990d61052534a6649da))\n* **demo-page, engine:** 🎸 supprot onSelectNode ([f496a82](https://github.com/ByteCrazy/chameleon/commit/f496a82920cab7ae8704335b2ecd02a7887de3b2))\n\n## [0.0.12](https://github.com/ByteCrazy/chameleon/compare/v0.0.11...v0.0.12) (2023-04-23)\n\n### ✨ Features | 新功能\n\n* **demo-page, docs-website, engine, model:** 🎸 update schema and change err to warn when schema check ([e753862](https://github.com/ByteCrazy/chameleon/commit/e7538626bad6681e4d488ac835fef61a603c0853))\n* **demo-page, engine, layout, model, render:** 🎸 node support config self isContainer property ([d94f1f9](https://github.com/ByteCrazy/chameleon/commit/d94f1f9203f2c273856fd4aa489a1ed9cbb0f0ec))\n\n## [0.0.11](https://github.com/ByteCrazy/chameleon/compare/v0.0.10...v0.0.11) (2023-04-21)\n\n### ✨ Features | 新功能\n\n* **build-script, build-script-example, demo-page, engine, layout, material, model, render:** 🎸 optimize build-script output file format ([eda44d2](https://github.com/ByteCrazy/chameleon/commit/eda44d255bd3c5af19013bdd61342e1f0817413a))\n\n## [0.0.10](https://github.com/ByteCrazy/chameleon/compare/v0.0.9...v0.0.10) (2023-04-17)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.9](https://github.com/ByteCrazy/chameleon/compare/v0.0.8...v0.0.9) (2023-04-17)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.8](https://github.com/ByteCrazy/chameleon/compare/v0.0.7...v0.0.8) (2023-04-16)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.7](https://github.com/ByteCrazy/chameleon/compare/v0.0.6...v0.0.7) (2023-03-29)\n\n### ✨ Features | 新功能\n\n* **build-script, build-script-example, demo-page, engine, layout, material, model, render:** 🎸 external monaco eitor and remove style panel ([494d898](https://github.com/ByteCrazy/chameleon/commit/494d898fd75dabe84d867ff45e84ae63f9b59c5e))\n\n## [0.0.6](https://github.com/ByteCrazy/chameleon/compare/v0.0.5...v0.0.6) (2023-03-28)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.5](https://github.com/ByteCrazy/chameleon/compare/v0.0.4...v0.0.5) (2023-03-28)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.4](https://github.com/ByteCrazy/chameleon/compare/v0.0.3...v0.0.4) (2023-03-27)\n\n**Note:** Version bump only for package @chamn/demo-page\n\n## [0.0.2](https://github.com/ByteCrazy/chameleon/compare/v0.0.1...v0.0.2) (2023-03-27)\n\n**Note:** Version bump only for package @chamn/demo-page\n"
  },
  {
    "path": "packages/demo-page/__tests__/demo.test.ts",
    "content": "test('adds 1 + 2 to equal 3', () => {\n  expect(3).toBe(3);\n});\n"
  },
  {
    "path": "packages/demo-page/build.config.js",
    "content": "/* eslint-disable no-undef */\n// 开发模式默认读取 index.html 作为开发模式入口\n// entry 作为打包库入口\nexport default {\n  entry: './src/index.ts',\n  libName: 'DemoPage',\n  fileName: 'index',\n  external: ['react', 'react-dom'],\n  global: {\n    react: 'React',\n    'react-dom': 'ReactDOM',\n  },\n  // 额外的 vite 配置\n  vite: {},\n};\n"
  },
  {
    "path": "packages/demo-page/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" type=\"image/svg+xml\" href=\"/vite.svg\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Vite + React + TS</title>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/main.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "packages/demo-page/jest.config.js",
    "content": "/* eslint-disable no-undef */\n/*\n * For a detailed explanation regarding each configuration property, visit:\n * https://jestjs.io/docs/configuration\n */\n\nmodule.exports = {\n  // All imported modules in your tests should be mocked automatically\n  // automock: false,\n\n  // Stop running tests after `n` failures\n  // bail: 0,\n\n  // The directory where Jest should store its cached dependency information\n  // cacheDirectory: \"/private/var/folders/l9/th_r5d_12wxdj16859_mctjw0000gn/T/jest_dx\",\n\n  // Automatically clear mock calls, instances, contexts and results before every test\n  // clearMocks: false,\n\n  // Indicates whether the coverage information should be collected while executing the test\n  // collectCoverage: false,\n\n  // An array of glob patterns indicating a set of files for which coverage information should be collected\n  // collectCoverageFrom: undefined,\n\n  // The directory where Jest should output its coverage files\n  // coverageDirectory: undefined,\n\n  // An array of regexp pattern strings used to skip coverage collection\n  // coveragePathIgnorePatterns: [\n  //   \"/node_modules/\"\n  // ],\n\n  // Indicates which provider should be used to instrument code for coverage\n  // coverageProvider: \"babel\",\n\n  // A list of reporter names that Jest uses when writing coverage reports\n  // coverageReporters: [\n  //   \"json\",\n  //   \"text\",\n  //   \"lcov\",\n  //   \"clover\"\n  // ],\n\n  // An object that configures minimum threshold enforcement for coverage results\n  // coverageThreshold: undefined,\n\n  // A path to a custom dependency extractor\n  // dependencyExtractor: undefined,\n\n  // Make calling deprecated APIs throw helpful error messages\n  // errorOnDeprecated: false,\n\n  // The default configuration for fake timers\n  // fakeTimers: {\n  //   \"enableGlobally\": false\n  // },\n\n  // Force coverage collection from ignored files using an array of glob patterns\n  // forceCoverageMatch: [],\n\n  // A path to a module which exports an async function that is triggered once before all test suites\n  // globalSetup: undefined,\n\n  // A path to a module which exports an async function that is triggered once after all test suites\n  // globalTeardown: undefined,\n\n  // A set of global variables that need to be available in all test environments\n  // globals: {},\n\n  // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.\n  // maxWorkers: \"50%\",\n\n  // An array of directory names to be searched recursively up from the requiring module's location\n  // moduleDirectories: [\n  //   \"node_modules\"\n  // ],\n\n  // An array of file extensions your modules use\n  // moduleFileExtensions: [\n  //   \"js\",\n  //   \"mjs\",\n  //   \"cjs\",\n  //   \"jsx\",\n  //   \"ts\",\n  //   \"tsx\",\n  //   \"json\",\n  //   \"node\"\n  // ],\n\n  // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module\n  // moduleNameMapper: {},\n\n  // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader\n  // modulePathIgnorePatterns: [],\n\n  // Activates notifications for test results\n  // notify: false,\n\n  // An enum that specifies notification mode. Requires { notify: true }\n  // notifyMode: \"failure-change\",\n\n  // A preset that is used as a base for Jest's configuration\n  preset: 'ts-jest',\n\n  // Run tests from one or more projects\n  // projects: undefined,\n\n  // Use this configuration option to add custom reporters to Jest\n  // reporters: undefined,\n\n  // Automatically reset mock state before every test\n  // resetMocks: false,\n\n  // Reset the module registry before running each individual test\n  // resetModules: false,\n\n  // A path to a custom resolver\n  // resolver: undefined,\n\n  // Automatically restore mock state and implementation before every test\n  // restoreMocks: false,\n\n  // The root directory that Jest should scan for tests and modules within\n  // rootDir: undefined,\n\n  // A list of paths to directories that Jest should use to search for files in\n  // roots: [\n  //   \"<rootDir>\"\n  // ],\n\n  // Allows you to use a custom runner instead of Jest's default test runner\n  // runner: \"jest-runner\",\n\n  // The paths to modules that run some code to configure or set up the testing environment before each test\n  // setupFiles: [],\n\n  // A list of paths to modules that run some code to configure or set up the testing framework before each test\n  // setupFilesAfterEnv: [],\n\n  // The number of seconds after which a test is considered as slow and reported as such in the results.\n  // slowTestThreshold: 5,\n\n  // A list of paths to snapshot serializer modules Jest should use for snapshot testing\n  // snapshotSerializers: [],\n\n  // The test environment that will be used for testing\n  testEnvironment: 'jsdom',\n\n  // Options that will be passed to the testEnvironment\n  // testEnvironmentOptions: {},\n\n  // Adds a location field to test results\n  // testLocationInResults: false,\n\n  // The glob patterns Jest uses to detect test files\n  // testMatch: [\n  //   \"**/__tests__/**/*.[jt]s?(x)\",\n  //   \"**/?(*.)+(spec|test).[tj]s?(x)\"\n  // ],\n\n  // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped\n  // testPathIgnorePatterns: [\n  //   \"/node_modules/\"\n  // ],\n\n  // The regexp pattern or array of patterns that Jest uses to detect test files\n  // testRegex: [],\n\n  // This option allows the use of a custom results processor\n  // testResultsProcessor: undefined,\n\n  // This option allows use of a custom test runner\n  // testRunner: \"jest-circus/runner\",\n\n  // A map from regular expressions to paths to transformers\n  // transform: undefined,\n\n  // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation\n  // transformIgnorePatterns: [\n  //   \"/node_modules/\",\n  //   \"\\\\.pnp\\\\.[^\\\\/]+$\"\n  // ],\n\n  // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them\n  // unmockedModulePathPatterns: undefined,\n\n  // Indicates whether each individual test should be reported during the run\n  // verbose: undefined,\n\n  // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode\n  // watchPathIgnorePatterns: [],\n\n  // Whether to use watchman for file crawling\n  // watchman: true,\n};\n"
  },
  {
    "path": "packages/demo-page/package.json",
    "content": "{\n  \"name\": \"@chamn/demo-page\",\n  \"private\": false,\n  \"type\": \"module\",\n  \"files\": [\n    \"dist\"\n  ],\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"version\": \"0.10.4\",\n  \"main\": \"dist/index.cjs.js\",\n  \"module\": \"dist/index.es.js\",\n  \"types\": \"dist/index.d.ts\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./dist/index.d.ts\",\n      \"module-sync\": \"./dist/index.es.js\",\n      \"import\": \"./dist/index.es.js\",\n      \"require\": \"./dist/index.cjs.js\"\n    },\n    \"./dist/*\": \"./dist/*\"\n  },\n  \"scripts\": {\n    \"start\": \"build-script\",\n    \"build\": \"build-script --build\",\n    \"build:w\": \"build-script --build --watch\",\n    \"lint\": \"eslint ./src/\",\n    \"prettier\": \"prettier --write ./src\",\n    \"test\": \"jest\"\n  },\n  \"peerDependencies\": {\n    \"react\": \">=16.9.0\",\n    \"react-dom\": \">=16.9.0\"\n  },\n  \"devDependencies\": {\n    \"@chamn/build-script\": \"workspace:*\",\n    \"@chamn/model\": \"workspace:*\",\n    \"@types/react\": \"^18.2.0\",\n    \"@types/react-dom\": \"^18.0.6\",\n    \"react\": \"^18.2.0\",\n    \"react-dom\": \"^18.2.0\",\n    \"sass\": \"^1.54.0\"\n  },\n  \"config\": {},\n  \"gitHead\": \"dc3e55fdeb903a8012f6ebd3ebc018ed61ad89db\"\n}\n"
  },
  {
    "path": "packages/demo-page/src/index.ts",
    "content": "export { BasePage } from './pages/basePage';\nexport { BasePageBClient } from './pages/basePage-b-client';\nexport { SamplePage } from './pages/simplePage';\nexport { EmptyPage } from './pages/emptyPage';\nexport { LayoutPage } from './pages/layout';\nexport { Material } from './material/index';\n"
  },
  {
    "path": "packages/demo-page/src/material/advanceCustomButton.ts",
    "content": "import { CMaterialType } from '@chamn/model';\n\nexport const AdvanceButtonMeta: CMaterialType = {\n  title: 'Button',\n  componentName: 'Button',\n  npm: {\n    package: '@chamn/mock-material',\n    exportName: 'Button',\n    name: 'Button',\n    version: '1.0.0',\n  },\n  icon: 'https://alifd.oss-cn-hangzhou.aliyuncs.com/fusion-cool/icons/icon-light/ic_light_button.png',\n  props: [\n    {\n      name: 'type',\n      title: '按钮类型',\n      valueType: 'string',\n      setters: [\n        {\n          componentName: 'SelectSetter',\n          props: {\n            options: [\n              {\n                value: 'primary',\n                label: 'primary',\n              },\n              {\n                value: 'link',\n                label: 'link',\n              },\n              {\n                value: '',\n                label: 'Default',\n              },\n            ],\n          },\n        },\n      ],\n    },\n    {\n      name: 'block',\n      title: '块状按钮',\n      valueType: 'boolean',\n      setters: ['BooleanSetter'],\n      condition: (state) => {\n        if (state.type === 'primary') {\n          return true;\n        }\n        return false;\n      },\n    },\n    {\n      name: 'children',\n      title: '文本',\n      valueType: 'string',\n      setters: ['StringSetter', 'ExpressionSetter'],\n    },\n    {\n      name: 'onClick',\n      title: '点击时',\n      valueType: 'function',\n      setters: ['FunctionSetter', 'ExpressionSetter'],\n    },\n    {\n      name: 'text1',\n      title: '联动文本1',\n      valueType: 'string',\n      setters: [\n        {\n          componentName: 'StringSetter',\n        },\n      ],\n      condition: (state) => {\n        if (state.type === 'primary1') {\n          return true;\n        }\n        return false;\n      },\n    },\n    {\n      name: 'text2',\n      title: '联动文本2',\n      valueType: 'string',\n      setters: [\n        {\n          componentName: 'StringSetter',\n        },\n      ],\n      condition: (state) => {\n        if (state.text1 === '1') {\n          return true;\n        }\n        return false;\n      },\n    },\n    {\n      name: 'text3',\n      title: '联动文本3',\n      valueType: 'string',\n      setters: [\n        {\n          componentName: 'StringSetter',\n        },\n        'ExpressionSetter',\n      ],\n    },\n  ],\n  snippets: [\n    {\n      title: '自定义按钮',\n      snapshot:\n        'https://alifd.oss-cn-hangzhou.aliyuncs.com/fusion-cool/icons/icon-light/ic_light_button.png',\n      description:\n        '我是 antd 的 Button 组件,我是 antd 的 Button 组件,我是 antd 的 Button 组件,我是 antd 的 Button 组件,我是 antd 的 Button 组件,我是 antd 的 Button 组件,我是 antd 的 Button 组件,我是 antd 的 Button 组件,我是 antd 的 Button 组件',\n      schema: {\n        props: {\n          type: 'primary',\n        },\n        children: ['I am a Button'],\n      },\n    },\n    {\n      title: '基础按钮',\n      snapshot:\n        'https://alifd.oss-cn-hangzhou.aliyuncs.com/fusion-cool/icons/icon-light/ic_light_button.png',\n      category: '基础控件',\n      schema: {\n        props: {\n          type: 'primary',\n        },\n        children: ['I am a Button'],\n      },\n    },\n    {\n      title: '基础按钮',\n      snapshot:\n        'https://alifd.oss-cn-hangzhou.aliyuncs.com/fusion-cool/icons/icon-light/ic_light_button.png',\n      category: '基础控件',\n      schema: {\n        props: {\n          type: 'primary',\n        },\n        children: ['I am a Button'],\n      },\n    },\n    {\n      title: '基础按钮',\n      snapshot:\n        'https://alifd.oss-cn-hangzhou.aliyuncs.com/fusion-cool/icons/icon-light/ic_light_button.png',\n      category: '基础控件1',\n      schema: {\n        props: {\n          type: 'primary',\n        },\n        children: ['I am a Button'],\n      },\n    },\n    {\n      title: '基础按钮',\n      snapshot:\n        'https://alifd.oss-cn-hangzhou.aliyuncs.com/fusion-cool/icons/icon-light/ic_light_button.png',\n      category: '基础控件2',\n      schema: {\n        props: {\n          type: 'primary',\n        },\n        children: ['I am a Button'],\n      },\n    },\n    {\n      title: '基础按钮',\n      snapshot:\n        'https://alifd.oss-cn-hangzhou.aliyuncs.com/fusion-cool/icons/icon-light/ic_light_button.png',\n      category: '基础控件3',\n      schema: {\n        props: {\n          type: 'primary',\n        },\n        children: ['I am a Button'],\n      },\n    },\n    {\n      title: '基础按钮',\n      snapshot:\n        'https://alifd.oss-cn-hangzhou.aliyuncs.com/fusion-cool/icons/icon-light/ic_light_button.png',\n      category: '基础控件4',\n      schema: {\n        props: {\n          type: 'primary',\n        },\n        children: ['I am a Button'],\n      },\n    },\n  ],\n};\n"
  },
  {
    "path": "packages/demo-page/src/material/button.tsx",
    "content": "import { CMaterialType } from '@chamn/model';\nimport React from 'react';\n\nexport const ButtonMeta: CMaterialType<\n  | 'ColorSetter'\n  | 'SliderSetter'\n  | 'CSSSizeSetter'\n  | 'BoxSizingSetter'\n  | 'AntDColorSetter'\n  | 'RadioGroupSetter'\n  | 'CSSSizeSetter'\n> = {\n  title: 'Button',\n  componentName: 'Button',\n  npm: {\n    package: '@chamn/mock-material',\n    exportName: 'Button',\n    version: '1.0.0',\n    name: 'Button',\n  },\n  icon: 'https://alifd.oss-cn-hangzhou.aliyuncs.com/fusion-cool/icons/icon-light/ic_light_button.png',\n  props: [\n    {\n      name: 'setterTest',\n      title: 'setter test',\n      valueType: 'string',\n      setters: [\n        'AntDColorSetter',\n        {\n          componentName: 'RadioGroupSetter',\n          props: {\n            options: [\n              {\n                label: 'A',\n                value: 'a',\n              },\n              {\n                label: 'B',\n                value: 'b',\n              },\n              {\n                label: 'C',\n                value: 'c',\n              },\n            ],\n            optionType: 'button',\n            buttonStyle: 'solid',\n          },\n        },\n        'ColorSetter',\n        'SliderSetter',\n        'CSSSizeSetter',\n        'BoxSizingSetter',\n      ],\n    },\n    {\n      name: 'setterTest2',\n      title: 'setter test2',\n      valueType: 'string',\n      setters: [\n        {\n          componentName: 'ColorSetter',\n          hiddenLabel: true,\n        },\n      ],\n    },\n    {\n      name: 'type',\n      title: '按钮类型',\n      valueType: 'string',\n      setters: [\n        {\n          componentName: 'SelectSetter',\n          props: {\n            options: [\n              {\n                value: 'primary',\n                label: 'primary',\n              },\n              {\n                value: 'link',\n                label: 'link',\n              },\n              {\n                value: '',\n                label: 'Default',\n              },\n            ],\n          },\n        },\n      ],\n    },\n    {\n      name: 'block',\n      title: '块状按钮',\n      valueType: 'boolean',\n      setters: ['BooleanSetter'],\n      condition: (state) => {\n        if (state.type === 'primary') {\n          return true;\n        }\n        return false;\n      },\n    },\n    {\n      name: 'children',\n      title: '文本',\n      valueType: 'string',\n      setters: [\n        {\n          componentName: 'StringSetter',\n          initialValue: '123',\n        },\n        'ExpressionSetter',\n      ],\n    },\n    {\n      name: 'onClick',\n      title: '点击时',\n      valueType: 'function',\n      setters: ['FunctionSetter', 'ExpressionSetter'],\n    },\n    {\n      name: 'text1',\n      title: '联动文本1',\n      valueType: 'string',\n      setters: [\n        {\n          componentName: 'StringSetter',\n        },\n      ],\n      condition: (state) => {\n        if (state.type === 'primary1') {\n          return true;\n        }\n        return false;\n      },\n    },\n    {\n      name: 'text2',\n      title: '联动文本2',\n      valueType: 'string',\n      setters: [\n        {\n          componentName: 'StringSetter',\n        },\n      ],\n      condition: (state) => {\n        if (state.text1 === '1') {\n          return true;\n        }\n        return false;\n      },\n    },\n    {\n      name: 'text3',\n      title: '联动文本3',\n      valueType: 'string',\n      setters: [\n        {\n          componentName: 'StringSetter',\n        },\n        'ExpressionSetter',\n      ],\n    },\n  ],\n  advanceCustom: {\n    onCopy: async () => {\n      console.log('onCopy');\n    },\n    selectRectViewRender: (props) => {\n      console.log(123, props);\n      return (\n        <div\n          style={{\n            width: '100%',\n            height: '100%',\n            pointerEvents: 'none',\n          }}\n        >\n          123\n        </div>\n      );\n    },\n    hoverRectViewRender: () => {\n      return <>Hover</>;\n    },\n    dropViewRender: (props) => {\n      console.log('123213', props);\n      return <>drop 11111</>;\n    },\n    ghostViewRender: () => {\n      return <>Big Button</>;\n    },\n    toolbarViewRender: () => {\n      return <>toolbar</>;\n    },\n    canDragNode: async (node, params) => {\n      console.log('canDragNode', node, params);\n      if (params.event?.extraData?.type === 'NEW_ADD') {\n        return true;\n      }\n      return true;\n    },\n    onDragging: async () => {\n      console.log('onDragging');\n    },\n    onDrop: async () => {\n      console.log('onDrop');\n      return true;\n    },\n    onSelect: async () => {\n      console.log('onSelect');\n      return true;\n    },\n    onDelete: async () => {\n      console.log('onDelete');\n      return true;\n    },\n    onNewAdd: async (_node, params) => {\n      params.viewPortal.setView(<div>123</div>);\n      console.log('onNewAdd');\n      return new Promise((resolve) => {\n        setTimeout(() => {\n          resolve(true);\n          params.viewPortal.clearView();\n        }, 1000);\n      });\n    },\n  },\n  snippets: [\n    {\n      title: '基础按钮',\n      snapshot:\n        'https://alifd.oss-cn-hangzhou.aliyuncs.com/fusion-cool/icons/icon-light/ic_light_button.png',\n      description: '自定义延迟插入 Button, 不能被选中, 不能拖拽',\n      schema: {\n        props: {\n          type: 'primary',\n        },\n        children: ['I am a Button'],\n      },\n    },\n  ],\n};\n"
  },
  {
    "path": "packages/demo-page/src/material/col.ts",
    "content": "import { CMaterialType } from '@chamn/model';\n\nexport const ColMeta: CMaterialType = {\n  title: 'Col',\n  componentName: 'Col',\n  npm: {\n    package: '@chamn/mock-material',\n    exportName: 'Col',\n    name: 'Col',\n    version: '1.0.0',\n  },\n  icon: 'https://alifd.oss-cn-hangzhou.aliyuncs.com/fusion-cool/icons/icon-light/ic_light_button.png',\n  props: [\n    {\n      name: 'style',\n      title: '样式',\n      valueType: 'object',\n      setters: ['JSONSetter'],\n    },\n  ],\n  snippets: [\n    {\n      title: '列',\n      snapshot:\n        'https://alifd.oss-cn-hangzhou.aliyuncs.com/fusion-cool/icons/icon-light/ic_light_button.png',\n      category: 'HTML 元素',\n      schema: {\n        props: {},\n      },\n    },\n  ],\n  isContainer: true,\n};\n"
  },
  {
    "path": "packages/demo-page/src/material/index.ts",
    "content": "import { CMaterialType } from '@chamn/model';\nimport { ButtonMeta } from './button';\n// import { ColMeta } from './col';\n// import { InputMeta } from './input';\nimport { ModalMeta } from './modal';\nimport { DivMeta } from './native';\n// import { RowMeta } from './row';\nimport { TableMeta } from './table';\nimport { LayoutMeta } from './layout/index';\nimport { AdvanceButtonMeta } from './advanceCustomButton';\n\nexport const Material: CMaterialType<any>[] = [\n  TableMeta,\n  ModalMeta,\n  ButtonMeta,\n  LayoutMeta,\n  AdvanceButtonMeta,\n  DivMeta,\n];\n"
  },
  {
    "path": "packages/demo-page/src/material/input.ts",
    "content": "import { CMaterialType } from '@chamn/model';\n\nexport const InputMeta: CMaterialType = {\n  title: 'Input',\n  componentName: 'Input',\n  npm: {\n    package: '@chamn/mock-material',\n    exportName: 'Input',\n    name: 'Input',\n    version: '1.0.0',\n  },\n  icon: 'https://alifd.oss-cn-hangzhou.aliyuncs.com/fusion-cool/icons/icon-light/ic_light_button.png',\n  props: [\n    {\n      name: 'style',\n      title: '样式',\n      valueType: 'object',\n      setters: ['JSONSetter'],\n    },\n  ],\n  snippets: [\n    {\n      title: '输入框',\n      snapshot:\n        'https://alifd.oss-cn-hangzhou.aliyuncs.com/fusion-cool/icons/icon-light/ic_light_button.png',\n      category: 'HTML 元素',\n      schema: {\n        props: {},\n      },\n    },\n  ],\n};\n"
  },
  {
    "path": "packages/demo-page/src/material/layout/index.tsx",
    "content": "import { CMaterialType } from '@chamn/model';\n\nexport const LayoutMeta: CMaterialType = {\n  title: 'CLayout',\n  componentName: 'CLayout',\n  npm: {\n    package: '@chamn/mock-material',\n    exportName: 'CLayout',\n    version: '1.0.0',\n    name: 'CLayout',\n  },\n  icon: 'Layout',\n  disableEditorDragDom: {\n    class: ['as44556', 'resize-handle'],\n  },\n  props: [\n    {\n      name: 'type',\n      title: '按钮类型',\n      valueType: 'string',\n      setters: [\n        {\n          componentName: 'SelectSetter',\n          props: {\n            options: [\n              {\n                value: 'primary',\n                label: 'primary',\n              },\n              {\n                value: 'link',\n                label: 'link',\n              },\n              {\n                value: '',\n                label: 'Default',\n              },\n            ],\n          },\n        },\n      ],\n    },\n    {\n      name: 'block',\n      title: '块状按钮',\n      valueType: 'boolean',\n      setters: ['BooleanSetter'],\n      condition: (state) => {\n        if (state.type === 'primary') {\n          return true;\n        }\n        return false;\n      },\n    },\n    {\n      name: 'children',\n      title: '文本',\n      valueType: 'string',\n      setters: [\n        {\n          componentName: 'StringSetter',\n          initialValue: '123',\n        },\n        'ExpressionSetter',\n      ],\n    },\n    {\n      name: 'onClick',\n      title: '点击时',\n      valueType: 'function',\n      setters: ['FunctionSetter', 'ExpressionSetter'],\n    },\n    {\n      name: 'text1',\n      title: '联动文本1',\n      valueType: 'string',\n      setters: [\n        {\n          componentName: 'StringSetter',\n        },\n      ],\n      condition: (state) => {\n        if (state.type === 'primary1') {\n          return true;\n        }\n        return false;\n      },\n    },\n    {\n      name: 'text2',\n      title: '联动文本2',\n      valueType: 'string',\n      setters: [\n        {\n          componentName: 'StringSetter',\n        },\n      ],\n      condition: (state) => {\n        if (state.text1 === '1') {\n          return true;\n        }\n        return false;\n      },\n    },\n    {\n      name: 'text3',\n      title: '联动文本3',\n      valueType: 'string',\n      setters: [\n        {\n          componentName: 'StringSetter',\n        },\n        'ExpressionSetter',\n      ],\n    },\n  ],\n  snippets: [\n    {\n      title: '基础布局',\n      description: '自定义延迟插入 Button, 不能被选中, 不能拖拽',\n      snapshotText: 'Layout',\n      schema: {\n        props: {\n          type: 'primary',\n        },\n        children: ['I am a Button'],\n      },\n    },\n  ],\n};\n"
  },
  {
    "path": "packages/demo-page/src/material/modal.ts",
    "content": "import { CMaterialType } from '@chamn/model';\n\nexport const ModalMeta: CMaterialType = {\n  title: 'Modal',\n  componentName: 'Modal',\n  npm: {\n    package: '@chamn/mock-material',\n    exportName: 'Modal',\n    version: '1.0.0',\n    name: 'Modal',\n  },\n  icon: 'https://alifd.oss-cn-hangzhou.aliyuncs.com/fusion-cool/icons/icon-light/ic_light_button.png',\n  props: [\n    {\n      name: 'visible',\n      title: '可见性',\n      valueType: 'boolean',\n      setters: ['BooleanSetter', 'ExpressionSetter'],\n    },\n  ],\n  fixedProps: {\n    open: true,\n  },\n  rootSelector: '.ant-modal-content',\n  snippets: [\n    {\n      title: '基础元素',\n      snapshotText: 'Modal',\n      category: '高级组件',\n      schema: {\n        props: {},\n        children: ['I am a Modal'],\n      },\n    },\n  ],\n};\n"
  },
  {
    "path": "packages/demo-page/src/material/native.ts",
    "content": "import { CMaterialType } from '@chamn/model';\n\nexport const DivMeta: CMaterialType = {\n  title: 'Div',\n  componentName: 'div',\n  icon: 'https://alifd.oss-cn-hangzhou.aliyuncs.com/fusion-cool/icons/icon-light/ic_light_button.png',\n  props: [\n    {\n      name: 'style',\n      title: '样式',\n      valueType: 'object',\n      setters: ['JSONSetter'],\n    },\n  ],\n  methods: [\n    {\n      name: 'doAlert',\n      params: [\n        {\n          name: 'msg',\n        },\n      ],\n      title: '打开警告提示',\n    },\n    {\n      name: 'doAlert2',\n      params: [\n        {\n          name: 'msg',\n          description: '警告内容',\n          tsType: 'string',\n          example: '请求失败',\n        },\n        {\n          name: 'count',\n          description: '提示次数',\n          tsType: 'number',\n          example: '1',\n        },\n      ],\n      title: '打开警告提示2',\n    },\n  ],\n  snippets: [\n    {\n      title: '基础元素',\n      snapshotText: 'Div',\n      category: 'HTML 元素',\n      schema: {\n        props: {},\n        children: ['I am a Div tag'],\n      },\n    },\n  ],\n};\n"
  },
  {
    "path": "packages/demo-page/src/material/row.ts",
    "content": "import { CMaterialType } from '@chamn/model';\n\nexport const RowMeta: CMaterialType = {\n  title: 'Row',\n  componentName: 'Row',\n  npm: {\n    package: '@chamn/mock-material',\n    exportName: 'Row',\n    version: '1.0.0',\n    name: 'Row',\n  },\n  icon: 'https://alifd.oss-cn-hangzhou.aliyuncs.com/fusion-cool/icons/icon-light/ic_light_button.png',\n  props: [\n    {\n      name: 'style',\n      title: '样式',\n      valueType: 'object',\n      setters: ['JSONSetter'],\n    },\n  ],\n  snippets: [\n    {\n      title: '行',\n      snapshot:\n        'https://alifd.oss-cn-hangzhou.aliyuncs.com/fusion-cool/icons/icon-light/ic_light_button.png',\n      category: 'HTML 元素',\n      schema: {\n        props: {},\n      },\n    },\n  ],\n  isContainer: true,\n};\n"
  },
  {
    "path": "packages/demo-page/src/material/table.ts",
    "content": "import { CMaterialType } from '@chamn/model';\n\nexport const TableMeta: CMaterialType = {\n  title: 'Table',\n  componentName: 'Table',\n  npm: {\n    name: 'MockMaterial',\n    package: '@chamn/mock-material',\n    exportName: 'Table',\n    version: '1.0.0',\n  },\n  props: [\n    {\n      name: 'name',\n      title: '表名',\n      valueType: 'string',\n      setters: [\n        'TextAreaSetter',\n        'StringSetter',\n        {\n          componentName: 'ArraySetter',\n          props: {\n            item: {\n              setters: [\n                {\n                  componentName: 'ShapeSetter',\n                  props: {\n                    elements: [\n                      {\n                        name: 'name',\n                        title: '列名',\n                        valueType: 'string',\n                        setters: ['StringSetter'],\n                      },\n                      {\n                        name: 'dataIndex',\n                        title: 'dataIndex',\n                        valueType: 'string',\n                        setters: ['StringSetter'],\n                      },\n                      {\n                        name: 'filteredValue',\n                        title: '过滤值',\n                        valueType: 'array',\n                        setters: ['StringSetter'],\n                      },\n                    ],\n                  },\n                  initialValue: {},\n                },\n                'StringSetter',\n                {\n                  componentName: 'SelectSetter',\n                  props: {\n                    options: [\n                      { value: 'jack', label: 'Jack' },\n                      { value: 'lucy', label: 'Lucy' },\n                      { value: 'Yiminghe', label: 'yiminghe' },\n                      { value: 'disabled', label: 'Disabled', disabled: true },\n                    ],\n                  },\n                },\n                'JSONSetter',\n                'FunctionSetter',\n                'ComponentSetter',\n              ],\n              initialValue: '',\n            },\n          },\n          initialValue: [],\n        },\n        {\n          componentName: 'ExpressionSetter',\n          initialValue: [],\n        },\n      ],\n    },\n    {\n      name: 'selectSetter',\n      title: 'select',\n      valueType: 'string',\n      setters: [\n        {\n          componentName: 'SelectSetter',\n          props: {\n            options: [\n              { value: 'jack', label: 'Jack' },\n              { value: 'lucy', label: 'Lucy' },\n              { value: 'Yiminghe', label: 'yiminghe' },\n              { value: 'disabled', label: 'Disabled', disabled: true },\n            ],\n          },\n        },\n      ],\n    },\n    {\n      name: 'testArrayString',\n      title: '数组字符串',\n      valueType: 'array',\n      setters: [\n        {\n          componentName: 'ArraySetter',\n          props: {\n            item: {\n              setters: [\n                'StringSetter',\n                'SelectSetter',\n                'JSONSetter',\n                'FunctionSetter',\n                'ComponentSetter',\n              ],\n              initialValue: '',\n            },\n          },\n          initialValue: [],\n        },\n        {\n          componentName: 'ExpressionSetter',\n          initialValue: [],\n        },\n        'StringSetter',\n        'SelectSetter',\n        'JSONSetter',\n        'FunctionSetter',\n        'ComponentSetter',\n      ],\n    },\n    {\n      name: 'pagination',\n      title: '分页',\n      valueType: 'object',\n      setters: [\n        {\n          componentName: 'ShapeSetter',\n          props: {\n            elements: [\n              {\n                name: 'current',\n                title: '当前页',\n                valueType: 'number',\n                setters: ['NumberSetter', 'ExpressionSetter'],\n              },\n            ],\n          },\n          initialValue: {},\n        },\n      ],\n    },\n    {\n      name: 'style',\n      title: '样式',\n      valueType: 'object',\n      setters: ['JSONSetter'],\n    },\n    {\n      name: 'columns',\n      title: '数据列',\n      valueType: 'array',\n      setters: [\n        {\n          componentName: 'ArraySetter',\n          props: {\n            item: {\n              setters: [\n                {\n                  componentName: 'ShapeSetter',\n                  props: {\n                    elements: [\n                      {\n                        name: 'title',\n                        title: '列名',\n                        valueType: 'string',\n                        setters: ['StringSetter', 'NumberSetter'],\n                      },\n                      {\n                        name: 'dataIndex',\n                        title: 'dataIndex',\n                        valueType: 'string',\n                        setters: ['StringSetter'],\n                      },\n                      {\n                        name: 'render',\n                        title: '渲染组件',\n                        valueType: 'component',\n                        setters: ['ComponentSetter'],\n                      },\n                      {\n                        name: 'filteredValue',\n                        title: '过滤值',\n                        valueType: 'array',\n                        setters: ['StringSetter'],\n                        condition: (state: any) => {\n                          if (state.dataIndex === '1') {\n                            return true;\n                          }\n                          return false;\n                        },\n                      },\n                    ],\n                  },\n                  initialValue: {},\n                },\n                'StringSetter',\n                'SelectSetter',\n                'JSONSetter',\n                'FunctionSetter',\n                'ComponentSetter',\n              ],\n              initialValue: {\n                title: 'Name',\n                dataIndex: 'Name',\n                filteredValue: '123',\n              },\n            },\n          },\n          initialValue: [],\n        },\n        {\n          componentName: 'ExpressionSetter',\n          initialValue: [],\n        },\n      ],\n    },\n\n    {\n      name: 'sorter',\n      title: '排序',\n      valueType: 'function',\n      setters: ['FunctionSetter', 'ExpressionSetter'],\n    },\n    {\n      name: 'title',\n      title: '表头',\n      valueType: 'component',\n      setters: ['ComponentSetter'],\n    },\n  ],\n  snippets: [\n    {\n      title: '表格',\n      snapshotText: 'Table',\n      category: '高级组件',\n      schema: {\n        props: {\n          data: [\n            {\n              key: '1',\n              name: 'John Brown',\n              age: 32,\n              address: 'New York No. 1 Lake Park',\n              tags: ['nice', 'developer'],\n            },\n          ],\n          columns: [\n            {\n              title: 'Name',\n              dataIndex: 'name',\n              key: 'name',\n            },\n          ],\n        },\n        children: ['I am a Div Table'],\n      },\n    },\n  ],\n};\n"
  },
  {
    "path": "packages/demo-page/src/pages/basePage-b-client.ts",
    "content": "import { CPageDataType } from '@chamn/model';\nconst data = [\n  {\n    key: '1',\n    name: 'John Brown',\n    age: 32,\n    address: 'New York No. 1 Lake Park',\n    tags: ['nice', 'developer'],\n  },\n  {\n    key: '2',\n    name: 'Jim Green',\n    age: 42,\n    address: 'London No. 1 Lake Park',\n    tags: ['loser'],\n  },\n  {\n    key: '3',\n    name: 'Joe Black',\n    age: 32,\n    address: 'Sidney No. 1 Lake Park',\n    tags: ['cool', 'teacher'],\n  },\n];\n\nconst columns = [\n  {\n    title: 'Name',\n    dataIndex: 'name',\n    key: 'name',\n    render: {\n      type: 'SLOT',\n      renderType: 'FUNC',\n      params: ['val', 'record', 'index'],\n      value: [\n        {\n          id: '5',\n          componentName: 'Row',\n          children: [\n            {\n              id: '6',\n              componentName: 'Button',\n              props: {\n                mark: 'nameRender',\n                children: {\n                  type: 'EXPRESSION',\n                  value: '$$context.params.val',\n                },\n              },\n            },\n            {\n              id: '7',\n              componentName: 'Col',\n              children: [\n                {\n                  componentName: 'Button',\n                  props: {\n                    mark: 'nameRender',\n                  },\n                  children: [\n                    {\n                      id: '8',\n                      componentName: 'div',\n                      children: ['I am div'],\n                    },\n                  ],\n                },\n              ],\n            },\n          ],\n        },\n        {\n          id: '99898999',\n          componentName: 'Button',\n          children: ['123'],\n        },\n      ],\n    },\n  },\n  {\n    title: 'Age',\n    dataIndex: 'age',\n    key: 'age',\n  },\n  {\n    title: 'Address',\n    dataIndex: 'address',\n    key: 'address',\n  },\n  {\n    title: 'Tags',\n    key: 'tags',\n    dataIndex: 'tags',\n  },\n  {\n    title: 'Action',\n    key: 'action',\n  },\n];\n\nexport const BasePageBClient: CPageDataType = {\n  version: '1.0.0',\n  name: 'BaseDemoPage',\n  componentsMeta: [],\n  componentsTree: {\n    id: '1',\n    componentName: 'RootContainer',\n    props: {\n      a: 1,\n    },\n    state: {\n      b: 1,\n      buttonVisible: true,\n      modalVisible: false,\n    },\n    children: [\n      {\n        id: 'row11111',\n        componentName: 'Row',\n      },\n      {\n        id: 'globalStateText',\n        componentName: 'div',\n        props: {\n          children: {\n            type: 'EXPRESSION',\n            value:\n              '\"rowState to reshow: \" + $$context.stateManager.RowState.state.rowMark',\n          },\n        },\n      },\n      {\n        id: 'div1',\n        componentName: 'div',\n        children: [\n          {\n            id: 'div1btn',\n            componentName: 'Button',\n            state: {\n              list: [11, 22, 33, 44, 55],\n            },\n            props: {\n              children: {\n                type: 'EXPRESSION',\n                value: '$$context.loopData.index',\n              },\n            },\n            loop: {\n              open: true,\n              data: {\n                type: 'EXPRESSION',\n                value: '$$context.state.list',\n              },\n              key: {\n                type: 'EXPRESSION',\n                value: '$$context.loopData.item',\n              },\n            },\n          },\n        ],\n      },\n      {\n        id: 'div12222',\n        componentName: 'div',\n        children: ['566666'],\n      },\n      {\n        id: 'Modal',\n        componentName: 'Modal',\n        refId: 'ModalRef',\n        props: {\n          open: {\n            type: 'EXPRESSION',\n            value: '$$context.globalState.modalVisible',\n          },\n          onCancel: {\n            type: 'FUNCTION',\n            value: `\n            function (a) {\n                $$context.updateGlobalState({\n                 modalVisible: false\n                });\n            }\n            `,\n          },\n        },\n        children: ['I am a modal'],\n      },\n      {\n        id: '999',\n        componentName: 'Button',\n        state: {\n          a: 1,\n        },\n        props: {\n          type: 'primary',\n          onClick: {\n            type: 'FUNCTION',\n            value: `function onClick(a) {\n              console.log(a);\n              $$context.updateState({a: $$context.state.a + 1})\n              $$context.updateGlobalState({\n                buttonVisible: !$$context.globalState.buttonVisible,\n                modalVisible: !$$context.globalState.modalVisible\n              })\n            }`,\n          },\n          children: ['控制右边按钮的显示隐藏'],\n        },\n      },\n      {\n        id: '2',\n        componentName: 'Button',\n        state: {\n          a: 1,\n        },\n        props: {\n          type: 'primary',\n          onClick: {\n            type: 'FUNCTION',\n            value: `function onClick(a) {\n              console.log(a);\n              $$context.updateState({a: $$context.state.a + 1})\n              $$context.updateGlobalState({ b: $$context.globalState.b + 1})\n            }`,\n          },\n          children: {\n            type: 'EXPRESSION',\n            value: '$$context.globalState.b',\n          },\n        },\n        condition: {\n          type: 'EXPRESSION',\n          value: '$$context.globalState.buttonVisible',\n        },\n      },\n\n      {\n        id: '3',\n        componentName: 'Table',\n        state: {\n          a: 3,\n          data: data,\n        },\n        props: {\n          name: 111,\n          testArrayString: [1, 2, 3, 4, 5],\n          columns,\n          dataSource: {\n            type: 'EXPRESSION',\n            value: '$$context.state.data',\n          },\n        },\n        configure: {},\n      },\n      {\n        id: '4',\n        componentName: 'Row',\n        state: {\n          rowMark: 1,\n        },\n        nodeName: 'RowState',\n        children: [\n          {\n            componentName: 'div',\n            props: {\n              children: {\n                type: 'EXPRESSION',\n                value:\n                  '\"rowState to reshow: \" + $$context.stateManager.RowState.state.rowMark',\n              },\n            },\n          },\n          {\n            id: '10',\n            componentName: 'Col',\n            children: [\n              {\n                id: '11',\n                componentName: 'Button',\n                children: ['123 木头人'],\n              },\n            ],\n          },\n          {\n            id: '12',\n            componentName: 'Input',\n            props: {\n              value: {\n                type: 'EXPRESSION',\n                value: '$$context.globalState.b',\n              },\n              onChange: {\n                type: 'FUNCTION',\n                value: `\n                  function(value) {\n                    console.log(value, $$context);\n                    $$context.updateGlobalState({\n                      b: value.target.value\n                    })\n                  }\n                `,\n              },\n            },\n          },\n          {\n            componentName: 'div',\n            children: [\n              '2222',\n              {\n                componentName: 'div',\n                children: [\n                  '1111',\n                  {\n                    componentName: 'Button',\n                    props: {\n                      onClick: {\n                        type: 'FUNCTION',\n                        value: `\n                          function (a) {\n                            console.log(a);\n                            const stateManager = $$context.stateManager;\n                            const state = stateManager.RowState.state;\n                            stateManager.RowState.updateState({\n                              rowMark: state.rowMark  +1\n                            })\n                          }\n                        `,\n                      },\n                    },\n                    children: ['change row state value'],\n                  },\n                ],\n              },\n            ],\n          },\n        ],\n      },\n    ],\n  },\n};\n"
  },
  {
    "path": "packages/demo-page/src/pages/basePage.ts",
    "content": "import { CPageDataType } from '@chamn/model';\n\nexport const BasePage: CPageDataType = {\n  version: '1.0.0',\n  name: 'BaseDemoPage',\n  componentsMeta: [],\n  componentsTree: {\n    componentName: 'RootContainer',\n    props: {\n      a: 1,\n    },\n    state: {\n      b: 2,\n      buttonVisible: true,\n      modalVisible: false,\n    },\n    configure: {\n      propsSetter: {},\n      advanceSetter: {},\n    },\n    children: [\n      {\n        props: {\n          width: '100px',\n          height: '100px',\n        },\n        componentName: 'CContainer',\n        id: 'ckakcd',\n        children: [\n          {\n            props: {\n              width: '100px',\n              height: '100px',\n            },\n            componentName: 'CContainer',\n            id: 'ef9vms',\n            children: [\n              {\n                props: {\n                  width: '100px',\n                  height: '100px',\n                  afterMount: {\n                    type: 'FUNCTION',\n                    value:\n                      \"function didRender() {\\n  const staticVar = $$context.getStaticVar();\\n  console.log('$$context', $$context)\\n  let setTimer = function () {\\n    const staticVar = $$context.getStaticVar();\\n    if (staticVar.timer) {\\n      clearInterval(staticVar.timer);\\n    }\\n    console.log('window.__CHAMN_RENDER_MODE ', window.__CHAMN_RENDER_MODE )\\n    if(window.__CHAMN_RENDER_MODE === 'DESIGN') {\\n      return;\\n    }\\n    const timer = setInterval(() => {\\n      const bannerStateObj = $$context.getStateObj();\\n      console.log('bannerStateObj', bannerStateObj)\\n      bannerStateObj.updateState((oldState) => {\\n        const newPage = (oldState.currentPage + 1) % 3;\\n        console.log('newPage', newPage, oldState)\\n        return {\\n          ...oldState,\\n          currentPage: newPage\\n        };\\n      })\\n    }, 2 * 1000);\\n    staticVar.timer = timer;\\n  }\\n\\n  staticVar.preScence = function leftClick(e) {\\n    const staticVar = $$context.getStaticVar();\\n    if (staticVar.timer) {\\n      clearInterval(staticVar.timer);\\n    }\\n\\n    const bannerStateObj = $$context.getStateObj();\\n    console.log('currentStateObj', bannerStateObj, $$context, bannerStateObj);\\n    if (bannerStateObj.state.currentPage === 0) {\\n      setTimer();\\n      return\\n    }\\n    const newPage = (bannerStateObj.state.currentPage - 1) % 3;\\n    bannerStateObj.updateState({\\n      currentPage: newPage\\n    });\\n    setTimer();\\n  };\\n\\n  staticVar.nextScence = function rightClick(e) {\\n    const staticVar = $$context.getStaticVar();\\n    if (staticVar.timer) {\\n      clearInterval(staticVar.timer);\\n    }\\n\\n    console.log($$context, e);\\n    const currentStateObj = $$context.getStateObj();\\n    console.log('currentStateObj', currentStateObj);\\n\\n    if (currentStateObj.state.currentPage === 2) {\\n      setTimer();\\n      return\\n    }\\n    const newPage = (currentStateObj.state.currentPage + 1) % 3\\n    currentStateObj.updateState({\\n      currentPage: newPage\\n    });\\n    setTimer();\\n  };\\n\\n  console.log('staticVar', staticVar)\\n\\n\\n  setTimer();\\n}\",\n                  },\n                  beforeDestroy: {\n                    type: 'FUNCTION',\n                    value:\n                      \"function beforeDestroy() {\\n  console.log('clear timer 1111');\\n  if ($$context.staticState.timer) {\\n    console.log('clear timer');\\n    clearInterval($$context.staticState.timer);\\n  }  \\n}\",\n                  },\n                  $$attributes: [{}],\n                },\n                componentName: 'CContainer',\n                id: '2vi5b1',\n                children: [\n                  {\n                    props: {\n                      width: '100px',\n                      height: '100px',\n                      $$attributes: [],\n                    },\n                    style: [\n                      {\n                        property: 'transform',\n                        value: {\n                          type: 'EXPRESSION',\n                          value:\n                            '`translateX(-${($$context.stateManager.bannerState.state.currentPage) * 100}%) translateZ(0) `',\n                        },\n                      },\n                    ],\n                    componentName: 'CContainer',\n                    id: '69079u',\n                    children: [\n                      {\n                        props: {\n                          width: '100px',\n                          height: '100px',\n                          $$attributes: [],\n                        },\n                        style: [\n                          {\n                            property: 'width',\n                            value: '100%',\n                          },\n                          {\n                            property: 'background-repeat',\n                            value: 'no-repeat',\n                          },\n                          {\n                            property: 'background-position',\n                            value: 'center',\n                          },\n                          {\n                            property: 'background-size',\n                            value: 'cover',\n                          },\n                          {\n                            property: 'flex-shrink',\n                            value: '0',\n                          },\n                          {\n                            property: 'height',\n                            value: '100%',\n                          },\n                          {\n                            property: 'Webkit-transform',\n                            value: 'translate3d(0, 0, 0)',\n                          },\n                          {\n                            property: 'background-image',\n                            value: {\n                              type: 'EXPRESSION',\n                              value: '`url(\"${$$context.loopData.item}\")`',\n                            },\n                          },\n                        ],\n                        componentName: 'CBlock',\n                        id: 'v59d71',\n                        configure: {\n                          propsSetter: {},\n                          advanceSetter: {\n                            'loop.data': {\n                              name: 'loop.data',\n                              setter: 'ExpressionSetter',\n                            },\n                          },\n                        },\n                        title: 'CBlock-1',\n                        loop: {\n                          open: true,\n                          data: {\n                            type: 'EXPRESSION',\n                            value:\n                              '$$context.stateManager.bannerState.state.imgList',\n                          },\n                          forName: 'item',\n                          forIndex: 'index',\n                          key: '',\n                          name: '',\n                        },\n                        condition: true,\n                        classNames: [\n                          {\n                            name: '123',\n                            status: {\n                              type: 'EXPRESSION',\n                              value:\n                                '$$context.stateManager.bannerState.state.currentPage',\n                            },\n                          },\n                          {\n                            name: 'leftBox',\n                            status: {\n                              type: 'EXPRESSION',\n                              value: 'true',\n                            },\n                          },\n                        ],\n                        css: {\n                          class: 'v59d71',\n                          value: [\n                            {\n                              state: 'normal',\n                              media: [\n                                {\n                                  type: 'max-width',\n                                  value: '991',\n                                },\n                              ],\n                            },\n                            {\n                              state: 'hover',\n                              media: [],\n                            },\n                          ],\n                        },\n                      },\n                    ],\n                    configure: {\n                      propsSetter: {},\n                      advanceSetter: {},\n                    },\n                    title: 'banner-box',\n                    loop: {\n                      open: false,\n                      data: [],\n                      forName: 'item',\n                      forIndex: 'index',\n                      key: '',\n                      name: '',\n                    },\n                    condition: true,\n                    refId: 'bannerBox',\n                    css: {\n                      class: 'c_69079u',\n                      value: [\n                        {\n                          state: 'normal',\n                          media: [],\n                          text: '\\n                          display: flex;\\n                          width: 100%;\\n                          position: absolute;\\n                          transition: all 0.3s;\\n                          height: 100%;\\n                          Webkit-backface-visibility: hidden\\n                          ',\n                        },\n                      ],\n                    },\n                  },\n                  {\n                    props: {\n                      width: '',\n                      height: '',\n                      $$attributes: [\n                        {\n                          key: 'onClick',\n                          value: {\n                            type: 'FUNCTION',\n                            value:\n                              \"function leftClick(e) {\\n  const variableSpace =  $$context.getStaticVarById('bannerState')\\n  console.log('variableSpace', variableSpace);\\n  variableSpace.preScence();\\n}\",\n                          },\n                        },\n                      ],\n                      children: {\n                        type: 'EXPRESSION',\n                        value: '',\n                      },\n                    },\n                    componentName: 'CBlock',\n                    id: '9g9ohd',\n                    configure: {\n                      propsSetter: {\n                        '$$attributes.0.value': {\n                          name: '$$attributes.0.value',\n                          setter: 'FunctionSetter',\n                        },\n                        children: {\n                          name: 'children',\n                          setter: 'ExpressionSetter',\n                        },\n                      },\n                      advanceSetter: {},\n                    },\n                    title: 'array-left',\n                    css: {\n                      class: 'c_9g9ohd',\n                      value: [\n                        {\n                          state: 'normal',\n                          media: [],\n                          text: 'width: 50px;height: 50px;background-color: rgba(0,0,0,0.5);position: absolute;z-index: 999;top: 50%;transform: translateY(-50%);cursor: pointer;border-radius: 4px;left: 10px;',\n                        },\n                      ],\n                    },\n                  },\n                  {\n                    css: {\n                      class: 'c_je9fi5',\n                      value: [\n                        {\n                          state: 'normal',\n                          media: [],\n                          text: 'width: 50px;height: 50px;background-color: rgba(0,0,0,0.5);position: absolute;z-index: 999;top: 50%;transform: translateY(-50%);right: 10px;cursor: pointer;border-radius: 4px; ',\n                        },\n                      ],\n                    },\n                    props: {\n                      width: '',\n                      height: '',\n                      $$attributes: [\n                        {\n                          key: 'onClick',\n                          value: {\n                            type: 'FUNCTION',\n                            value:\n                              \"function rightClick(e) {\\n\\n  const variableSpace =  $$context.getStaticVarById('bannerState')\\n  console.log('variableSpace', variableSpace);\\n  variableSpace.nextScence();\\n}\",\n                          },\n                        },\n                      ],\n                    },\n                    componentName: 'CBlock',\n                    configure: {\n                      propsSetter: {\n                        '$$attributes.0.value': {\n                          name: '$$attributes.0.value',\n                          setter: 'FunctionSetter',\n                        },\n                      },\n                      advanceSetter: {},\n                    },\n                    title: 'array-right',\n                    id: 'je9fi5',\n                  },\n                  {\n                    style: [\n                      {\n                        value: 'absolute',\n                        property: 'position',\n                      },\n                      {\n                        value: '10px',\n                        property: 'bottom',\n                      },\n                      {\n                        value: '50%',\n                        property: 'left',\n                      },\n                      {\n                        value: 'translateX(-50%)',\n                        property: 'transform',\n                      },\n                      {\n                        value: '20px',\n                        property: 'height',\n                      },\n                      {\n                        value: 'flex',\n                        property: 'display',\n                      },\n                      {\n                        value: 'center',\n                        property: 'align-items',\n                      },\n                      {\n                        value: 'rgba(0,0,0,0.5)',\n                        property: 'background-color',\n                      },\n                      {\n                        value: '10px',\n                        property: 'border-radius',\n                      },\n                      {\n                        value: '0 10px',\n                        property: 'padding',\n                      },\n                      {\n                        value: 'space-around',\n                        property: 'justify-content',\n                      },\n                      {\n                        value: '80px',\n                        property: 'width',\n                      },\n                    ],\n                    props: {\n                      width: '100px',\n                      height: '100px',\n                    },\n                    componentName: 'CContainer',\n                    id: 'bl87pg',\n                    children: [\n                      {\n                        props: {\n                          width: '',\n                          height: '',\n                          $$attributes: [\n                            {\n                              key: 'onClick',\n                              value: {\n                                type: 'FUNCTION',\n                                value:\n                                  'function click() {\\n  console.log($$context.loopData);\\n}',\n                              },\n                            },\n                          ],\n                        },\n                        componentName: 'CBlock',\n                        id: 'jn98v0',\n                        configure: {\n                          propsSetter: {\n                            '$$attributes.0.value': {\n                              name: '$$attributes.0.value',\n                              setter: 'FunctionSetter',\n                            },\n                            children: {\n                              name: 'children',\n                              setter: 'ExpressionSetter',\n                            },\n                          },\n                          advanceSetter: {\n                            'loop.data': {\n                              name: 'loop.data',\n                              setter: 'ExpressionSetter',\n                            },\n                          },\n                        },\n                        loop: {\n                          open: true,\n                          data: {\n                            type: 'EXPRESSION',\n                            value:\n                              '$$context.stateManager.bannerState.state.imgList',\n                          },\n                          forName: 'item',\n                          forIndex: 'index',\n                          key: '',\n                          name: '',\n                        },\n                        condition: true,\n                        children: [\n                          {\n                            style: [\n                              {\n                                value: {\n                                  type: 'EXPRESSION',\n                                  value:\n                                    \"$$context.stateManager.bannerState.state.currentPage === $$context.loopData.index ? 'white' : 'rgba(0,0,0,0.3)'\",\n                                },\n                                property: 'background',\n                              },\n                            ],\n                            props: {\n                              width: '',\n                              height: '',\n                              $$attributes: [\n                                {\n                                  key: 'onClick',\n                                  value: {\n                                    type: 'FUNCTION',\n                                    value:\n                                      'function click() {\\n  console.log(222, $$context.loopData);\\n}',\n                                  },\n                                },\n                              ],\n                              children: {\n                                type: 'EXPRESSION',\n                                value: '',\n                              },\n                            },\n                            componentName: 'CBlock',\n                            configure: {\n                              propsSetter: {\n                                '$$attributes.0.value': {\n                                  name: '$$attributes.0.value',\n                                  setter: 'FunctionSetter',\n                                },\n                                children: {\n                                  name: 'children',\n                                  setter: 'ExpressionSetter',\n                                },\n                              },\n                              advanceSetter: {\n                                'loop.data': {\n                                  name: 'loop.data',\n                                  setter: 'ExpressionSetter',\n                                },\n                              },\n                            },\n                            loop: {\n                              open: false,\n                              data: {\n                                type: 'EXPRESSION',\n                                value: '',\n                              },\n                              forName: 'item',\n                              forIndex: 'index',\n                              key: '',\n                              name: '',\n                            },\n                            condition: true,\n                            id: '5mu9jm',\n                            css: {\n                              class: 'c_5mu9jm',\n                              value: [\n                                {\n                                  state: 'normal',\n                                  media: [],\n                                  text: 'border-radius: 4px;width: 10px;height: 10px; ',\n                                },\n                              ],\n                            },\n                          },\n                        ],\n                        css: {\n                          class: 'c_jn98v0',\n                          value: [\n                            {\n                              state: 'normal',\n                              media: [],\n                              text: 'width: 10px;height: 10px;background-color: rgba(200,200,200,0.5);border-radius: 4px; ',\n                            },\n                          ],\n                        },\n                      },\n                    ],\n                    configure: {\n                      propsSetter: {},\n                      advanceSetter: {},\n                    },\n                    loop: {\n                      open: false,\n                      data: [],\n                      forName: 'item',\n                      forIndex: 'index',\n                      key: '',\n                      name: '',\n                    },\n                    condition: true,\n                    title: 'CContainer-thumbail',\n                    css: {\n                      class: 'c_bl87pg',\n                      value: [\n                        {\n                          state: 'normal',\n                          media: [],\n                          text: 'animation-duration: 1;',\n                        },\n                      ],\n                    },\n                  },\n                ],\n                configure: {\n                  propsSetter: {},\n                  advanceSetter: {},\n                },\n                state: {\n                  currentPage: 1,\n                  imgList: [\n                    'https://images.unsplash.com/photo-1584080277544-2db5b2c2d9dd?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1470&q=80',\n                    'https://images.unsplash.com/photo-1486046866764-e426b5b93d98?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2091&q=80',\n                    'https://images.unsplash.com/photo-1534803005787-fa0b3987f6fc?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1502&q=80',\n                  ],\n                },\n                loop: {\n                  open: false,\n                  data: [],\n                  forName: 'item',\n                  forIndex: 'index',\n                  key: '',\n                  name: '',\n                },\n                condition: true,\n                nodeName: 'bannerState',\n                css: {\n                  class: 'c_2vi5b1',\n                  value: [\n                    {\n                      state: 'normal',\n                      media: [],\n                      text: 'height: 500px;position: relative;width: 100%;overflow: hidden;margin: 0 auto;border-radius: 10px; ',\n                    },\n                  ],\n                },\n              },\n            ],\n            configure: {\n              propsSetter: {},\n              advanceSetter: {},\n            },\n            title: 'CContainer-bg',\n            css: {\n              class: 'ef9vms',\n              value: [\n                {\n                  state: 'normal',\n                  media: [],\n                  text: 'padding: 20px 40px;',\n                },\n              ],\n            },\n          },\n          {\n            props: {\n              content: 'Hello Chamelon EG',\n            },\n            componentName: 'CText',\n            id: 'qpbnqn',\n            configure: {\n              propsSetter: {},\n              advanceSetter: {},\n            },\n            classNames: [\n              {\n                name: '',\n                status: {\n                  type: 'EXPRESSION',\n                  value: 'true',\n                },\n              },\n            ],\n            css: {\n              class: 'c_qpbnqn',\n              value: [\n                {\n                  state: 'normal',\n                  media: [\n                    {\n                      type: 'max-width',\n                      value: '767',\n                      text: ' color: pink;',\n                    },\n                    {\n                      type: 'max-width',\n                      value: '991',\n                    },\n                  ],\n                  text: 'text-align: center;width: 100%;display: inline-block;font-size: 80px;padding: 20px;box-sizing: border-box;font-weight: bold;background-image: linear-gradient(         45deg,         #CA4246 16.666%,          #E16541 16.666%,          #E16541 33.333%,          #F18F43 33.333%,          #F18F43 50%,          #8B9862 50%,          #8B9862 66.666%,          #476098 66.666%,          #476098 83.333%,          #A7489B 83.333%);background-color: #CA4246;background-size: 100%;background-repeat: repeat;color: transparent;-webkit-background-clip: text; ',\n                },\n              ],\n            },\n          },\n          {\n            style: [\n              {\n                value: '20px 40px',\n                property: 'margin',\n              },\n              {\n                value: '20px',\n                property: 'border-radius',\n              },\n              {\n                value: 'hidden',\n                property: 'overflow',\n              },\n              {\n                value: '2px 2px 5px rgba(0,0,0,0.2)',\n                property: 'box-shadow',\n              },\n              {\n                value: 'hidden',\n                property: 'Webkit-backface-visibility',\n              },\n            ],\n            props: {\n              width: '100px',\n              height: '100px',\n            },\n            componentName: 'CContainer',\n            id: 'ekv045',\n            children: [\n              {\n                props: {\n                  width: '100%',\n                  height: '',\n                  src: 'https://vjs.zencdn.net/v/oceans.mp4',\n                  autoPlay: '',\n                  controls: true,\n                  $$attributes: [],\n                },\n                componentName: 'CVideo',\n                id: 'vu26ll',\n                configure: {\n                  propsSetter: {\n                    autoplay: {\n                      name: 'autoplay',\n                      setter: 'ExpressionSetter',\n                    },\n                    autoPlay: {\n                      name: 'autoPlay',\n                      setter: 'BooleanSetter',\n                    },\n                  },\n                  advanceSetter: {},\n                },\n                css: {\n                  class: 'c_vu26ll',\n                  value: [\n                    {\n                      state: 'normal',\n                      media: [],\n                      text: 'margin: auto;display: block; ',\n                    },\n                  ],\n                },\n              },\n            ],\n            configure: {\n              propsSetter: {},\n              advanceSetter: {},\n            },\n            title: 'video-container',\n          },\n        ],\n        configure: {\n          propsSetter: {},\n          advanceSetter: {},\n        },\n        title: 'bg-CContainer',\n        classNames: [],\n        css: {\n          class: 'ckakcd',\n          value: [\n            {\n              state: 'normal',\n              media: [],\n              text: 'background-color: white;width: 100%;overflow: auto; ',\n            },\n          ],\n        },\n      },\n    ],\n  },\n  thirdLibs: [\n    {\n      package: 'dayjs',\n      name: 'dayjs',\n      version: '1.0.0',\n    },\n    {\n      package: 'antd',\n      name: 'antd',\n      version: '1.0.0',\n    },\n  ],\n  assets: [\n    {\n      package: 'dayjs',\n      globalName: 'dayjs',\n      resources: [\n        {\n          src: 'https://cdn.jsdelivr.net/npm/dayjs@1.11.12/dayjs.min.js',\n        },\n      ],\n    },\n  ],\n};\n"
  },
  {
    "path": "packages/demo-page/src/pages/emptyPage.ts",
    "content": "import { CPageDataType, InnerComponentNameEnum } from '@chamn/model';\n\nexport const EmptyPage: CPageDataType = {\n  version: '1.0.0',\n  name: 'EmptyPage',\n  componentsMeta: [],\n  componentsTree: {\n    componentName: InnerComponentNameEnum.ROOT_CONTAINER,\n    props: {},\n    children: [],\n  },\n};\n"
  },
  {
    "path": "packages/demo-page/src/pages/layout.ts",
    "content": "import { CPageDataType } from '@chamn/model';\n\nexport const LayoutPage: CPageDataType = {\n  version: '1.0.0',\n  name: 'LayoutPage',\n  componentsMeta: [\n    {\n      componentName: 'GridItem',\n      name: 'GridItem',\n      package: '@chamn/material',\n      version: '0.0.46',\n      destructuring: true,\n      exportName: 'GridItem',\n    },\n    {\n      componentName: 'GridLayout',\n      name: 'GridLayout',\n      package: '@chamn/material',\n      version: '0.0.46',\n      destructuring: true,\n      exportName: 'GridLayout',\n    },\n  ],\n  componentsTree: {\n    componentName: 'RootContainer',\n    props: {\n      a: 1,\n    },\n    state: {\n      b: 2,\n      buttonVisible: true,\n      modalVisible: false,\n    },\n    configure: {\n      propsSetter: {},\n      advanceSetter: {},\n    },\n    children: [\n      {\n        props: {\n          breakpoints: [\n            {\n              w: 350,\n              label: 'mobile',\n            },\n            {\n              w: 768,\n              label: 'xs',\n            },\n            {\n              w: 992,\n              label: 'sm',\n            },\n            {\n              w: 1200,\n              label: 'md',\n            },\n            {\n              w: 1920,\n              label: 'lg',\n            },\n            {\n              w: 3600,\n              label: 'xl',\n            },\n          ],\n        },\n        componentName: 'GridLayout',\n        id: 'hps09b',\n        children: [\n          {\n            props: {\n              responsive: [\n                {\n                  label: 'lg',\n                  info: {\n                    x: 0,\n                    y: 11,\n                    w: 14,\n                    h: 3,\n                  },\n                  x: '',\n                  y: '',\n                },\n                {\n                  label: 'md',\n                  info: {\n                    x: 0,\n                    y: 4,\n                    w: 24,\n                    h: 2,\n                  },\n                  x: '',\n                  y: '',\n                },\n                {\n                  label: 'xs',\n                  info: {\n                    x: 6,\n                    y: 7,\n                    w: 14,\n                    h: 3,\n                  },\n                },\n                {\n                  label: 'sm',\n                  info: {\n                    x: 0,\n                    y: 11,\n                    w: 14,\n                    h: 3,\n                  },\n                },\n              ],\n            },\n            componentName: 'GridItem',\n            id: 'vnuvg0',\n            configure: {\n              propsSetter: {},\n              advanceSetter: {},\n            },\n            style: [\n              {\n                property: 'background-color',\n                value: 'rgb(139,187,17)',\n              },\n              {\n                property: 'background-image',\n                value: '',\n              },\n              {\n                property: 'background-size',\n                value: '',\n              },\n              {\n                property: 'background-repeat',\n                value: '',\n              },\n            ],\n          },\n          {\n            props: {\n              responsive: [\n                {\n                  label: 'lg',\n                  info: {\n                    x: 9,\n                    y: 7,\n                    w: 15,\n                    h: 4,\n                  },\n                  x: '',\n                  y: '',\n                },\n                {\n                  label: 'md',\n                  info: {\n                    x: 9,\n                    y: 6,\n                    w: 15,\n                    h: 3,\n                  },\n                  x: '',\n                  y: '',\n                },\n                {\n                  label: 'xs',\n                  info: {\n                    x: 5,\n                    y: 5,\n                    w: 16,\n                    h: 2,\n                  },\n                },\n                {\n                  label: 'sm',\n                  info: {\n                    x: 9,\n                    y: 7,\n                    w: 15,\n                    h: 4,\n                  },\n                },\n              ],\n            },\n            componentName: 'GridItem',\n            configure: {\n              propsSetter: {},\n              advanceSetter: {},\n            },\n            id: 'pl20rg',\n            style: [\n              {\n                property: 'background-color',\n                value: 'rgb(250,140,22)',\n              },\n              {\n                property: 'background-image',\n                value: '',\n              },\n              {\n                property: 'background-size',\n                value: '',\n              },\n              {\n                property: 'background-repeat',\n                value: '',\n              },\n            ],\n          },\n          {\n            props: {\n              responsive: [\n                {\n                  label: 'lg',\n                  info: {\n                    x: 0,\n                    y: 5,\n                    w: 13,\n                    h: 2,\n                  },\n                  x: '',\n                  y: '',\n                },\n                {\n                  label: 'md',\n                  info: {\n                    x: 0,\n                    y: 9,\n                    w: 13,\n                    h: 5,\n                  },\n                  x: '',\n                  y: '',\n                },\n                {\n                  label: 'xs',\n                  info: {\n                    x: 4,\n                    y: 3,\n                    w: 18,\n                    h: 2,\n                  },\n                },\n                {\n                  label: 'sm',\n                  info: {\n                    x: 0,\n                    y: 5,\n                    w: 13,\n                    h: 2,\n                  },\n                },\n              ],\n            },\n            componentName: 'GridItem',\n            configure: {\n              propsSetter: {},\n              advanceSetter: {},\n            },\n            id: 'b7l87q',\n            style: [\n              {\n                property: 'background-color',\n                value: 'rgb(114,46,209)',\n              },\n              {\n                property: 'background-image',\n                value: '',\n              },\n              {\n                property: 'background-size',\n                value: '',\n              },\n              {\n                property: 'background-repeat',\n                value: '',\n              },\n            ],\n          },\n          {\n            props: {\n              responsive: [\n                {\n                  label: 'lg',\n                  info: {\n                    x: 0,\n                    y: 0,\n                    w: 24,\n                    h: 3,\n                  },\n                  x: '',\n                  y: '',\n                },\n                {\n                  label: 'md',\n                  info: {\n                    x: 0,\n                    y: 0,\n                    w: 24,\n                    h: 2,\n                  },\n                  x: '',\n                  y: '',\n                },\n                {\n                  label: 'xs',\n                  info: {\n                    x: 0,\n                    y: 0,\n                    w: 24,\n                    h: 3,\n                  },\n                },\n                {\n                  label: 'sm',\n                  info: {\n                    x: 0,\n                    y: 0,\n                    w: 24,\n                    h: 3,\n                  },\n                },\n              ],\n            },\n            componentName: 'GridItem',\n            configure: {\n              propsSetter: {},\n              advanceSetter: {},\n            },\n            id: 'egib67',\n            style: [\n              {\n                property: 'background-color',\n                value: 'rgb(245,34,45)',\n              },\n              {\n                property: 'background-image',\n                value: '',\n              },\n              {\n                property: 'background-size',\n                value: '',\n              },\n              {\n                property: 'background-repeat',\n                value: '',\n              },\n            ],\n          },\n        ],\n        configure: {\n          propsSetter: {},\n          advanceSetter: {},\n        },\n      },\n    ],\n  },\n  thirdLibs: [\n    {\n      package: 'dayjs',\n      name: 'dayjs',\n      version: '1.0.0',\n    },\n    {\n      package: 'antd',\n      name: 'antd',\n      version: '1.0.0',\n    },\n  ],\n  assets: [\n    {\n      package: '@chamn/material',\n      globalName: 'ChamnCommonComponents',\n      resources: [\n        {\n          src: 'https://cdn.jsdelivr.net/npm/@chamn/material/dist/index.js',\n        },\n        {\n          src: 'https://cdn.jsdelivr.net/npm/@chamn/material/dist/style.css',\n        },\n      ],\n    },\n  ],\n};\n"
  },
  {
    "path": "packages/demo-page/src/pages/simplePage.ts",
    "content": "import {\n  CNodePropsTypeEnum,\n  CPageDataType,\n  LogicType,\n  TLogicAssignValueItem,\n  TLogicJumpLinkItem,\n} from '@chamn/model';\n\nexport const SamplePage: CPageDataType = {\n  version: '1.0.0',\n  name: 'BaseDemoPage',\n  componentsMeta: [],\n  componentsTree: {\n    id: '1',\n    componentName: 'RootContainer',\n    props: {\n      a: 1,\n    },\n    children: [\n      {\n        id: 'globalStateText',\n        componentName: 'div',\n        injectEnvList: ['COMPONENTS'],\n        props: {\n          children: {\n            type: 'EXPRESSION',\n            value:\n              '\"rowState to reshow: \" + $$context.stateManager.RowState.state.rowMark',\n          },\n        },\n      },\n      {\n        componentName: 'Card',\n        state: {\n          link: 'https://www.baidu2.com/state',\n        },\n        props: {\n          onClick1: {\n            type: 'FUNCTION',\n            value: `\n              function (a) {\n                console.log(a, $$context);\n                const stateManager = $$context.stateManager;\n                const methods = $$context.getMethods();\n                console.log(\"🚀 ~ methods:\", methods)\n                const state = stateManager.RowState.state;\n                stateManager.RowState.updateState({\n                  rowMark: state.rowMark  +1\n                })\n              }\n            `,\n          },\n          onClick: {\n            type: CNodePropsTypeEnum.ACTION,\n            handler: [\n              {\n                id: 1,\n                type: 'RUN_CODE',\n                sourceCode: '',\n                value: `\n                    console.log('12321312');\n                    return 555;\n                  `,\n                next: 2,\n              },\n              {\n                id: 2,\n                next: 3,\n                type: 'JUMP_LINK',\n                link: 'https://www.baidu.com',\n              },\n              {\n                type: 'JUMP_LINK',\n                link: {\n                  type: 'EXPRESSION',\n                  value: '$$context.state.link',\n                },\n              } as TLogicJumpLinkItem,\n              {\n                id: '3',\n                next: '4',\n                type: 'JUMP_LINK',\n                link: {\n                  type: 'FUNCTION',\n                  sourceCode: `function () {\n                    return $$context.state.link;\n                  }`,\n                  value: `function () {\n                  console.log('jump3');\n                    return $$context.state.link;\n                  }`,\n                },\n              } as TLogicJumpLinkItem,\n              {\n                id: 4,\n                type: LogicType.REQUEST_API,\n                apiPath: {\n                  type: 'FUNCTION',\n                  value: `\n                    function () {\n                      console.log($$context);\n                      return 'https://www.api.com'\n                    }\n                  `,\n                },\n                body: {\n                  a: '123',\n                  f: 456,\n                  b: {\n                    type: 'EXPRESSION',\n                    value: '$$context.state.link',\n                  },\n                  c: {\n                    type: 'FUNCTION',\n                    sourceCode: `function () {\n                    return $$context.state.link;\n                  }`,\n                    value: `function () {\n                    return $$context.state.link;\n                  }`,\n                  },\n                },\n                responseVarName: 'APIResult',\n                afterSuccessResponse: [\n                  {\n                    id: 't1',\n                    next: 't2',\n                    type: 'RUN_CODE',\n                    value: `\n                      console.log($$context, $$response);\n                      console.log(77889999, apiResult)\n                  `,\n                  },\n                  {\n                    id: 't2',\n                    next: 't3',\n                    type: 'CALL_NODE_METHOD',\n                    nodeId: 'testNode',\n                    methodName: 'sayHello',\n                    args: [\n                      123,\n                      {\n                        type: 'EXPRESSION',\n                        value: '$$context.state.a',\n                      },\n                      {\n                        type: 'FUNCTION',\n                        value: `\n                        function (apiResult) {\n                          console.log('apiResult 99999', apiResult, $$response);\n                          return 890;\n                        }\n                      `,\n                      },\n                    ],\n                    returnVarName: 'callNodeReturnVar',\n                  },\n                  {\n                    type: 'RUN_CODE',\n                    id: 't3',\n                    next: 't4',\n                    value: `\n                      console.log(9898989, apiResult, $$context, $$response, $$actionVariableSpace);\n                      console.log(77889999,callNodeReturnVar, APIResult)\n                  `,\n                  },\n                  {\n                    id: 't4',\n                    next: 't5',\n                    type: LogicType.ASSIGN_VALUE,\n                    valueType: 'STATE',\n                    currentValue: {\n                      type: 'EXPRESSION',\n                      value: 'APIResult',\n                    },\n                    targetValueName: {\n                      nodeId: 'xxx',\n                      keyPath: 'apiResult',\n                    },\n                  } as TLogicAssignValueItem,\n                  {\n                    id: 't5',\n                    next: 't6',\n                    type: LogicType.ASSIGN_VALUE,\n                    valueType: 'MEMORY',\n                    currentValue: {\n                      type: 'FUNCTION',\n                      value: `function (apiResult) {\n                        console.log(6677, apiResult, APIResult);\n                        return apiResult;\n                      }\n                      `,\n                    },\n                    targetValueName: 'tempApiResult1',\n                  } as TLogicAssignValueItem,\n                ],\n                afterFailedResponse: [\n                  {\n                    type: 'RUN_CODE',\n                    id: 't7',\n                    next: 't8',\n                    value: `\n                      console.log($$context, $$response);\n                      console.log('errrrror', apiResult)\n                  `,\n                  },\n                ],\n              },\n            ],\n          },\n        },\n        eventListener: [\n          {\n            name: 'ON_DID_RENDER',\n            func: {\n              type: CNodePropsTypeEnum.ACTION,\n              handler: [\n                {\n                  id: '12',\n                  type: 'RUN_CODE',\n                  value: `\n                    console.log('12321312', $$context, $PARAMS);\n                    console.log('Component Did Mount')\n                    return 'Component Did Mount'\n                `,\n                },\n              ],\n            },\n          },\n          {\n            name: 'ON_WILL_DESTROY',\n            func: {\n              type: CNodePropsTypeEnum.ACTION,\n              handler: [\n                {\n                  id: '123',\n                  type: 'RUN_CODE',\n                  value: `\n                    console.log('12321312', $$context, params);\n                    console.log('Component ON_WILL_DESTROY')\n                    return 'Component ON_WILL_DESTROY'\n                `,\n                },\n              ],\n            },\n          },\n        ],\n        methods: [\n          {\n            name: 'getAge',\n            type: 'FUNCTION',\n            value: `function getAge() {\n            console.log(12);\n            }`,\n          },\n        ],\n        children: ['Action login flow Demo'],\n      },\n      {\n        id: 'testNode',\n        componentName: 'Button',\n        props: {\n          onClick: {\n            type: 'FUNCTION',\n            value: `\n              function (a) {\n                console.log(a, $$context);\n                const stateManager = $$context.stateManager;\n                const methods = $$context.getMethods();\n                console.log(\"🚀 ~ methods:\", methods)\n                const state = stateManager.RowState.state;\n                stateManager.RowState.updateState({\n                  rowMark: state.rowMark  +1\n                })\n              }\n            `,\n          },\n        },\n        methods: [\n          {\n            name: 'getAge',\n            type: 'FUNCTION',\n            value: `function getAge() {\n            console.log(12);\n            }`,\n          },\n        ],\n        children: ['change row state value'],\n      },\n      {\n        id: '4',\n        componentName: 'Row',\n        state: {\n          rowMark: 1,\n        },\n        nodeName: 'RowState',\n        children: [\n          {\n            componentName: 'div',\n            props: {\n              children: {\n                type: 'EXPRESSION',\n                value:\n                  '\"rowState to reshow: \" + $$context.stateManager.RowState.state.rowMark',\n              },\n            },\n          },\n        ],\n      },\n    ],\n  },\n};\n"
  },
  {
    "path": "packages/demo-page/src/vite-env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "packages/demo-page/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ESNext\",\n    \"useDefineForClassFields\": true,\n    \"lib\": [\n      \"DOM\",\n      \"DOM.Iterable\",\n      \"ESNext\"\n    ],\n    \"allowJs\": false,\n    \"skipLibCheck\": true,\n    \"esModuleInterop\": false,\n    \"allowSyntheticDefaultImports\": true,\n    \"strict\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": false,\n    \"noEmit\": true,\n    \"jsx\": \"react\"\n  },\n  \"include\": [\n    \"src\"\n  ],\n}"
  },
  {
    "path": "packages/docs-app/.gitignore",
    "content": "# build output\ndist/\n# generated types\n.astro/\n\n# dependencies\nnode_modules/\n\n# logs\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\n\n\n# environment variables\n.env\n.env.production\n\n# macOS-specific files\n.DS_Store\n"
  },
  {
    "path": "packages/docs-app/.vscode/extensions.json",
    "content": "{\n  \"recommendations\": [\"astro-build.astro-vscode\"],\n  \"unwantedRecommendations\": []\n}\n"
  },
  {
    "path": "packages/docs-app/.vscode/launch.json",
    "content": "{\n  \"version\": \"0.2.0\",\n  \"configurations\": [\n    {\n      \"command\": \"./node_modules/.bin/astro dev\",\n      \"name\": \"Development server\",\n      \"request\": \"launch\",\n      \"type\": \"node-terminal\"\n    }\n  ]\n}\n"
  },
  {
    "path": "packages/docs-app/CHANGELOG.md",
    "content": "# Change Log\n\nAll notable changes to this project will be documented in this file.\nSee [Conventional Commits](https://conventionalcommits.org) for commit guidelines.\n\n## [0.10.4](https://github.com/ByteCrazy/chameleon/compare/v0.10.3...v0.10.4) (2026-01-17)\n\n**Note:** Version bump only for package docs-app\n\n## [0.10.3](https://github.com/ByteCrazy/chameleon/compare/v0.10.2...v0.10.3) (2026-01-17)\n\n**Note:** Version bump only for package docs-app\n\n## [0.10.2](https://github.com/ByteCrazy/chameleon/compare/v0.10.1...v0.10.2) (2026-01-17)\n\n**Note:** Version bump only for package docs-app\n\n## [0.10.1](https://github.com/ByteCrazy/chameleon/compare/v0.10.0...v0.10.1) (2026-01-11)\n\n**Note:** Version bump only for package docs-app\n\n## [0.10.0](https://github.com/hlerenow/chameleon/compare/v0.9.3...v0.10.0) (2025-12-25)\n\n### 📝 Documentation | 文档\n\n* 添加 GridLayout 代码示例和插件开发文档 ([cade905](https://github.com/hlerenow/chameleon/commit/cade90591efff604a8ab3395c905c4f713ba2b93))\n\n## [0.9.3](https://github.com/ByteCrazy/chameleon/compare/v0.9.2...v0.9.3) (2025-07-20)\n\n**Note:** Version bump only for package docs-app\n\n## [0.9.2](https://github.com/ByteCrazy/chameleon/compare/v0.9.1...v0.9.2) (2025-07-13)\n\n**Note:** Version bump only for package docs-app\n\n## [0.9.1](https://github.com/ByteCrazy/chameleon/compare/v0.9.0...v0.9.1) (2025-07-13)\n\n**Note:** Version bump only for package docs-app\n\n## [0.9.0](https://github.com/ByteCrazy/chameleon/compare/v0.8.6...v0.9.0) (2025-07-13)\n\n**Note:** Version bump only for package docs-app\n\n## [0.8.6](https://github.com/ByteCrazy/chameleon/compare/v0.8.5...v0.8.6) (2025-06-21)\n\n**Note:** Version bump only for package docs-app\n\n## [0.8.5](https://github.com/ByteCrazy/chameleon/compare/v0.8.4...v0.8.5) (2025-04-13)\n\n**Note:** Version bump only for package docs-app\n\n## [0.8.4](https://github.com/ByteCrazy/chameleon/compare/v0.8.3...v0.8.4) (2025-04-13)\n\n**Note:** Version bump only for package docs-app\n\n## [0.8.3](https://github.com/ByteCrazy/chameleon/compare/v0.8.2...v0.8.3) (2025-04-12)\n\n**Note:** Version bump only for package docs-app\n\n## [0.8.2](https://github.com/ByteCrazy/chameleon/compare/v0.8.1...v0.8.2) (2025-04-11)\n\n**Note:** Version bump only for package docs-app\n\n## [0.8.1](https://github.com/ByteCrazy/chameleon/compare/v0.8.0...v0.8.1) (2025-04-10)\n\n**Note:** Version bump only for package docs-app\n\n## [0.8.0](https://github.com/ByteCrazy/chameleon/compare/v0.7.0...v0.8.0) (2025-04-10)\n\n**Note:** Version bump only for package docs-app\n\n## [0.7.0](https://github.com/ByteCrazy/chameleon/compare/v0.6.0...v0.7.0) (2025-04-06)\n\n**Note:** Version bump only for package docs-app\n\n## [0.6.0](https://github.com/ByteCrazy/chameleon/compare/v0.5.2...v0.6.0) (2025-03-30)\n\n**Note:** Version bump only for package docs-app\n\n## [0.5.2](https://github.com/ByteCrazy/chameleon/compare/v0.5.1...v0.5.2) (2025-03-30)\n\n**Note:** Version bump only for package docs-app\n\n## [0.5.1](https://github.com/ByteCrazy/chameleon/compare/v0.5.0...v0.5.1) (2025-03-30)\n\n**Note:** Version bump only for package docs-app\n\n## [0.5.0](https://github.com/ByteCrazy/chameleon/compare/v0.4.0...v0.5.0) (2025-03-30)\n\n**Note:** Version bump only for package docs-app\n\n## [0.4.0](https://github.com/ByteCrazy/chameleon/compare/v0.3.21...v0.4.0) (2025-03-29)\n\n**Note:** Version bump only for package docs-app\n\n## [0.3.21](https://github.com/ByteCrazy/chameleon/compare/v0.3.20...v0.3.21) (2025-03-26)\n\n**Note:** Version bump only for package docs-app\n\n## [0.3.20](https://github.com/ByteCrazy/chameleon/compare/v0.3.19...v0.3.20) (2025-03-26)\n\n**Note:** Version bump only for package docs-app\n\n## [0.3.19](https://github.com/ByteCrazy/chameleon/compare/v0.3.18...v0.3.19) (2025-03-26)\n\n**Note:** Version bump only for package docs-app\n\n## [0.3.18](https://github.com/ByteCrazy/chameleon/compare/v0.3.17...v0.3.18) (2025-03-26)\n\n**Note:** Version bump only for package docs-app\n"
  },
  {
    "path": "packages/docs-app/README.md",
    "content": "# Starlight Starter Kit: Basics\n\n[![Built with Starlight](https://astro.badg.es/v2/built-with-starlight/tiny.svg)](https://starlight.astro.build)\n\n```\npnpm create astro@latest -- --template starlight\n```\n\n[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/starlight/tree/main/examples/basics)\n[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/starlight/tree/main/examples/basics)\n[![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/withastro/starlight&create_from_path=examples/basics)\n[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fwithastro%2Fstarlight%2Ftree%2Fmain%2Fexamples%2Fbasics&project-name=my-starlight-docs&repository-name=my-starlight-docs)\n\n> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun!\n\n## 🚀 Project Structure\n\nInside of your Astro + Starlight project, you'll see the following folders and files:\n\n```\n.\n├── public/\n├── src/\n│   ├── assets/\n│   ├── content/\n│   │   ├── docs/\n│   └── content.config.ts\n├── astro.config.mjs\n├── package.json\n└── tsconfig.json\n```\n\nStarlight looks for `.md` or `.mdx` files in the `src/content/docs/` directory. Each file is exposed as a route based on its file name.\n\nImages can be added to `src/assets/` and embedded in Markdown with a relative link.\n\nStatic assets, like favicons, can be placed in the `public/` directory.\n\n## 🧞 Commands\n\nAll commands are run from the root of the project, from a terminal:\n\n| Command                   | Action                                           |\n| :------------------------ | :----------------------------------------------- |\n| `pnpm install`             | Installs dependencies                            |\n| `pnpm dev`             | Starts local dev server at `localhost:4321`      |\n| `pnpm build`           | Build your production site to `./dist/`          |\n| `pnpm preview`         | Preview your build locally, before deploying     |\n| `pnpm astro ...`       | Run CLI commands like `astro add`, `astro check` |\n| `pnpm astro -- --help` | Get help using the Astro CLI                     |\n\n## 👀 Want to learn more?\n\nCheck out [Starlight’s docs](https://starlight.astro.build/), read [the Astro documentation](https://docs.astro.build), or jump into the [Astro Discord server](https://astro.build/chat).\n"
  },
  {
    "path": "packages/docs-app/astro.config.mjs",
    "content": "/* eslint-disable no-undef */\nimport { defineConfig, passthroughImageService } from 'astro/config';\nimport starlight from '@astrojs/starlight';\nimport react from '@astrojs/react';\nimport astroMermaid from 'astro-mermaid';\n\nconst isProd = process.env.NODE_ENV === 'production';\nconsole.log('🚀 ~ isProd:', isProd);\n\n// https://astro.build/config\nexport default defineConfig({\n  base: isProd ? '/chameleon/documents' : '/documents',\n  integrations: [\n    starlight({\n      title: 'Chameleon Docs',\n      social: {\n        github: 'https://github.com/hlerenow/chameleon',\n      },\n      sidebar: [\n        {\n          label: 'Guides',\n          autogenerate: {\n            directory: 'guides',\n          },\n        },\n        {\n          label: 'Reference',\n          autogenerate: {\n            directory: 'reference',\n          },\n        },\n      ],\n    }),\n    react(),\n    astroMermaid({\n      theme: 'default',\n    }),\n  ],\n  image: {\n    service: passthroughImageService(),\n  },\n});\n"
  },
  {
    "path": "packages/docs-app/package.json",
    "content": "{\n  \"name\": \"docs-app\",\n  \"type\": \"module\",\n  \"private\": true,\n  \"version\": \"0.10.4\",\n  \"scripts\": {\n    \"dev\": \"astro dev\",\n    \"start\": \"astro dev\",\n    \"build\": \"astro build\",\n    \"preview\": \"astro preview\",\n    \"astro\": \"astro\"\n  },\n  \"dependencies\": {\n    \"@ant-design/icons\": \"^5.4.0\",\n    \"@astrojs/check\": \"^0.9.2\",\n    \"@astrojs/react\": \"^3.6.2\",\n    \"@astrojs/starlight\": \"^0.32.4\",\n    \"@chamn/demo-page\": \"workspace:^\",\n    \"@chamn/engine\": \"workspace:^\",\n    \"@chamn/model\": \"workspace:^\",\n    \"@chamn/render\": \"workspace:^\",\n    \"@types/react\": \"^18.3.3\",\n    \"@types/react-dom\": \"^18.3.0\",\n    \"antd\": \"^5.23.2\",\n    \"astro\": \"^5.5.3\",\n    \"astro-mermaid\": \"^1.2.0\",\n    \"mermaid\": \"^11.12.2\",\n    \"react\": \"^18.3.1\",\n    \"react-dom\": \"^18.3.1\",\n    \"typescript\": \"~5.7.2\"\n  }\n}"
  },
  {
    "path": "packages/docs-app/src/codeSnippets/ButtonMeta.tsx",
    "content": "import { CMaterialType } from '@chamn/model';\n\nexport const ButtonMeta: CMaterialType = {\n  title: 'Button',\n  componentName: 'Button',\n  npm: {\n    package: 'antd',\n    exportName: 'Button',\n    destructuring: true,\n    version: '1.0.0',\n  },\n  icon: 'https://alifd.oss-cn-hangzhou.aliyuncs.com/fusion-cool/icons/icon-light/ic_light_button.png',\n  // 对 Button 属性的行为描述\n  props: [\n    {\n      name: 'type',\n      title: '按钮类型',\n      valueType: 'string',\n      // 用于配制用那些输入控件来接收 props 的值\n      setters: [\n        {\n          componentName: 'SelectSetter',\n          props: {\n            options: [\n              {\n                value: 'primary',\n                label: 'primary',\n              },\n              {\n                value: 'link',\n                label: 'link',\n              },\n              {\n                value: '',\n                label: 'Default',\n              },\n            ],\n          },\n        },\n      ],\n    },\n    {\n      name: 'block',\n      title: '块状按钮',\n      valueType: 'boolean',\n      setters: ['BooleanSetter'],\n      // 用于支持联动，当某些 key 的值变化后，控制当前属性是否显示的逻辑判断\n      condition: (state) => {\n        if (state.type === 'primary') {\n          return true;\n        }\n        return false;\n      },\n    },\n    {\n      name: 'children',\n      title: '文本',\n      valueType: 'string',\n      // 支持多种值的输入控件\n      setters: ['StringSetter', 'ExpressionSetter'],\n    },\n    {\n      name: 'onClick',\n      title: '点击时',\n      valueType: 'function',\n      setters: ['FunctionSetter', 'ExpressionSetter'],\n    },\n    {\n      name: 'text1',\n      title: '联动文本1',\n      valueType: 'string',\n      setters: [\n        {\n          componentName: 'StringSetter',\n        },\n      ],\n      condition: (state) => {\n        if (state.type === 'primary1') {\n          return true;\n        }\n        return false;\n      },\n    },\n    {\n      name: 'text2',\n      title: '联动文本2',\n      valueType: 'string',\n      setters: [\n        {\n          componentName: 'StringSetter',\n        },\n      ],\n      condition: (state) => {\n        if (state.text1 === '1') {\n          return true;\n        }\n        return false;\n      },\n    },\n    {\n      name: 'text3',\n      title: '联动文本3',\n      valueType: 'string',\n      setters: [\n        {\n          componentName: 'StringSetter',\n        },\n        'ExpressionSetter',\n      ],\n    },\n  ],\n  // 组件 schema 片段，会被展示在组件库中\n  snippets: [\n    {\n      title: '基础按钮',\n      snapshot: 'https://alifd.oss-cn-hangzhou.aliyuncs.com/fusion-cool/icons/icon-light/ic_light_button.png',\n      category: '基础控件2',\n      // 当被拖入到画布上时会将对应的 schema 添加到 页面级别的 Schema 中, 如果 schema 没有componentName， 会默认添加当前组件的 componentName\n      schema: {\n        props: {\n          type: 'primary',\n        },\n        children: ['I am a Button'],\n      },\n    },\n    {\n      title: '基础按钮',\n      snapshot: 'https://alifd.oss-cn-hangzhou.aliyuncs.com/fusion-cool/icons/icon-light/ic_light_button.png',\n      category: '基础控件3',\n      schema: {\n        props: {\n          type: 'primary',\n        },\n        children: ['I am a Button'],\n      },\n    },\n    {\n      title: '基础按钮',\n      snapshot: 'https://alifd.oss-cn-hangzhou.aliyuncs.com/fusion-cool/icons/icon-light/ic_light_button.png',\n      category: '基础控件4',\n      schema: {\n        props: {\n          type: 'primary',\n        },\n        children: ['I am a Button'],\n      },\n    },\n  ],\n};\n"
  },
  {
    "path": "packages/docs-app/src/codeSnippets/Editor.tsx",
    "content": "import { BasePage, Material } from '@chamn/demo-page';\nimport { Button, message, Modal } from 'antd';\nimport React, { useCallback, useEffect, useState } from 'react';\nimport ReactDOM from 'react-dom';\nimport ReactDOMClient from 'react-dom/client';\nimport '@chamn/engine/dist/style.css';\nimport { Engine, EnginContext, InnerComponentMeta, plugins } from '@chamn/engine';\nimport { RollbackOutlined } from '@ant-design/icons';\nimport './index.css';\n\nconst { DisplaySourceSchema, DEFAULT_PLUGIN_LIST } = plugins;\n\nconst win = window as any;\nwin.React = React;\nwin.ReactDOM = ReactDOM;\nwin.ReactDOMClient = ReactDOMClient;\n\nexport const App = () => {\n  const [ready, setReady] = useState(false);\n  const [page, setPage] = useState(BasePage);\n\n  useEffect(() => {\n    // 从本地获取 page schema\n    const localPage = localStorage.getItem('pageSchema');\n    if (localPage) {\n      setPage(JSON.parse(localPage));\n    }\n    setReady(true);\n  }, []);\n\n  const onReady = useCallback(async (ctx: EnginContext) => {\n    const designer = await ctx.pluginManager.onPluginReadyOk('Designer');\n    const reloadPage = async () => {\n      setTimeout(() => {\n        const designerExports = designer?.export;\n        designerExports.reload();\n      }, 0);\n    };\n\n    // 获取 引擎 工作台对象\n    const workbench = ctx.engine.getWorkbench();\n\n    // 自定义顶部 bar\n    workbench?.replaceTopBarView(\n      <div\n        style={{\n          width: '100%',\n          height: '100%',\n          display: 'flex',\n          alignItems: 'center',\n          justifyContent: 'flex-end',\n          paddingRight: '10px',\n        }}\n      >\n        <div className=\"logo\">Chameleon EG</div>\n\n        <a target=\"_blank\" href=\"https://github.com/hlerenow/chameleon\" rel=\"noreferrer\">\n          <Button style={{ marginRight: '10px' }}>Github </Button>\n        </a>\n\n        <Button\n          style={{ marginRight: '10px' }}\n          onClick={async () => {\n            const res = await ctx.pluginManager.get('History');\n            res?.export.preStep();\n          }}\n        >\n          <RollbackOutlined />\n        </Button>\n        <Button\n          style={{ marginRight: '10px' }}\n          onClick={async () => {\n            const res = await ctx.pluginManager.get('History');\n            res?.export.nextStep();\n          }}\n        >\n          <RollbackOutlined\n            style={{\n              transform: 'rotateY(180deg)',\n            }}\n          />\n        </Button>\n\n        <DisplaySourceSchema pageModel={ctx.engine.pageModel} engineCtx={ctx}>\n          <Button style={{ marginRight: '10px' }}>Source Code</Button>\n        </DisplaySourceSchema>\n\n        <Button\n          style={{ marginRight: '10px' }}\n          onClick={() => {\n            reloadPage();\n          }}\n        >\n          Refresh Page\n        </Button>\n        <Button\n          style={{ marginRight: '10px' }}\n          onClick={() => {\n            const src = '/#/preview';\n\n            Modal.info({\n              closable: true,\n              icon: null,\n              width: 'calc(100vw - 100px)',\n              centered: true,\n              title: (\n                <div>\n                  Preview\n                  <Button\n                    size=\"small\"\n                    style={{\n                      float: 'right',\n                      marginRight: '30px',\n                    }}\n                    onClick={() => {\n                      window.open(src);\n                    }}\n                  >\n                    Open in new window\n                  </Button>\n                </div>\n              ),\n              content: (\n                <div\n                  style={{\n                    width: '100%',\n                    height: 'calc(100vh - 200px)',\n                  }}\n                >\n                  <iframe\n                    style={{\n                      border: '1px solid #e7e7e7',\n                      width: '100%',\n                      height: '100%',\n                      borderRadius: '4px',\n                      overflow: 'hidden',\n                    }}\n                    src={src}\n                  />\n                </div>\n              ),\n              footer: null,\n            });\n          }}\n        >\n          Preview\n        </Button>\n        <Button\n          type=\"primary\"\n          onClick={() => {\n            const newPage = ctx.engine.pageModel.export();\n            localStorage.setItem('pageSchema', JSON.stringify(newPage));\n            message.success('Save successfully');\n          }}\n        >\n          Save\n        </Button>\n      </div>\n    );\n  }, []);\n\n  if (!ready) {\n    return <>loading...</>;\n  }\n\n  return (\n    <Engine\n      plugins={DEFAULT_PLUGIN_LIST}\n      schema={page}\n      // 传入组件物料, 这里使用内置的基础物料以及 测试物料信\n      material={[...InnerComponentMeta, ...Material]}\n      onReady={onReady}\n    />\n  );\n};\n"
  },
  {
    "path": "packages/docs-app/src/codeSnippets/GridItemMeta.tsx",
    "content": "/* eslint-disable react-hooks/rules-of-hooks */\nimport { CMaterialType } from '@chamn/model';\nimport { snippetsGridItem } from './snippets';\nimport { useEffect, useState } from 'react';\nimport { DesignerPluginInstance } from '@chamn/engine/dist/plugins/Designer/type';\nimport { breakpoints } from './config';\nimport { DesignerCtx } from '@chamn/engine/dist/plugins/Designer/components/Canvas';\nimport { debounce } from 'lodash-es';\nimport { GridItemPropsType } from './GridItem';\n\nconst GRID_ITEM_INSTANCE_MAP: any = {};\n\nexport const ReactGridItemMeta: CMaterialType = {\n  componentName: 'GridItem',\n  title: '高级布局容器',\n  category: '高级布局',\n  groupName: '内置组件',\n  props: [\n    {\n      name: 'responsive',\n      title: 'Responsive',\n      setters: [\n        {\n          componentName: 'ArraySetter',\n          initialValue: breakpoints,\n          props: {\n            collapse: {\n              open: true,\n            },\n            item: {\n              initialValue: { w: 0, label: 'customSize' },\n              setters: [\n                {\n                  componentName: 'ShapeSetter',\n                  initialValue: {\n                    with: 0,\n                    c: 0,\n                  },\n                  props: {\n                    collapse: {\n                      open: true,\n                    },\n                    elements: [\n                      {\n                        name: 'label',\n                        title: 'label',\n                        setters: [\n                          {\n                            componentName: 'StringSetter',\n                            props: {\n                              disabled: true,\n                            },\n                          },\n                        ],\n                        valueType: 'number',\n                      },\n                      {\n                        name: 'info',\n                        title: 'info',\n                        valueType: 'object',\n                        setters: [\n                          {\n                            componentName: 'ShapeSetter',\n                            initialValue: {\n                              with: 0,\n                              label: '',\n                            },\n                            props: {\n                              collapse: false,\n                              elements: [\n                                {\n                                  name: 'w',\n                                  title: 'width',\n                                  setters: ['NumberSetter', 'ExpressionSetter'],\n                                  valueType: 'number',\n                                },\n                                {\n                                  name: 'h',\n                                  title: 'height',\n                                  setters: ['NumberSetter', 'ExpressionSetter'],\n                                  valueType: 'number',\n                                },\n                                {\n                                  name: 'x',\n                                  title: 'offsetX',\n                                  setters: ['NumberSetter', 'ExpressionSetter'],\n                                  valueType: 'number',\n                                },\n                                {\n                                  name: 'y',\n                                  title: 'offsetY',\n                                  setters: ['NumberSetter', 'ExpressionSetter'],\n                                  valueType: 'number',\n                                },\n                              ],\n                            },\n                          },\n                        ],\n                      },\n                    ],\n                  },\n                },\n              ],\n            },\n          },\n        },\n      ],\n      valueType: 'number',\n    },\n  ],\n  isContainer: true,\n  npm: {\n    name: 'GridItem',\n    package: __PACKAGE_NAME__ || '',\n    version: __PACKAGE_VERSION__,\n    destructuring: true,\n    exportName: 'GridItem',\n  },\n  disableEditorDragDom: true,\n  advanceCustom: {\n    rightPanel: {\n      advanceOptions: {\n        render: false,\n        loop: false,\n      },\n    },\n    autoGetDom: false,\n    toolbarViewRender: ({ node, context, toolBarItemList }) => {\n      // 引擎自带的 显示隐藏，与编辑模式冲突，这里隐藏，不允许隐藏\n      toolBarItemList.splice(1, 1);\n      const [posInfo, setPostInfo] = useState({\n        label: '',\n        w: 0,\n        h: 0,\n        x: 0,\n        y: 0,\n      });\n\n      const getNodePosAndSizeInfo = debounce(async () => {\n        const compInsRef = GRID_ITEM_INSTANCE_MAP[node.id];\n        if (!compInsRef) {\n          return;\n        }\n        const posInfo = compInsRef.current?.getCurrentPosAndSizeInfo();\n        setPostInfo({\n          label: posInfo.label,\n          x: posInfo.info?.x,\n          y: posInfo.info?.y,\n          w: posInfo.info?.w,\n          h: posInfo.info?.h,\n        });\n      }, 100);\n\n      const registerResize = async () => {\n        node.onChange(getNodePosAndSizeInfo);\n        const ctx = context as DesignerCtx;\n        const designer = await ctx.pluginManager.get<DesignerPluginInstance>('Designer');\n\n        const subWin = designer?.export.getDesignerWindow();\n        subWin?.addEventListener('resize', getNodePosAndSizeInfo);\n        window?.addEventListener('resize', getNodePosAndSizeInfo);\n      };\n      const removeListener = async () => {\n        const ctx = context as DesignerCtx;\n        const designer = await ctx.pluginManager.get<DesignerPluginInstance>('Designer');\n\n        const subWin = designer?.export.getDesignerWindow();\n        subWin?.removeEventListener('resize', getNodePosAndSizeInfo);\n        window.removeEventListener('resize', getNodePosAndSizeInfo);\n      };\n      useEffect(() => {\n        getNodePosAndSizeInfo();\n        registerResize();\n        return () => {\n          removeListener();\n        };\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n      }, []);\n      return (\n        <div\n          style={{\n            display: 'flex',\n            float: 'right',\n            zIndex: 999,\n            pointerEvents: 'all',\n          }}\n        >\n          <div\n            style={{\n              background: 'white',\n              marginRight: '5px',\n              fontSize: '12px',\n              padding: '0 10px',\n              display: 'flex',\n              alignItems: 'center',\n              justifyContent: 'center',\n            }}\n          >\n            <b\n              style={{\n                paddingRight: '2px',\n                color: 'red',\n              }}\n            >\n              {posInfo.label}\n            </b>\n            | w: {posInfo.w} | h: {posInfo.h} | x: {posInfo.x} | y: {posInfo.y}\n          </div>\n          {toolBarItemList}\n        </div>\n      );\n    },\n    onDragStart: async () => {\n      return false;\n    },\n    wrapComponent: (Comp, options) => {\n      return (props: any) => {\n        return (\n          <Comp\n            {...props}\n            {...options}\n            dev={true}\n            onGetRef={(ref: any) => {\n              GRID_ITEM_INSTANCE_MAP[options.node.id] = ref;\n            }}\n          />\n        );\n      };\n    },\n    canDropNode: async (_node, params) => {\n      const { dropNode } = params;\n      if (!dropNode) {\n        return false;\n      }\n      if (dropNode.value.componentName === 'GridLayout') {\n        return true;\n      }\n\n      return false;\n    },\n    onCopy: async (node) => {\n      const newProps: GridItemPropsType = node.getPlainProps();\n      const newResponsive = newProps.responsive.map((el) => {\n        return {\n          ...el,\n          x: '',\n          y: '',\n        };\n      });\n      newProps.responsive = newResponsive;\n      node.updateValue({\n        props: newProps,\n      });\n      return true;\n    },\n    onNewAdd: async (_node, params) => {\n      const { dropNode } = params;\n      if (!dropNode) {\n        return false;\n      }\n      if (dropNode.value.componentName === 'GridLayout') {\n        return true;\n      }\n\n      return false;\n    },\n  },\n\n  snippets: snippetsGridItem,\n};\n\nexport default [ReactGridItemMeta];\n"
  },
  {
    "path": "packages/docs-app/src/codeSnippets/GridLayoutComponent.tsx",
    "content": "import React, { useCallback, useRef } from 'react';\nimport { ColumnOptions, GridStack } from 'gridstack';\nimport 'gridstack/dist/gridstack.min.css';\nimport 'gridstack/dist/gridstack-extra.min.css';\nimport './layout.scss';\nimport { useEffect, useMemo, useState } from 'react';\nimport { EnginContext } from '@chamn/engine';\nimport { getDefaultContextValue, GridContext, GridContextType } from './context';\nimport { breakpoints } from './config';\nimport { debounce } from 'lodash-es';\nimport { ResponsivePoint } from './type';\n\nexport type ReactGridLayoutPropsType = {\n  children: any;\n  onMount?: (grid: GridStack) => void;\n  ctx: EnginContext;\n  subWin?: Window;\n  staticGrid?: boolean;\n  animate?: boolean;\n  layout?: ColumnOptions;\n  $SET_DOM?: (dom: HTMLElement) => void;\n  breakpoints?: ResponsivePoint[];\n  onBreakpointChange?: (breakpoint: { w: number; label: string }) => void;\n};\n\nexport const GridLayout = ({ subWin, staticGrid, animate, onMount, $SET_DOM, ...props }: ReactGridLayoutPropsType) => {\n  const [ctx, setCtx] = useState<GridContextType>(getDefaultContextValue());\n  const id = useMemo(() => {\n    return Math.random().toString(32).slice(3, 9);\n  }, []);\n  const gridRef = useRef<GridStack>();\n  const refDom = useRef<HTMLDivElement>(null);\n\n  const init = useCallback(async () => {\n    const tempGridStack: typeof GridStack = (subWin as any)?.GridStack || GridStack;\n\n    const grid = tempGridStack.init(\n      {\n        cellHeight: '30px',\n        margin: 3,\n        column: 24,\n        float: true,\n        minRow: 3,\n        animate: true,\n        staticGrid: staticGrid ?? true,\n        columnOpts: {\n          layout: 'scale',\n        },\n        draggable: {\n          handle: '.grid-drag-handler',\n        },\n      },\n      id\n    );\n    if (!grid) {\n      return;\n    }\n    gridRef.current = grid;\n    onMount?.(grid);\n    setCtx((oldVal) => {\n      return {\n        ...oldVal,\n        gridStack: grid,\n        ready: true,\n      };\n    });\n\n    ctx.onMount?.forEach((el) => {\n      el(grid);\n    });\n  }, [ctx.onMount, id, onMount, staticGrid, subWin]);\n\n  /** 配合设计器使用 */\n  if (refDom.current) {\n    $SET_DOM?.(refDom.current);\n  }\n\n  useEffect(() => {\n    if (props.breakpoints) {\n      gridRef.current?.destroy(false);\n      init();\n    }\n  }, [init, props.breakpoints, props.layout]);\n\n  const responseJudge = debounce(() => {\n    const sunWinW = (subWin ?? window).innerWidth;\n    const pointInfo = breakpoints.find((el) => el.w >= sunWinW);\n\n    if (!pointInfo) {\n      return;\n    }\n\n    setCtx((oldVal) => {\n      props.onBreakpointChange?.(pointInfo);\n      if (pointInfo.w === ctx.currentBreakpoint.w) {\n        return oldVal;\n      }\n      return {\n        ...oldVal,\n        currentBreakpoint: pointInfo,\n      };\n    });\n  }, 50);\n\n  useEffect(() => {\n    window.addEventListener('resize', responseJudge);\n    subWin?.addEventListener('resize', responseJudge);\n    responseJudge();\n    return () => {\n      window.removeEventListener('resize', responseJudge);\n      subWin?.removeEventListener('resize', responseJudge);\n    };\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n\n  const finalCtx = useMemo(() => {\n    return {\n      ...ctx,\n      breakpoints: props.breakpoints ?? breakpoints,\n    };\n  }, [ctx, props.breakpoints]);\n\n  return (\n    <GridContext.Provider value={finalCtx}>\n      <div id={id} className=\"grid-stack\" ref={refDom}>\n        {props.children}\n      </div>\n    </GridContext.Provider>\n  );\n};\n"
  },
  {
    "path": "packages/docs-app/src/codeSnippets/GridLayoutMeta.tsx",
    "content": "import { CMaterialType } from '@chamn/model';\nimport { snippets } from './snippets';\nimport { LayoutWrap } from './edit/layoutWrap';\nimport { useEffect, useState, useCallback, useRef } from 'react';\nimport { EnginContext } from '@chamn/engine';\nimport { DesignerPluginInstance } from '@chamn/engine/dist/plugins/Designer/type';\nimport { GridStack } from 'gridstack';\nimport { breakpoints } from './config';\n\nexport const ReactGridLayoutMeta: CMaterialType = {\n  componentName: 'GridLayout',\n  title: '高级布局画布',\n  props: [\n    {\n      name: 'breakpoints',\n      title: 'Breakpoints',\n      setters: [\n        {\n          componentName: 'ArraySetter',\n          initialValue: breakpoints,\n          props: {\n            collapse: {\n              open: true,\n            },\n            item: {\n              initialValue: { w: 0, label: '' },\n              setters: [\n                {\n                  componentName: 'ShapeSetter',\n                  initialValue: {\n                    with: 0,\n                    c: 0,\n                  },\n                  props: {\n                    collapse: {\n                      open: true,\n                    },\n                    elements: [\n                      {\n                        name: 'label',\n                        title: 'label',\n                        setters: ['StringSetter'],\n                        valueType: 'string',\n                      },\n                      {\n                        name: 'w',\n                        title: 'width',\n                        setters: [\n                          {\n                            componentName: 'NumberSetter',\n                            props: {\n                              suffix: 'px',\n                            },\n                          },\n                        ],\n                        valueType: 'number',\n                      },\n                    ],\n                  },\n                },\n              ],\n            },\n          },\n        },\n      ],\n      valueType: 'array',\n    },\n  ],\n  isContainer: true,\n  category: '高级布局',\n  groupName: '内置组件',\n  npm: {\n    name: 'GridLayout',\n    package: __PACKAGE_NAME__ || '',\n    version: __PACKAGE_VERSION__,\n    destructuring: true,\n    exportName: 'GridLayout',\n  },\n  snippets: snippets,\n  advanceCustom: {\n    autoGetDom: false,\n    wrapComponent: (Comp, options) => {\n      return (props: any) => {\n        const [iframeWindow, setIframeWindow] = useState();\n        const designerRef = useRef<DesignerPluginInstance>();\n        useEffect(() => {\n          const ctx: EnginContext = options.ctx;\n          ctx.pluginManager.onPluginReadyOk('Designer').then((ins: DesignerPluginInstance) => {\n            designerRef.current = ins;\n            const win = ins.export.getDesignerWindow();\n            setIframeWindow(win as any);\n          });\n        }, []);\n\n        const onGridMount = useCallback((grid: GridStack) => {\n          grid.on('dragstart', () => {\n            designerRef.current?.export.getLayoutRef().current?.banSelectNode();\n          });\n          grid.on('dragstop', (event) => {\n            setTimeout(() => {\n              designerRef.current?.export.getLayoutRef().current?.recoverSelectNode();\n              const nodeId = (event.target as any)?.getAttribute('data-grid-id');\n              designerRef.current?.export.getLayoutRef().current?.selectNode(nodeId);\n            }, 0);\n          });\n        }, []);\n\n        if (!iframeWindow) {\n          return <></>;\n        }\n\n        return <LayoutWrap {...props} {...options} targetComp={Comp} subWin={iframeWindow} onMount={onGridMount} />;\n      };\n    },\n    rightPanel: {\n      visual: false,\n    },\n  },\n};\n\nexport default [ReactGridLayoutMeta];\n"
  },
  {
    "path": "packages/docs-app/src/codeSnippets/GridLayoutWrap.tsx",
    "content": "import { useRef } from 'react';\nimport { GridLayout, ReactGridLayoutPropsType } from '..';\nimport { GridStack, GridStackElementHandler } from 'gridstack';\nimport { breakpoints } from '../config';\nimport { GridItemPropsType } from '../GridItem';\n\ntype ChangeLayoutEvent = {\n  detail: {\n    el: HTMLElement;\n    grid: GridStack;\n    x: number;\n    y: number;\n    w: number;\n    h: number;\n  }[];\n};\n\nexport const LayoutWrap = (\n  props: ReactGridLayoutPropsType & {\n    targetComp: typeof GridLayout;\n  }\n) => {\n  const { targetComp: Comp, ...restProps } = props;\n  const ref = useRef<GridStack>();\n\n  const initEditLogic = (grid: GridStack) => {\n    const updateGridItemLayout: GridStackElementHandler = (changeLayout) => {\n      const { detail }: ChangeLayoutEvent = changeLayout as any;\n      const sunWinW = props.subWin!.innerWidth;\n      const pointInfo = breakpoints.find((el) => el.w >= sunWinW);\n      detail.forEach((item) => {\n        const nodeId = item.el.getAttribute('data-grid-id');\n        const node = props.ctx.engine.pageModel.getNode(String(nodeId));\n        if (node) {\n          const plainProps: GridItemPropsType = node.getPlainProps();\n          const newResponsive = plainProps.responsive;\n          let targetItem = newResponsive.find((el) => el.label === pointInfo!.label);\n          if (!targetItem) {\n            targetItem = {\n              label: pointInfo!.label,\n              info: {} as any,\n            };\n            newResponsive.push(targetItem);\n          }\n          targetItem.info = {\n            x: item.x,\n            y: item.y,\n            w: item.w,\n            h: item.h,\n          };\n\n          node.updateValue({\n            props: plainProps,\n          });\n        }\n      });\n    };\n    grid.on('change', updateGridItemLayout);\n  };\n\n  return (\n    <Comp\n      {...restProps}\n      animate={true}\n      staticGrid={false}\n      subWin={props.subWin}\n      onMount={(grid) => {\n        ref.current = grid;\n        initEditLogic(grid);\n        restProps.onMount?.(grid);\n      }}\n    />\n  );\n};\n"
  },
  {
    "path": "packages/docs-app/src/codeSnippets/index.css",
    "content": "html,\nbody,\n#root {\n  width: 100%;\n  height: 100%;\n  overflow: hidden;\n}\n\nbody {\n  margin: 0;\n  padding: 0;\n}\n\n.logo {\n  height: 100%;\n  font-size: 20px;\n  display: flex;\n  align-items: center;\n  margin-left: 20px;\n  font-weight: bolder;\n  margin-right: auto;\n}\n"
  },
  {
    "path": "packages/docs-app/src/codeSnippets/render.tsx",
    "content": "import { useEffect, useState } from 'react';\nimport {\n  ReactAdapter,\n  Render,\n  useRender,\n  AssetLoader,\n  collectVariable,\n  flatObject,\n  getComponentsLibs,\n  getThirdLibs,\n} from '@chamn/render';\nimport { AssetPackage, CPageDataType } from '@chamn/model';\n\n// 加载资源并收集组件\nconst loadAssets = async (assets: AssetPackage[]) => {\n  const assetLoader = new AssetLoader(assets);\n  try {\n    await assetLoader.load();\n    // 从 window 对象收集组件变量\n    const componentCollection = collectVariable(assets, window);\n    return componentCollection;\n  } catch (e) {\n    console.error('Failed to load assets:', e);\n    return null;\n  }\n};\n\nexport const Preview = () => {\n  const [page, setPage] = useState<CPageDataType>();\n  const renderHandle = useRender();\n  const [loading, setLoading] = useState(true);\n  const [pageComponents, setPageComponents] = useState({});\n  const [renderContext, setRenderContext] = useState({});\n\n  // 加载页面资源并区分组件库和第三方库\n  const loadPageAssets = async (pageInfo: CPageDataType) => {\n    const assets = pageInfo.assets || [];\n    const allLibs = (await loadAssets(assets)) || {};\n\n    // 区分 UI 组件库和第三方库\n    const componentsLibs = getComponentsLibs(flatObject(allLibs), pageInfo.componentsMeta || []);\n    const thirdLibs = getThirdLibs(allLibs, pageInfo.thirdLibs || []);\n\n    if (componentsLibs) {\n      setPageComponents(componentsLibs);\n      setRenderContext({ thirdLibs });\n      setLoading(false);\n    }\n  };\n\n  useEffect(() => {\n    const localPage = localStorage.getItem('pageSchema');\n    if (localPage) {\n      const page: CPageDataType = JSON.parse(localPage);\n      setPage(page);\n      loadPageAssets(page);\n    }\n  }, []);\n\n  if (loading) {\n    return <>加载中...</>;\n  }\n\n  return (\n    <div className=\"App\" style={{ overflow: 'auto', height: '100%' }}>\n      <Render\n        page={page}\n        components={pageComponents}\n        render={renderHandle}\n        adapter={ReactAdapter}\n        $$context={renderContext}\n      />\n    </div>\n  );\n};\n"
  },
  {
    "path": "packages/docs-app/src/components/Link.tsx",
    "content": "import React from 'react';\n\nexport const Link = (props: { children: any; url: string }) => {\n  const baseUrl = import.meta.env.BASE_URL;\n  return <a href={`${baseUrl}/${props.url}`}>{props.children}</a>;\n};\n"
  },
  {
    "path": "packages/docs-app/src/content/config.ts",
    "content": "import { defineCollection } from 'astro:content';\nimport { docsSchema } from '@astrojs/starlight/schema';\n\nexport const collections = {\n  docs: defineCollection({ schema: docsSchema() }),\n};\n"
  },
  {
    "path": "packages/docs-app/src/content/docs/guides/addCustomComponent.mdx",
    "content": "---\ntitle: 添加自定义组件物料\nsidebar:\n  order: 3\n---\n\nimport { Code } from '@astrojs/starlight/components';\n\n引擎内置只提供了基础的 HTML Tag 组件以及对应的物料, 如: div、p、video...等等，如果需要扩展使用自定义的物料，参考如下方式\n\n## 编辑器配置\n\n### 导入组件物料\n\n首先需要导入自定义组件的物料描述。物料通常以 npm 包的形式提供，包含组件的元数据信息：\n\n```tsx\nimport { Engine, InnerComponentMeta, plugins } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\n\n// 导入自定义物料（假设物料包为 @custom/material）\nimport customMaterial from '@custom/material/dist/meta';\n\n// 或者从本地文件导入\n// import customMaterial from './material/meta';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n```\n\n### 配置资源包列表\n\n编辑器模式下需要提供组件的 UMD 格式资源（JS 和 CSS），这些资源会在编辑器的 iframe 中加载：\n\n```tsx\n// 方式一：使用 CDN 资源\nconst assetPackagesList = [\n  {\n    package: '@custom/material',\n    globalName: 'CustomMaterial',\n    resources: [\n      {\n        src: 'https://cdn.example.com/custom-material.umd.js',\n      },\n      {\n        src: 'https://cdn.example.com/custom-material.css',\n      },\n    ],\n  },\n];\n\n// 方式二：使用本地资源（推荐使用 ?url 后缀导入， vite 构建工具会自动处理，其他工具需要自行处理）\nimport customMaterialJS from '@custom/material/dist/index.umd.js?url';\nimport customMaterialCSS from '@custom/material/dist/style.css?url';\n\nconst assetPackagesList = [\n  {\n    package: '@custom/material',\n    globalName: 'CustomMaterial',\n    resources: [\n      {\n        src: customMaterialJS,\n      },\n      {\n        src: customMaterialCSS,\n      },\n    ],\n  },\n];\n```\n\n### 完整示例\n\n```tsx\nimport { useState, useEffect } from 'react';\nimport { Engine, InnerComponentMeta, plugins, EnginContext } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\n\n// 导入自定义物料\nimport customMaterial from '@custom/material/dist/meta';\n\n// 导入资源文件\nimport customMaterialJS from '@custom/material/dist/index.umd.js?url';\nimport customMaterialCSS from '@custom/material/dist/style.css?url';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nconst assetPackagesList = [\n  {\n    package: customMaterial.package || '@custom/material',\n    globalName: customMaterial.globalName || 'CustomMaterial',\n    resources: [\n      {\n        src: customMaterialJS,\n      },\n      {\n        src: customMaterialCSS,\n      },\n    ],\n  },\n];\n\nfunction Editor() {\n  const [page, setPage] = useState(null);\n\n  useEffect(() => {\n    // 从本地存储加载页面数据\n    const localPage = localStorage.getItem('pageSchema');\n    if (localPage) {\n      setPage(JSON.parse(localPage));\n    }\n  }, []);\n\n  const onReady = (ctx: EnginContext) => {\n    console.log('Engine ready!', ctx);\n  };\n\n  return (\n    <Engine\n      plugins={DEFAULT_PLUGIN_LIST}\n      schema={page}\n      // 传入组件物料，合并内置物料和自定义物料\n      material={[...InnerComponentMeta, ...customMaterial.meta]}\n      // 传入组件物料对应的 UMD 资源\n      assetPackagesList={assetPackagesList}\n      onReady={onReady}\n    />\n  );\n}\n```\n\n### 动态更新物料\n\n如果需要在运行时动态添加物料，可以使用 `engine.updateMaterials` API：\n\n```tsx\nconst onReady = async (ctx: EnginContext) => {\n  // 动态添加新的物料和资源\n  await ctx.engine.updateMaterials(\n    [\n      // 新的物料数组\n      {\n        componentName: 'NewComponent',\n        title: '新组件',\n        // ... 其他物料配置\n      },\n    ],\n    [\n      // 新的资源包列表\n      {\n        package: 'new-package',\n        globalName: 'NewPackage',\n        resources: [\n          {\n            src: 'https://cdn.example.com/new-package.umd.js',\n          },\n        ],\n      },\n    ]\n  );\n};\n```\n\n## 渲染页面配置\n\n渲染页面（预览/生产环境）可以直接 import npm 包，不需要提供 UMD 格式的资源：\n\n```tsx\nimport { useEffect, useState } from 'react';\nimport { ReactAdapter, Render, useRender } from '@chamn/render';\nimport { CPageDataType } from '@chamn/model';\n\n// 直接导入组件库\nimport * as antd from 'antd';\nimport CustomComps from '@custom/material';\n\nconst components = {\n  ...antd,\n  ...CustomComps,\n};\n\nexport const Preview = () => {\n  const [page, setPage] = useState<CPageDataType>();\n  const renderHandle = useRender();\n  const [loading, setLoading] = useState(true);\n\n  useEffect(() => {\n    const localPage = localStorage.getItem('pageSchema');\n    if (localPage) {\n      setPage(JSON.parse(localPage));\n      setLoading(false);\n    }\n  }, []);\n\n  if (loading) {\n    return <>未找到页面信息，请确保已在编辑器中保存</>;\n  }\n\n  return (\n    <div className=\"App\" style={{ overflow: 'auto', height: '100%' }}>\n      <Render page={page} components={components} render={renderHandle} adapter={ReactAdapter} />\n    </div>\n  );\n};\n```\n\n## 注意事项\n\n1. **物料格式**：物料对象需要包含 `meta` 属性，它是一个物料数组\n2. **资源格式**：编辑器模式下必须使用 UMD 格式的资源，资源会在 iframe 中动态加载\n3. **全局变量名**：`globalName` 需要与组件库导出的全局变量名一致\n4. **包名匹配**：`assetPackagesList` 中的 `package` 需要与物料中的 `npm.package` 字段匹配\n5. **渲染页面**：生产环境可以直接使用 npm 包，无需 UMD 资源\n\n:::tip\n如何开发自定义组件物料？\n\n查看 [物料开发指南](../reference/Material/developMaterial/) 了解如何开发自定义组件物料。\n:::\n"
  },
  {
    "path": "packages/docs-app/src/content/docs/guides/index.mdx",
    "content": "---\ntitle: 快速开始\ndescripttopn:\nsidebar:\n  order: 1\n---\n\nimport { Tabs, TabItem, FileTree, CardGrid, LinkCard, Steps, Code } from '@astrojs/starlight/components';\nimport EditorSourceCode from '../../../codeSnippets/Editor.tsx?raw';\nimport cssSourceCode from '../../../codeSnippets/index.css?raw';\nimport engImg from '../../../assets/engine-thumbail.png';\n\n## 安装\n\n<Tabs>\n  <TabItem label=\"npm\">```shell npm i @chamn/engine @chamn/model @chamn/render ```</TabItem>\n  <TabItem label=\"pnpm\">```shell pnpm i @chamn/engine @chamn/model @chamn/render ```</TabItem>\n  <TabItem label=\"yarn\">```shell yarn i @chamn/engine @chamn/model @chamn/render ```</TabItem>\n</Tabs>\n\n你可以在你的机器上的任何地方运行 `create astro`，因此在开始之前无需创建一个新的空目录。如果你还没有为你的新项目准备一个空目录，向导将会自动为你创建一个。\n\n如果一切顺利，你将看到一个成功信息，随后是一些推荐的后续步骤。现在你的项目已经创建好了，你可以 `cd` 进入你的新项目目录开始使用 Astro。\n\n由于演示用例使用了 `antd` 以及 `@chamn/demo-page` 库(这不是必须的，只是这里做演示使用)，所以你还需要安装：\n\n<Tabs>\n  <TabItem label=\"npm\">```shell npm i antd @ant-design/icons @chamn/demo-page ```</TabItem>\n  <TabItem label=\"pnpm\">```shell pnpm i antd @ant-design/icons @chamn/demo-page ```</TabItem>\n  <TabItem label=\"yarn\">```shell yarn i antd @ant-design/icons @chamn/demo-page ```</TabItem>\n</Tabs>\n\n## 用法\n\n创建一个 Editor.tsx 文件，将以下代码拷贝进去:\n\n<Code code={EditorSourceCode} lang=\"tsx\" title=\"Editor.tsx\" />\n\n添加 css 让编辑器撑满窗口\n\n<Code code={cssSourceCode} lang=\"css\" title=\"index.css\" />\n\n## 配置 render.umd.js\n\n因为渲染画布是在 iframe 中运行，所以需使用 umd 模式的 js，然后让 iframe 加载。在 vite 构建工具下，有以下三种配置方式：\n\n### 方式一：使用 `?url` 后缀导入（推荐）\n\n这是最简单的方式，直接使用 vite 的 `?url` 后缀导入文件 URL，无需额外配置：\n\n```tsx\nimport renderAsURL from '@chamn/render/dist/index.umd.js?url';\n\n<Engine\n  plugins={DEFAULT_PLUGIN_LIST}\n  schema={page}\n  material={[...InnerComponentMeta, ...Material]}\n  assetPackagesList={assetPackagesList}\n  onReady={onReady}\n  renderJSUrl={renderAsURL}\n/>;\n```\n\n或者从 node_modules 路径导入：\n\n```tsx\nimport renderAsURL from '../../node_modules/@chamn/render/dist/index.umd.js?url';\n```\n\n### 方式二：使用 vite-plugin-static-copy 插件\n\n使用 `vite-plugin-static-copy` 插件在构建时自动将文件拷贝到输出目录：\n\n首先安装依赖：\n\n<Tabs>\n  <TabItem label=\"npm\">```shell npm i vite-plugin-static-copy ```</TabItem>\n  <TabItem label=\"pnpm\">```shell pnpm i vite-plugin-static-copy ```</TabItem>\n  <TabItem label=\"yarn\">```shell yarn i vite-plugin-static-copy ```</TabItem>\n</Tabs>\n\n然后在 `vite.config.ts` 中配置：\n\n```ts\nimport { defineConfig } from 'vite';\nimport react from '@vitejs/plugin-react';\nimport monacoEditorPlugin from 'vite-plugin-monaco-editor';\nimport { viteStaticCopy } from 'vite-plugin-static-copy';\n\nexport default defineConfig({\n  plugins: [\n    react(),\n    monacoEditorPlugin({}),\n    viteStaticCopy({\n      targets: [\n        {\n          src: './node_modules/@chamn/render/dist/index.umd.js',\n          dest: './',\n          rename: 'render.umd.js',\n        },\n      ],\n    }),\n  ],\n});\n```\n\n这样构建时会自动将 `render.umd.js` 拷贝到输出目录的根目录下，然后使用相对路径：\n\n```tsx\n<Engine\n  plugins={DEFAULT_PLUGIN_LIST}\n  schema={page}\n  material={[...InnerComponentMeta, ...Material]}\n  assetPackagesList={assetPackagesList}\n  onReady={onReady}\n  renderJSUrl=\"./render.umd.js\"\n/>\n```\n\n### 方式三：使用 CDN 或自定义路径\n\n如果使用 CDN 或其他自定义路径，可以直接传入 URL：\n\n```tsx\n<Engine\n  plugins={DEFAULT_PLUGIN_LIST}\n  schema={page}\n  material={[...InnerComponentMeta, ...Material]}\n  assetPackagesList={assetPackagesList}\n  onReady={onReady}\n  renderJSUrl=\"https://cdn.example.com/render.umd.js\"\n/>\n```\n\n或者使用相对路径：\n\n```tsx\n<Engine\n  plugins={DEFAULT_PLUGIN_LIST}\n  schema={page}\n  material={[...InnerComponentMeta, ...Material]}\n  assetPackagesList={assetPackagesList}\n  onReady={onReady}\n  renderJSUrl=\"./public/render.umd.js\"\n/>\n```\n\n## 配置 monaco-editor 构建\n\n因为引擎使用了 monaco-editor 代码编辑器所以需要配置相应的构建配置\n\n### Vite\n\n```ts\nimport { defineConfig } from 'vite';\nimport react from '@vitejs/plugin-react';\nimport monacoEditorPlugin from 'vite-plugin-monaco-editor';\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [react(), monacoEditorPlugin({})],\n});\n```\n\n### Webpack\n\n[Refenrence](https://www.npmjs.com/package/monaco-editor-webpack-plugin)\n\n## 运行\n\n将 Editor.tsx 页面作为你项目中的一个页面，运行，打开对应的地址你将会得到一个编辑页面\n\n<img src={engImg.src} style={{ marginBottom: '20px', border: '1px solid #ededed' }} alt=\"Engine thumbail\" />\n\n:::info\nGood Luck! :)\n:::\n"
  },
  {
    "path": "packages/docs-app/src/content/docs/guides/useRender.mdx",
    "content": "---\ntitle: 接入渲染器\ndescription: 了解如何将页面 schema 协议渲染为可运行的 React 页面\nsidebar:\n  order: 2\n---\n\nimport { Tabs, TabItem, Code } from '@astrojs/starlight/components';\n\n接入编辑器之后，通过页面编辑，最终导出后可以得到一个页面的 schema JSON 协议。如何将协议重新渲染为一个可以运行的 React 页面呢？\n\n## 安装\n\n<Tabs>\n  <TabItem label=\"npm\">```shell npm i @chamn/render @chamn/model ```</TabItem>\n  <TabItem label=\"pnpm\">```shell pnpm i @chamn/render @chamn/model ```</TabItem>\n  <TabItem label=\"yarn\">```shell yarn i @chamn/render @chamn/model ```</TabItem>\n</Tabs>\n\n## 使用方式\n\n渲染器支持两种使用方式：\n\n### 方式一：直接导入 npm 包（推荐）\n\n适用于生产环境，直接导入组件库，无需动态加载资源：\n\n```tsx\nimport { useEffect, useState } from 'react';\nimport { ReactAdapter, Render, useRender } from '@chamn/render';\nimport { CPageDataType } from '@chamn/model';\n\n// 直接导入组件库\nimport * as antd from 'antd';\nimport CustomComps from '@custom/material';\n\nconst components = {\n  ...antd,\n  ...CustomComps,\n};\n\nexport const Preview = () => {\n  const [page, setPage] = useState<CPageDataType>();\n  const renderHandle = useRender();\n  const [loading, setLoading] = useState(true);\n\n  useEffect(() => {\n    // 从本地存储或 API 获取页面协议\n    const localPage = localStorage.getItem('pageSchema');\n    if (localPage) {\n      setPage(JSON.parse(localPage));\n      setLoading(false);\n    }\n  }, []);\n\n  if (loading) {\n    return <>加载中...</>;\n  }\n\n  return (\n    <div className=\"App\" style={{ overflow: 'auto', height: '100%' }}>\n      <Render page={page} components={components} render={renderHandle} adapter={ReactAdapter} />\n    </div>\n  );\n};\n```\n\n### 方式二：动态加载 UMD 资源\n\n适用于需要从页面 schema 中动态加载组件资源的场景：\n\n```tsx\nimport { useEffect, useState } from 'react';\nimport {\n  ReactAdapter,\n  Render,\n  useRender,\n  AssetLoader,\n  collectVariable,\n  flatObject,\n  getComponentsLibs,\n  getThirdLibs,\n} from '@chamn/render';\nimport { AssetPackage, CPageDataType } from '@chamn/model';\n\n// 加载资源并收集组件\nconst loadAssets = async (assets: AssetPackage[]) => {\n  const assetLoader = new AssetLoader(assets);\n  try {\n    await assetLoader.load();\n    // 从 window 对象收集组件变量\n    const componentCollection = collectVariable(assets, window);\n    return componentCollection;\n  } catch (e) {\n    console.error('Failed to load assets:', e);\n    return null;\n  }\n};\n\nexport const Preview = () => {\n  const [page, setPage] = useState<CPageDataType>();\n  const renderHandle = useRender();\n  const [loading, setLoading] = useState(true);\n  const [pageComponents, setPageComponents] = useState({});\n  const [renderContext, setRenderContext] = useState({});\n\n  // 加载页面资源并区分组件库和第三方库\n  const loadPageAssets = async (pageInfo: CPageDataType) => {\n    const assets = pageInfo.assets || [];\n    const allLibs = (await loadAssets(assets)) || {};\n\n    // 区分 UI 组件库和第三方库\n    const componentsLibs = getComponentsLibs(flatObject(allLibs), pageInfo.componentsMeta || []);\n    const thirdLibs = getThirdLibs(allLibs, pageInfo.thirdLibs || []);\n\n    if (componentsLibs) {\n      setPageComponents(componentsLibs);\n      setRenderContext({ thirdLibs });\n      setLoading(false);\n    }\n  };\n\n  useEffect(() => {\n    const localPage = localStorage.getItem('pageSchema');\n    if (localPage) {\n      const page: CPageDataType = JSON.parse(localPage);\n      setPage(page);\n      loadPageAssets(page);\n    }\n  }, []);\n\n  if (loading) {\n    return <>加载中...</>;\n  }\n\n  return (\n    <div className=\"App\" style={{ overflow: 'auto', height: '100%' }}>\n      <Render\n        page={page}\n        components={pageComponents}\n        render={renderHandle}\n        adapter={ReactAdapter}\n        $$context={renderContext}\n      />\n    </div>\n  );\n};\n```\n\n## Render 组件 API\n\n### Props\n\n| 属性名                  | 类型                                                | 必填 | 说明                                         |\n| :---------------------- | :-------------------------------------------------- | :--- | :------------------------------------------- |\n| `page`                  | `CPageDataType`                                     | 是   | 页面协议数据                                 |\n| `adapter`               | `AdapterType`                                       | 是   | 适配器，通常使用 `ReactAdapter`              |\n| `components`            | `Record<string, any>`                               | 是   | 组件映射对象，key 为组件名，value 为组件实例 |\n| `render`                | `UseRenderReturnType`                               | 否   | `useRender` hook 返回的句柄，用于控制渲染    |\n| `renderMode`            | `'design' \\| 'normal'`                              | 否   | 渲染模式，默认为 `'normal'`                  |\n| `$$context`             | `Record<string, any>`                               | 否   | 上下文对象，可传递第三方库等数据             |\n| `requestAPI`            | `(params: any) => Promise<any>`                     | 否   | API 请求函数                                 |\n| `processNodeConfigHook` | `(config: any) => any`                              | 否   | 节点配置处理钩子                             |\n| `onGetRef`              | `(ref: any, nodeModel: any, instance: any) => void` | 否   | 获取组件 ref 的回调                          |\n| `onComponentMount`      | `(nodeModel: any, instance: any) => void`           | 否   | 组件挂载回调                                 |\n| `onComponentDestroy`    | `(nodeModel: any, instance: any) => void`           | 否   | 组件销毁回调                                 |\n\n### useRender Hook\n\n`useRender` 返回一个对象，包含：\n\n- `ref`: Render 组件的引用\n- `rerender`: 重新渲染函数，可以传入新的页面协议\n\n```tsx\nconst renderHandle = useRender();\n\n// 重新渲染页面\nrenderHandle.rerender(newPageSchema);\n```\n\n## 工具函数\n\n### AssetLoader\n\n用于动态加载 UMD 格式的资源：\n\n```tsx\nimport { AssetLoader } from '@chamn/render';\n\nconst assetLoader = new AssetLoader(assets, {\n  window: window, // 可选，指定加载资源的 window 对象\n});\n\nawait assetLoader.load({\n  async: false, // 是否并行加载，默认为 false\n});\n```\n\n### collectVariable\n\n从 window 对象收集组件变量：\n\n```tsx\nimport { collectVariable } from '@chamn/render';\n\nconst componentCollection = collectVariable(assets, window);\n```\n\n### flatObject\n\n拍平对象，将嵌套对象展开：\n\n```tsx\nimport { flatObject } from '@chamn/render';\n\nconst flatComponents = flatObject(componentCollection);\n```\n\n### getComponentsLibs / getThirdLibs\n\n区分组件库和第三方库：\n\n```tsx\nimport { getComponentsLibs, getThirdLibs } from '@chamn/render';\n\nconst componentsLibs = getComponentsLibs(flatObject(allLibs), pageInfo.componentsMeta || []);\n\nconst thirdLibs = getThirdLibs(allLibs, pageInfo.thirdLibs || []);\n```\n\n## 注意事项\n\n1. **组件名唯一性**：`components` 对象中的每个 key（组件名）必须唯一\n2. **组件导入**：如果使用第三方组件库（如 antd），需要手动导入并拍平后传递给 `components`\n3. **资源加载**：使用动态加载方式时，确保资源路径正确且可访问\n4. **上下文传递**：通过 `$$context` 传递第三方库时，确保库已正确加载\n5. **渲染模式**：`renderMode` 为 `'design'` 时用于设计时预览，`'normal'` 用于生产环境\n\n## 完整示例\n\n```tsx\nimport { useEffect, useState } from 'react';\nimport { ReactAdapter, Render, useRender } from '@chamn/render';\nimport { CPageDataType } from '@chamn/model';\nimport * as antd from 'antd';\nimport CustomComps from '@custom/material';\n\nconst components = {\n  ...antd,\n  ...CustomComps,\n};\n\nexport const Preview = () => {\n  const [page, setPage] = useState<CPageDataType>();\n  const renderHandle = useRender();\n  const [loading, setLoading] = useState(true);\n\n  useEffect(() => {\n    // 从 API 获取页面协议\n    fetch('/api/page/schema')\n      .then((res) => res.json())\n      .then((data) => {\n        setPage(data);\n        setLoading(false);\n      })\n      .catch((err) => {\n        console.error('Failed to load page:', err);\n        setLoading(false);\n      });\n  }, []);\n\n  if (loading) {\n    return <div>加载中...</div>;\n  }\n\n  if (!page) {\n    return <div>未找到页面数据</div>;\n  }\n\n  return (\n    <div className=\"App\" style={{ overflow: 'auto', height: '100%' }}>\n      <Render\n        page={page}\n        components={components}\n        render={renderHandle}\n        adapter={ReactAdapter}\n        renderMode=\"normal\"\n        requestAPI={async (params) => {\n          // 自定义 API 请求逻辑\n          const response = await fetch('/api/request', {\n            method: 'POST',\n            body: JSON.stringify(params),\n          });\n          return response.json();\n        }}\n      />\n    </div>\n  );\n};\n```\n\n:::tip\n推荐使用方式一（直接导入 npm 包），这种方式更简单、性能更好，且不需要处理资源加载的复杂性。\n:::\n"
  },
  {
    "path": "packages/docs-app/src/content/docs/index.mdx",
    "content": "---\ntitle: Welcome to Chameleon Engine\ndescription: Get started building your docs site with Starlight.\ntemplate: splash\nhero:\n  tagline: Congrats on setting up a new Starlight project!\n  image:\n    file: ../../assets/houston.webp\n  actions:\n    - text: Start Guide\n      link: /chameleon/documents/guides\n      icon: right-arrow\n      variant: primary\n    # - text: Read the docs\n    #   link: /reference/\n    #   icon: external\n---\n\nimport { Card, CardGrid } from '@astrojs/starlight/components';\nimport { Link } from '../../components/Link.tsx';\n\n## Next steps\n\n<CardGrid stagger>\n  <Card title=\"Material Develop\" icon=\"pencil\">\n    How to develop a component that can be used in the editor? <Link url=\"reference/material/introduction/\">Doc</Link>\n  </Card>\n  <Card title=\"Schema Defined\" icon=\"add-document\">\n    Definition of page protocol <Link url=\"reference/pageschema/page/\">Doc</Link>\n  </Card>\n  <Card title=\"Plugins\" icon=\"setting\">\n    How to develop editor plugins? <Link url=\"reference/plugin/plugin-develop\">Doc</Link>\n  </Card>\n  <Card title=\"API\" icon=\"open-book\">\n    API Description, Learn more in <Link url=\"guides\">Doc</Link>\n  </Card>\n</CardGrid>\n"
  },
  {
    "path": "packages/docs-app/src/content/docs/reference/Engine/_category_.json",
    "content": "{\n  \"position\": 1,\n  \"label\": \"Engine\",\n  \"collapsible\": true,\n  \"collapsed\": false,\n  \"customProps\": {\n    \"description\": \"Engine API 和使用文档\"\n  }\n}\n"
  },
  {
    "path": "packages/docs-app/src/content/docs/reference/Engine/api.mdx",
    "content": "---\ntitle: Engine API\nsidebar:\n  order: 2\n---\n\nimport { Tabs, TabItem } from '@astrojs/starlight/components';\n\n# Engine API\n\n本文档详细介绍了 Chameleon Engine 的所有 API，包括组件 Props、实例方法、事件系统等。\n\n## Engine Props\n\nEngine 组件接受以下 props：\n\n| 属性名              | 类型                                                  | 必填 | 说明                                       |\n| :------------------ | :---------------------------------------------------- | :--- | :----------------------------------------- |\n| `plugins`           | `CPlugin[]`                                           | ✅   | 插件列表                                   |\n| `schema`            | `CPageDataType`                                       | ✅   | 页面协议数据                               |\n| `material`          | `CMaterialType[]`                                     | ❌   | 组件物料描述列表                           |\n| `components`        | `Record<string, any>`                                 | ❌   | 组件映射对象                               |\n| `assetPackagesList` | `AssetPackage[]`                                      | ❌   | 资源包列表                                 |\n| `beforePluginRun`   | `(options: { pluginManager: PluginManager }) => void` | ❌   | 插件运行前的回调                           |\n| `onReady`           | `(ctx: EnginContext) => void`                         | ❌   | 所有插件加载完成后的回调                   |\n| `onMount`           | `(ctx: EnginContext) => void`                         | ❌   | 组件挂载后的回调                           |\n| `renderJSUrl`       | `string`                                              | ❌   | 渲染器 UMD JS 地址，默认 `./render.umd.js` |\n| `style`             | `React.CSSProperties`                                 | ❌   | 自定义样式                                 |\n| `className`         | `string`                                              | ❌   | 自定义类名                                 |\n| `renderProps`       | `Partial<RenderPropsType>`                            | ❌   | 渲染器属性配置                             |\n| `workbenchConfig`   | `Partial<WorkbenchPropsType>`                         | ❌   | 工作台配置（初始化时生效）                 |\n| `monacoEditor`      | `{ cndUrl?: string }`                                 | ❌   | Monaco 编辑器配置                          |\n\n### EnginContext\n\n`EnginContext` 是生命周期回调函数中接收的上下文对象，包含引擎实例和插件管理器。\n\n```typescript\ntype EnginContext = {\n  pluginManager: PluginManager;\n  engine: Engine;\n};\n```\n\n**使用示例：**\n\n```tsx\nconst onReady = (ctx: EnginContext) => {\n  // 访问插件管理器\n  const pluginManager = ctx.pluginManager;\n\n  // 访问引擎实例\n  const engine = ctx.engine;\n\n  // 获取页面模型\n  const pageModel = engine.pageModel;\n};\n```\n\n### EngineProps 详细说明\n\n#### plugins\n\n插件列表，必填。可以是内置插件或自定义插件。\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\n<Engine\n  plugins={DEFAULT_PLUGIN_LIST}\n  // 或自定义插件列表\n  // plugins={[DesignerPlugin, ComponentLibPlugin]}\n/>;\n```\n\n#### schema\n\n页面协议数据，必填。定义了页面的结构、组件、样式等信息。\n\n```tsx\nconst pageSchema = {\n  version: '1.0.0',\n  name: 'MyPage',\n  componentsTree: {\n    id: 'root',\n    componentName: 'Page',\n    children: [],\n  },\n  // ... 其他字段\n};\n\n<Engine schema={pageSchema} />;\n```\n\n#### material\n\n组件物料描述列表，可选。定义了组件的属性、事件、样式等信息。\n\n```tsx\nconst materials = [\n  {\n    componentName: 'Button',\n    title: '按钮',\n    props: [\n      {\n        name: 'text',\n        title: '文本',\n        setter: 'StringSetter',\n      },\n    ],\n  },\n];\n\n<Engine material={materials} />;\n```\n\n#### components\n\n组件映射对象，可选。用于直接传入组件实例，而不是通过资源包加载。\n\n```tsx\nimport { Button } from 'antd';\n\n<Engine\n  components={{\n    Button: Button,\n  }}\n/>;\n```\n\n#### assetPackagesList\n\n资源包列表，可选。定义了组件库的 JS/CSS 资源地址。\n\n```tsx\nconst assetPackagesList = [\n  {\n    package: 'antd',\n    version: '5.0.0',\n    urls: ['https://cdn.example.com/antd.js', 'https://cdn.example.com/antd.css'],\n  },\n];\n\n<Engine assetPackagesList={assetPackagesList} />;\n```\n\n#### workbenchConfig\n\n工作台配置，可选。用于初始化时配置工作台的显示状态。\n\n```tsx\n<Engine\n  workbenchConfig={{\n    hiddenLeftPanel: false,\n    hiddenRightPanel: false,\n    hiddenTopBar: false,\n    canvasFull: false,\n  }}\n/>\n```\n\n:::tip\n`workbenchConfig` 只在初始化时生效，后续需要通过 `hiddenWidget` API 来动态修改。\n:::\n\n## Engine 实例属性\n\nEngine 实例提供了以下公共属性：\n\n| 属性名                     | 类型                           | 说明           |\n| :------------------------- | :----------------------------- | :------------- |\n| `pluginManager`            | `PluginManager`                | 插件管理器实例 |\n| `pageModel`                | `CPage`                        | 页面模型实例   |\n| `emitter`                  | `Emitter<any>`                 | 事件发射器     |\n| `currentSelectNode`        | `CNode \\| CRootNode \\| null`   | 当前选中的节点 |\n| `material`                 | `CMaterialType[] \\| undefined` | 组件物料列表   |\n| `pageSchema`               | `CPageDataType \\| undefined`   | 页面协议数据   |\n| `assetsPackageListManager` | `AssetsPackageListManager`     | 资源包管理器   |\n\n**访问方式：**\n\n```tsx\nconst onReady = (ctx: EnginContext) => {\n  // 通过上下文访问\n  const engine = ctx.engine;\n  const pageModel = engine.pageModel;\n  const pluginManager = ctx.pluginManager;\n\n  // 访问页面模型\n  const rootNode = pageModel.getRootNode();\n  const allNodes = pageModel.getAllNodes();\n\n  // 访问事件发射器\n  engine.emitter.on('onSelectNodeChange', ({ node }) => {\n    console.log('节点变化:', node);\n  });\n};\n```\n\n## Engine 实例方法\n\n### updatePage\n\n更新页面数据。会重新加载整个页面，所有节点都会被重新创建。\n\n```typescript\nupdatePage(page: CPageDataType): void\n```\n\n**参数：**\n\n- `page`: 新的页面协议数据\n\n**示例：**\n\n```tsx\nconst onReady = (ctx: EnginContext) => {\n  // 从服务器获取新页面数据\n  fetch('/api/page')\n    .then((res) => res.json())\n    .then((newPageSchema) => {\n      ctx.engine.updatePage(newPageSchema);\n    });\n};\n\n// 或者使用本地数据\nconst newPageSchema = {\n  version: '1.0.0',\n  name: 'NewPage',\n  componentsTree: {\n    /* ... */\n  },\n};\nctx.engine.updatePage(newPageSchema);\n```\n\n:::warning\n调用 `updatePage` 会完全替换当前页面，所有未保存的更改都会丢失。建议在更新前先保存当前页面数据。\n:::\n\n### updateMaterials\n\n动态更新物料和资源包。这个方法会：\n\n1. 加载新的资源包到设计器窗口\n2. 收集组件变量\n3. 更新渲染器中的组件库\n4. 更新页面模型中的物料\n\n```typescript\nupdateMaterials(\n  materials: CMaterialType[],\n  assetPackagesList: AssetPackage[],\n  options?: {\n    formatComponents?: (componentMap: ComponentsType) => ComponentsType;\n  }\n): Promise<void>\n```\n\n**参数：**\n\n- `materials`: 新的组件物料描述列表\n- `assetPackagesList`: 新的资源包列表\n- `options.formatComponents`: 可选的组件格式化函数，用于自定义组件映射\n\n**示例：**\n\n```tsx\nconst onReady = async (ctx: EnginContext) => {\n  // 从服务器动态加载物料\n  const [materials, assetPackages] = await Promise.all([\n    fetch('/api/materials').then((r) => r.json()),\n    fetch('/api/asset-packages').then((r) => r.json()),\n  ]);\n\n  await ctx.engine.updateMaterials(materials, assetPackages, {\n    formatComponents: (components) => {\n      // 例如：给所有组件添加前缀\n      const formatted: Record<string, any> = {};\n      Object.keys(components).forEach((key) => {\n        formatted[`Custom${key}`] = components[key];\n      });\n      return formatted;\n    },\n  });\n\n  console.log('物料更新完成');\n};\n```\n\n:::tip\n`updateMaterials` 是异步方法，需要等待资源包加载完成。建议在更新后监听 `updateMaterials` 事件来确认更新完成。\n:::\n\n### refresh\n\n刷新页面\n\n```typescript\nrefresh(): Promise<void>\n```\n\n**示例：**\n\n```tsx\nconst onReady = async (ctx: EnginContext) => {\n  await ctx.engine.refresh();\n};\n```\n\n### getActiveNode\n\n获取当前选中的节点\n\n```typescript\ngetActiveNode(): CNode | CRootNode | null\n```\n\n**示例：**\n\n```tsx\nconst onReady = (ctx: EnginContext) => {\n  const activeNode = ctx.engine.getActiveNode();\n  console.log('当前选中节点:', activeNode);\n};\n```\n\n### preview\n\n进入预览模式\n\n```typescript\npreview(): Promise<void>\n```\n\n预览模式会隐藏所有编辑相关的面板，只显示画布内容。\n\n**示例：**\n\n```tsx\nconst onReady = async (ctx: EnginContext) => {\n  await ctx.engine.preview();\n};\n```\n\n### existPreview\n\n退出预览模式\n\n```typescript\nexistPreview(): Promise<void>\n```\n\n**示例：**\n\n```tsx\nconst onReady = async (ctx: EnginContext) => {\n  await ctx.engine.existPreview();\n};\n```\n\n### hiddenWidget\n\n隐藏或显示工作台组件\n\n```typescript\nhiddenWidget(config: Partial<TWidgetVisible>): void\n```\n\n**配置项：**\n\n| 属性名             | 类型      | 说明         |\n| :----------------- | :-------- | :----------- |\n| `hiddenTopBar`     | `boolean` | 隐藏顶部栏   |\n| `hiddenLeftPanel`  | `boolean` | 隐藏左侧面板 |\n| `hiddenRightPanel` | `boolean` | 隐藏右侧面板 |\n| `canvasFull`       | `boolean` | 画布全屏     |\n\n**示例：**\n\n```tsx\nconst onReady = (ctx: EnginContext) => {\n  // 隐藏左侧面板\n  ctx.engine.hiddenWidget({\n    hiddenLeftPanel: true,\n  });\n\n  // 全屏画布\n  ctx.engine.hiddenWidget({\n    hiddenTopBar: true,\n    hiddenLeftPanel: true,\n    hiddenRightPanel: true,\n    canvasFull: true,\n  });\n};\n```\n\n### getWorkbench\n\n获取工作台实例。工作台提供了丰富的 API 用于自定义编辑器界面。\n\n```typescript\ngetWorkbench(): Workbench | null\n```\n\n**返回值：**\n\n- `Workbench | null`: 工作台实例，如果工作台未初始化则返回 `null`\n\n**示例：**\n\n```tsx\nconst onReady = (ctx: EnginContext) => {\n  const workbench = ctx.engine.getWorkbench();\n\n  if (!workbench) {\n    console.warn('工作台未初始化');\n    return;\n  }\n\n  // 自定义顶部栏\n  workbench.replaceTopBarView(<CustomTopBar />);\n\n  // 自定义画布区域\n  workbench.replaceBodyView(<CustomCanvas />);\n\n  // 自定义右侧面板\n  workbench.replaceRightView(<CustomRightPanel />);\n};\n```\n\n:::info\n工作台 API 的详细说明请参考 [Workbench API](#workbench-api) 章节。\n:::\n\n### getI18n\n\n获取国际化对象\n\n```typescript\ngetI18n(): CustomI18n\n```\n\n**示例：**\n\n```tsx\nconst onReady = (ctx: EnginContext) => {\n  const i18n = ctx.engine.getI18n();\n  const text = i18n.t('some.key');\n};\n```\n\n### updateCurrentSelectNode\n\n更新当前选中的节点（内部方法，通常由插件调用）\n\n```typescript\nupdateCurrentSelectNode(node: CNode | CRootNode | null): void\n```\n\n**参数：**\n\n- `node`: 要选中的节点，传入 `null` 表示取消选中\n\n**说明：**\n\n这个方法会触发 `onSelectNodeChange` 事件，通常由设计器插件在用户点击节点时调用。\n\n## 静态属性\n\n### version\n\n获取 Engine 版本号\n\n```typescript\nEngine.version: string\n```\n\n**示例：**\n\n```tsx\nimport { Engine } from '@chamn/engine';\n\nconsole.log(Engine.version); // \"0.9.3\"\n\n// 用于版本检查\nif (Engine.version >= '0.9.0') {\n  // 使用新特性\n}\n```\n\n## 事件系统\n\nEngine 内部使用 `mitt` 实现事件系统，可以通过 `emitter` 监听和触发事件。\n\n### 监听事件\n\n```typescript\n// 监听事件\nengine.emitter.on('eventName', (data) => {\n  // 处理事件\n});\n\n// 取消监听\nconst handler = (data) => {\n  /* ... */\n};\nengine.emitter.on('eventName', handler);\nengine.emitter.off('eventName', handler);\n\n// 只监听一次\nengine.emitter.once('eventName', (data) => {\n  // 只执行一次\n});\n```\n\n### 事件列表\n\n#### onSelectNodeChange\n\n节点选中变化事件。当用户点击画布中的节点时触发。\n\n```typescript\nctx.engine.emitter.on('onSelectNodeChange', ({ node }) => {\n  if (node) {\n    console.log('选中节点:', node);\n    console.log('节点ID:', node.id);\n    console.log('组件名:', node.componentName);\n    console.log('节点属性:', node.props);\n  } else {\n    console.log('取消选中');\n  }\n});\n```\n\n**事件数据：**\n\n```typescript\n{\n  node: CNode | CRootNode | null;\n}\n```\n\n**示例：**\n\n```tsx\nconst onReady = (ctx: EnginContext) => {\n  ctx.engine.emitter.on('onSelectNodeChange', ({ node }) => {\n    if (node) {\n      // 更新外部状态\n      setSelectedNode(node);\n\n      // 显示节点信息\n      message.info(`选中了 ${node.componentName} 组件`);\n    }\n  });\n};\n```\n\n#### updateMaterials\n\n物料更新事件。当调用 `updateMaterials` 方法完成物料更新后触发。\n\n```typescript\nctx.engine.emitter.on('updateMaterials', () => {\n  console.log('物料已更新');\n  // 可以在这里刷新组件库面板等\n});\n```\n\n**示例：**\n\n```tsx\nconst onReady = (ctx: EnginContext) => {\n  ctx.engine.emitter.on('updateMaterials', () => {\n    // 刷新组件库\n    refreshComponentLibrary();\n\n    // 显示提示\n    message.success('物料更新成功');\n  });\n\n  // 更新物料\n  ctx.engine.updateMaterials(newMaterials, newAssetPackages);\n};\n```\n\n### 触发自定义事件\n\n你也可以通过 `emitter.emit` 触发自定义事件：\n\n```tsx\nconst onReady = (ctx: EnginContext) => {\n  // 触发自定义事件\n  ctx.engine.emitter.emit('customEvent', {\n    data: 'some data',\n  });\n\n  // 在其他地方监听\n  ctx.engine.emitter.on('customEvent', ({ data }) => {\n    console.log('收到自定义事件:', data);\n  });\n};\n```\n\n## Workbench API\n\n通过 `getWorkbench()` 获取工作台实例后，可以使用以下方法自定义编辑器界面。\n\n### replaceTopBarView\n\n替换顶部工具栏视图\n\n```typescript\nreplaceTopBarView(newView: React.ReactNode): void\n```\n\n**示例：**\n\n```tsx\nconst onReady = (ctx: EnginContext) => {\n  const workbench = ctx.engine.getWorkbench();\n  workbench?.replaceTopBarView(\n    <div style={{ padding: '0 20px', display: 'flex', justifyContent: 'space-between' }}>\n      <div>我的编辑器</div>\n      <Button onClick={() => ctx.engine.preview()}>预览</Button>\n    </div>\n  );\n};\n```\n\n### replaceBodyView\n\n替换画布区域视图\n\n```typescript\nreplaceBodyView(newView: React.ReactNode): void\n```\n\n**示例：**\n\n```tsx\nconst onReady = (ctx: EnginContext) => {\n  const workbench = ctx.engine.getWorkbench();\n  workbench?.replaceBodyView(<CustomCanvas />);\n};\n```\n\n### replaceRightView\n\n替换右侧面板视图\n\n```typescript\nreplaceRightView(newView: React.ReactNode): void\n```\n\n**示例：**\n\n```tsx\nconst onReady = (ctx: EnginContext) => {\n  const workbench = ctx.engine.getWorkbench();\n  workbench?.replaceRightView(<CustomPropertyPanel />);\n};\n```\n\n### replaceSubTopBarView\n\n替换子顶部工具栏视图\n\n```typescript\nreplaceSubTopBarView(newView: React.ReactNode): void\n```\n\n### addLeftPanel\n\n添加左侧面板\n\n```typescript\naddLeftPanel(panel: PanelItem): void\n```\n\n**PanelItem 类型：**\n\n```typescript\ntype PanelItem = {\n  name: string;\n  title: string | React.ReactNode;\n  icon: React.ReactNode;\n  view: React.ReactNode;\n};\n```\n\n**示例：**\n\n```tsx\nconst onReady = (ctx: EnginContext) => {\n  const workbench = ctx.engine.getWorkbench();\n  workbench?.addLeftPanel({\n    name: 'MyPanel',\n    title: '我的面板',\n    icon: <Icon />,\n    view: <MyPanelView />,\n  });\n};\n```\n\n### replaceLeftPanel\n\n替换指定的左侧面板\n\n```typescript\nreplaceLeftPanel(panelName: string, newPanel: PanelItem): void\n```\n\n### openLeftPanel / closeLeftPanel\n\n打开/关闭左侧面板\n\n```typescript\nopenLeftPanel(currentActiveLeftPanel?: string): Promise<void>\ncloseLeftPanel(): Promise<void>\n```\n\n### toggleLeftPanel\n\n切换左侧面板显示状态\n\n```typescript\ntoggleLeftPanel(): void\n```\n\n### openRightPanel / closeRightPanel\n\n打开/关闭右侧面板\n\n```typescript\nopenRightPanel(): void\ncloseRightPanel(): void\n```\n\n### toggleRightPanel\n\n切换右侧面板显示状态\n\n```typescript\ntoggleRightPanel(): void\n```\n\n### addCustomView\n\n添加自定义视图\n\n```typescript\naddCustomView(view: WorkbenchCustomView): () => void\n```\n\n**返回值：** 返回一个 dispose 函数，调用后移除该视图\n\n**示例：**\n\n```tsx\nconst onReady = (ctx: EnginContext) => {\n  const workbench = ctx.engine.getWorkbench();\n  const dispose = workbench?.addCustomView({\n    name: 'MyCustomView',\n    view: <MyCustomView />,\n  });\n\n  // 稍后移除\n  setTimeout(() => {\n    dispose?.();\n  }, 5000);\n};\n```\n\n### getHiddenWidgetConfig\n\n获取当前工作台组件的隐藏配置\n\n```typescript\ngetHiddenWidgetConfig(): {\n  hiddenTopBar: boolean;\n  hiddenLeftPanel: boolean;\n  hiddenRightPanel: boolean;\n}\n```\n\n## PluginManager API\n\n通过 `ctx.pluginManager` 可以访问插件管理器，用于管理插件的生命周期。\n\n### get\n\n获取插件实例\n\n```typescript\nget<P extends PluginInstance<any, any>>(pluginName: string): Promise<P | undefined>\n```\n\n**示例：**\n\n```tsx\nconst onReady = async (ctx: EnginContext) => {\n  // 获取设计器插件\n  const designerPlugin = await ctx.pluginManager.get('Designer');\n  const designerExport = designerPlugin?.export;\n\n  // 调用插件方法\n  designerExport?.reload();\n};\n```\n\n### add\n\n添加插件\n\n```typescript\nadd(plugin: CPlugin): Promise<void>\n```\n\n### remove\n\n移除插件\n\n```typescript\nremove(name: string): Promise<void>\n```\n\n### customPlugin\n\n自定义插件配置。允许在插件初始化之前修改插件的配置。\n\n```typescript\ncustomPlugin<P extends PluginInstance<any, any>>(\n  pluginName: string,\n  customPluginHook: CustomPluginHook<P>\n): void\n```\n\n**参数：**\n\n- `pluginName`: 插件名称\n- `customPluginHook`: 自定义钩子函数，接收插件实例并返回修改后的插件实例\n\n**示例：**\n\n```tsx\nconst beforePluginRun = ({ pluginManager }: { pluginManager: PluginManager }) => {\n  // 自定义设计器插件配置\n  pluginManager.customPlugin('Designer', (pluginInstance) => {\n    // 自定义渲染器\n    pluginInstance.ctx.config.customRender = customRender;\n\n    // 自定义 beforeInitRender\n    pluginInstance.ctx.config.beforeInitRender = async ({ iframe }) => {\n      const subWin = iframe.getWindow();\n      if (!subWin) return;\n\n      // 注入全局变量\n      (subWin as any).React = window.React;\n      (subWin as any).ReactDOM = window.ReactDOM;\n\n      // 注入自定义全局对象\n      (subWin as any).myCustomGlobal = {\n        config: {},\n        utils: {},\n      };\n    };\n\n    return pluginInstance;\n  });\n};\n\n<Engine\n  beforePluginRun={beforePluginRun}\n  // ... 其他 props\n/>;\n```\n\n### onPluginReadyOk\n\n等待插件准备完成\n\n```typescript\nonPluginReadyOk(\n  pluginName: string,\n  cb?: (pluginHandle: PluginInstance) => void\n): Promise<PluginInstance>\n```\n\n## 访问 Engine 实例\n\nEngine 实例会被挂载到全局对象上，可以通过以下方式访问：\n\n```typescript\n// 方式1: 通过 window.__CHAMELEON_ENG__ (构造函数中挂载)\nconst engine = (window as any).__CHAMELEON_ENG__;\n\n// 方式2: 通过 window.__C_ENGINE__ (componentDidMount 中挂载)\nconst engine = (window as any).__C_ENGINE__;\n\n// 方式3: 通过 onReady 回调（推荐）\nconst onReady = (ctx: EnginContext) => {\n  const engine = ctx.engine;\n};\n\n// 方式4: 通过 ref（如果使用函数组件）\nconst engineRef = useRef<Engine>(null);\n<Engine ref={engineRef} />;\n```\n\n:::tip\n推荐使用 `onReady` 回调来访问 Engine 实例，这样可以确保所有插件都已加载完成。\n:::\n\n## 类型定义\n\n### CPageDataType\n\n页面协议数据类型，定义了页面的完整结构。\n\n```typescript\ntype CPageDataType = {\n  version: string;\n  name: string;\n  css?: CSSType;\n  componentsMeta?: ComponentMetaType[];\n  thirdLibs?: LibMetaType[];\n  componentsTree: CRootNodeDataType;\n  assets?: AssetPackage[];\n};\n```\n\n### CMaterialType\n\n组件物料类型，定义了组件的描述信息。\n\n```typescript\ntype CMaterialType = {\n  componentName: string;\n  title: string;\n  icon?: React.ReactNode;\n  props?: CMaterialPropType[];\n  // ... 更多字段\n};\n```\n\n### AssetPackage\n\n资源包类型，定义了组件库的资源信息。\n\n```typescript\ntype AssetPackage = {\n  package: string;\n  version: string;\n  urls: string[];\n  library?: string;\n};\n```\n\n更多类型定义请参考 `@chamn/model` 包的文档。\n\n## 完整示例\n\n以下是一个完整的示例，展示了 Engine API 的综合使用：\n\n```tsx\nimport { Engine, plugins, EnginContext } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\nimport { Button, message } from 'antd';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  const [pageSchema, setPageSchema] = useState(initialPageSchema);\n\n  const onMount = (ctx: EnginContext) => {\n    console.log('Engine mounted');\n  };\n\n  const onReady = async (ctx: EnginContext) => {\n    console.log('Engine ready!');\n\n    const { engine, pluginManager } = ctx;\n\n    // 1. 获取工作台实例并自定义界面\n    const workbench = engine.getWorkbench();\n    workbench?.replaceTopBarView(\n      <div style={{ padding: '0 20px', display: 'flex', justifyContent: 'space-between' }}>\n        <div>我的编辑器</div>\n        <div>\n          <Button onClick={() => engine.preview()}>预览</Button>\n          <Button onClick={() => engine.existPreview()}>退出预览</Button>\n          <Button onClick={handleSave}>保存</Button>\n        </div>\n      </div>\n    );\n\n    // 2. 监听节点选中变化\n    engine.emitter.on('onSelectNodeChange', ({ node }) => {\n      if (node) {\n        console.log('选中节点:', node);\n        message.info(`选中了 ${node.componentName} 组件`);\n      }\n    });\n\n    // 3. 监听物料更新\n    engine.emitter.on('updateMaterials', () => {\n      message.success('物料已更新');\n    });\n\n    // 4. 获取插件实例\n    const designerPlugin = await pluginManager.get('Designer');\n    const historyPlugin = await pluginManager.get('History');\n\n    // 5. 添加自定义左侧面板\n    workbench?.addLeftPanel({\n      name: 'CustomPanel',\n      title: '自定义面板',\n      icon: <Icon />,\n      view: <CustomPanelView engine={engine} />,\n    });\n  };\n\n  const handleSave = () => {\n    const engine = (window as any).__C_ENGINE__;\n    if (engine) {\n      const pageData = engine.pageModel.export();\n      localStorage.setItem('pageSchema', JSON.stringify(pageData));\n      message.success('保存成功');\n    }\n  };\n\n  const handleUpdateMaterials = async () => {\n    const engine = (window as any).__C_ENGINE__;\n    if (engine) {\n      const [materials, assetPackages] = await fetchMaterials();\n      await engine.updateMaterials(materials, assetPackages);\n    }\n  };\n\n  return (\n    <Engine\n      plugins={DEFAULT_PLUGIN_LIST}\n      schema={pageSchema}\n      material={materials}\n      assetPackagesList={assetPackagesList}\n      onReady={onReady}\n      onMount={onMount}\n      workbenchConfig={{\n        hiddenLeftPanel: false,\n        hiddenRightPanel: false,\n      }}\n    />\n  );\n}\n```\n\n## 常见问题\n\n### Q: 如何确保在访问 Engine 实例时所有插件都已加载完成？\n\nA: 使用 `onReady` 回调，它会在所有插件加载完成后调用：\n\n```tsx\nconst onReady = async (ctx: EnginContext) => {\n  // 此时所有插件都已加载完成\n  const designerPlugin = await ctx.pluginManager.get('Designer');\n};\n```\n\n### Q: 如何动态更新页面数据？\n\nA: 使用 `updatePage` 方法：\n\n```tsx\nctx.engine.updatePage(newPageSchema);\n```\n\n### Q: 如何监听页面节点的变化？\n\nA: 可以通过页面模型的 `emitter` 监听：\n\n```tsx\nctx.engine.pageModel.emitter.on('onReloadPage', () => {\n  console.log('页面已重新加载');\n});\n```\n\n### Q: 如何自定义渲染器？\n\nA: 在 `beforePluginRun` 中自定义设计器插件的渲染配置：\n\n```tsx\nconst beforePluginRun = ({ pluginManager }) => {\n  pluginManager.customPlugin('Designer', (pluginInstance) => {\n    pluginInstance.ctx.config.customRender = customRender;\n    return pluginInstance;\n  });\n};\n```\n\n## beforeInitRender 详解\n\n`beforeInitRender` 是设计器插件配置中的一个重要钩子函数，它在渲染器初始化**之前**被调用，用于准备 iframe 环境。\n\n### 函数签名\n\n```typescript\ntype BeforeInitRender = (params: { iframe: IframeContainer }) => Promise<void> | void;\n```\n\n### 执行时机\n\n```\n1. 创建 iframe\n2. 加载 iframe URL/HTML\n3. ⭐ 调用 beforeInitRender（在这里初始化环境）\n4. 注入渲染器 JS\n5. 加载组件库资源\n6. 渲染页面\n```\n\n### 默认实现\n\nEngine 默认的 `beforeInitRender` 实现会将 React 相关库注入到 iframe 窗口中：\n\n```typescript\nconst beforeInitRender = async ({ iframe }) => {\n  const subWin = iframe.getWindow();\n  if (!subWin) return;\n\n  // 注入 React 18 相关库\n  (subWin as any).React = window.React;\n  (subWin as any).ReactDOM = window.ReactDOM;\n  (subWin as any).ReactDOMClient = window.ReactDOMClient;\n};\n```\n\n### 使用场景\n\n#### 1. 注入全局变量\n\n向 iframe 中注入自定义的全局变量或对象：\n\n```tsx\nconst beforePluginRun = ({ pluginManager }) => {\n  pluginManager.customPlugin('Designer', (pluginInstance) => {\n    pluginInstance.ctx.config.beforeInitRender = async ({ iframe }) => {\n      const subWin = iframe.getWindow();\n      if (!subWin) return;\n\n      // 注入配置对象\n      (subWin as any).APP_CONFIG = {\n        apiUrl: 'https://api.example.com',\n        theme: 'light',\n      };\n\n      // 注入工具函数\n      (subWin as any).utils = {\n        formatDate: (date) => {\n          /* ... */\n        },\n        request: (url) => {\n          /* ... */\n        },\n      };\n    };\n\n    return pluginInstance;\n  });\n};\n```\n\n#### 2. 配置跨窗口通信\n\n设置主窗口和 iframe 之间的通信机制：\n\n```tsx\nconst beforePluginRun = ({ pluginManager }) => {\n  pluginManager.customPlugin('Designer', (pluginInstance) => {\n    pluginInstance.ctx.config.beforeInitRender = async ({ iframe }) => {\n      const subWin = iframe.getWindow();\n      if (!subWin) return;\n\n      // 设置消息监听\n      subWin.addEventListener('message', (event) => {\n        if (event.data.type === 'customEvent') {\n          console.log('收到来自组件的消息:', event.data);\n        }\n      });\n\n      // 注入发送消息的方法\n      (subWin as any).sendToParent = (data) => {\n        window.postMessage(data, '*');\n      };\n    };\n\n    return pluginInstance;\n  });\n};\n```\n\n#### 3. 注入第三方库\n\n在渲染前注入必要的第三方库：\n\n```tsx\nconst beforePluginRun = ({ pluginManager }) => {\n  pluginManager.customPlugin('Designer', (pluginInstance) => {\n    pluginInstance.ctx.config.beforeInitRender = async ({ iframe }) => {\n      const subWin = iframe.getWindow();\n      if (!subWin) return;\n\n      // 注入 lodash\n      (subWin as any)._ = window._;\n\n      // 注入 moment\n      (subWin as any).moment = window.moment;\n\n      // 注入 axios\n      (subWin as any).axios = window.axios;\n    };\n\n    return pluginInstance;\n  });\n};\n```\n\n#### 4. 初始化样式或主题\n\n预先设置 iframe 的样式或主题：\n\n```tsx\nconst beforePluginRun = ({ pluginManager }) => {\n  pluginManager.customPlugin('Designer', (pluginInstance) => {\n    pluginInstance.ctx.config.beforeInitRender = async ({ iframe }) => {\n      const subWin = iframe.getWindow();\n      const subDoc = iframe.getDocument();\n      if (!subWin || !subDoc) return;\n\n      // 注入主题变量\n      (subWin as any).THEME = {\n        primaryColor: '#1890ff',\n        fontSize: '14px',\n      };\n\n      // 添加全局样式\n      const style = subDoc.createElement('style');\n      style.textContent = `\n        * {\n          box-sizing: border-box;\n        }\n        body {\n          margin: 0;\n          padding: 0;\n          font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto;\n        }\n      `;\n      subDoc.head.appendChild(style);\n    };\n\n    return pluginInstance;\n  });\n};\n```\n\n#### 5. 设置调试工具\n\n在开发环境中注入调试工具：\n\n```tsx\nconst beforePluginRun = ({ pluginManager }) => {\n  pluginManager.customPlugin('Designer', (pluginInstance) => {\n    pluginInstance.ctx.config.beforeInitRender = async ({ iframe }) => {\n      const subWin = iframe.getWindow();\n      if (!subWin) return;\n\n      if (process.env.NODE_ENV === 'development') {\n        // 注入调试工具\n        (subWin as any).__DEBUG__ = {\n          logProps: (componentName) => {\n            console.log(`组件 ${componentName} 的 props:` /* ... */);\n          },\n          inspect: (node) => {\n            console.log('节点信息:', node);\n          },\n        };\n\n        // 启用详细日志\n        (subWin as any).__ENABLE_VERBOSE_LOGGING__ = true;\n      }\n    };\n\n    return pluginInstance;\n  });\n};\n```\n\n#### 6. 模拟数据或 API\n\n为组件提供模拟数据或 API：\n\n```tsx\nconst beforePluginRun = ({ pluginManager }) => {\n  pluginManager.customPlugin('Designer', (pluginInstance) => {\n    pluginInstance.ctx.config.beforeInitRender = async ({ iframe }) => {\n      const subWin = iframe.getWindow();\n      if (!subWin) return;\n\n      // 注入模拟 API\n      (subWin as any).mockAPI = {\n        getUserInfo: async () => ({\n          id: 1,\n          name: 'Test User',\n          email: 'test@example.com',\n        }),\n        getProducts: async () => [\n          { id: 1, name: 'Product 1', price: 100 },\n          { id: 2, name: 'Product 2', price: 200 },\n        ],\n      };\n    };\n\n    return pluginInstance;\n  });\n};\n```\n\n### 完整示例\n\n结合多个场景的完整示例：\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  const beforePluginRun = ({ pluginManager }) => {\n    pluginManager.customPlugin('Designer', (pluginInstance) => {\n      pluginInstance.ctx.config.beforeInitRender = async ({ iframe }) => {\n        const subWin = iframe.getWindow();\n        const subDoc = iframe.getDocument();\n        if (!subWin || !subDoc) return;\n\n        // 1. 注入 React（必需）\n        (subWin as any).React = window.React;\n        (subWin as any).ReactDOM = window.ReactDOM;\n        (subWin as any).ReactDOMClient = window.ReactDOMClient;\n\n        // 2. 注入应用配置\n        (subWin as any).APP_CONFIG = {\n          apiUrl: process.env.REACT_APP_API_URL,\n          environment: process.env.NODE_ENV,\n          version: '1.0.0',\n        };\n\n        // 3. 注入第三方库\n        (subWin as any).axios = window.axios;\n        (subWin as any).dayjs = window.dayjs;\n\n        // 4. 设置通信\n        (subWin as any).sendToEditor = (data) => {\n          window.postMessage(\n            {\n              source: 'iframe',\n              ...data,\n            },\n            '*'\n          );\n        };\n\n        // 5. 添加全局样式\n        const style = subDoc.createElement('style');\n        style.textContent = `\n          :root {\n            --primary-color: #1890ff;\n            --text-color: #333;\n          }\n        `;\n        subDoc.head.appendChild(style);\n\n        // 6. 开发环境调试工具\n        if (process.env.NODE_ENV === 'development') {\n          (subWin as any).__DEBUG__ = {\n            log: (...args) => console.log('[iframe]', ...args),\n            error: (...args) => console.error('[iframe]', ...args),\n          };\n        }\n      };\n\n      return pluginInstance;\n    });\n  };\n\n  return (\n    <Engine plugins={DEFAULT_PLUGIN_LIST} schema={pageSchema} material={materials} beforePluginRun={beforePluginRun} />\n  );\n}\n```\n\n### 注意事项\n\n1. **必须注入 React**：如果使用默认渲染器，必须确保注入 React、ReactDOM 和 ReactDOMClient\n2. **异步操作**：`beforeInitRender` 支持异步操作，可以使用 `async/await`\n3. **错误处理**：建议添加错误处理，确保 iframe 初始化不会失败\n4. **性能考虑**：避免在 `beforeInitRender` 中执行耗时操作\n5. **安全性**：注意不要注入敏感信息到 iframe 中\n\n### 与 customRender 的区别\n\n| 特性         | beforeInitRender       | customRender       |\n| :----------- | :--------------------- | :----------------- |\n| **执行时机** | 渲染器加载之前         | 渲染页面时         |\n| **主要用途** | 初始化 iframe 环境     | 自定义渲染逻辑     |\n| **是否必须** | 否（有默认实现）       | 否（有默认实现）   |\n| **典型场景** | 注入全局变量、配置环境 | 自定义页面渲染方式 |\n\n### 调试技巧\n\n```tsx\nconst beforePluginRun = ({ pluginManager }) => {\n  pluginManager.customPlugin('Designer', (pluginInstance) => {\n    pluginInstance.ctx.config.beforeInitRender = async ({ iframe }) => {\n      const subWin = iframe.getWindow();\n      if (!subWin) {\n        console.error('无法获取 iframe window');\n        return;\n      }\n\n      console.log('beforeInitRender 开始执行');\n\n      // 注入变量\n      (subWin as any).React = window.React;\n\n      // 验证注入是否成功\n      console.log('React 注入成功:', !!(subWin as any).React);\n\n      // 监听 iframe 中的错误\n      subWin.addEventListener('error', (event) => {\n        console.error('iframe 错误:', event.error);\n      });\n\n      console.log('beforeInitRender 执行完成');\n    };\n\n    return pluginInstance;\n  });\n};\n```\n\n### Q: 如何获取当前页面的完整数据？\n\nA: 使用页面模型的 `export` 方法：\n\n```tsx\nconst pageData = ctx.engine.pageModel.export();\n// 或导出特定格式\nconst designData = ctx.engine.pageModel.export('design');\n```\n"
  },
  {
    "path": "packages/docs-app/src/content/docs/reference/Engine/introduction.mdx",
    "content": "---\ntitle: Engine 介绍\nsidebar:\n  order: 1\n---\n\nimport { Tabs, TabItem } from '@astrojs/starlight/components';\n\n# Engine 介绍\n\n`@chamn/engine` 是 Chameleon 可视化编程引擎的核心包，提供了一个完整的低代码编辑器解决方案。它基于 React 构建，提供了丰富的 API 和插件系统，让你可以快速构建自己的可视化页面编辑器。\n\n## 核心特性\n\n- 🎨 **可视化编辑**: 通过拖拽的方式快速构建页面\n- 🔌 **插件化架构**: 灵活的插件系统，支持自定义扩展\n- 📦 **物料管理**: 完善的组件物料管理机制\n- 🎯 **实时预览**: 支持实时预览和编辑模式切换\n- 🌐 **国际化支持**: 内置国际化能力\n- 🎛️ **工作台定制**: 可自定义的工作台布局和组件\n\n## 安装\n\n<Tabs>\n  <TabItem label=\"npm\">```shell npm i @chamn/engine @chamn/model @chamn/render ```</TabItem>\n  <TabItem label=\"pnpm\">```shell pnpm i @chamn/engine @chamn/model @chamn/render ```</TabItem>\n  <TabItem label=\"yarn\">```shell yarn i @chamn/engine @chamn/model @chamn/render ```</TabItem>\n</Tabs>\n\n## 快速开始\n\n最简单的使用方式：\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  return (\n    <Engine\n      plugins={DEFAULT_PLUGIN_LIST}\n      schema={pageSchema}\n      material={materials}\n      onReady={(ctx) => {\n        console.log('Engine ready!', ctx);\n      }}\n    />\n  );\n}\n```\n\n## 引擎架构\n\nChameleon Engine 采用分层架构设计，各模块之间通过清晰的依赖关系组织。以下是引擎的核心架构图：\n\n```mermaid\nflowchart TB\n    subgraph Engine[\"Engine\"]\n        direction TB\n        subgraph PluginManager[\"pluginManager\"]\n            direction TB\n            OutlineTree[\"outlineTree\"]\n            PropertyPanel[\"propertyPanel\"]\n            StylePanel[\"stylePanel\"]\n            OtherPlugins[\"...\"]\n        end\n\n        Workbench[\"workbench\"]\n\n        subgraph Designer[\"Designer(plugin)\"]\n            direction TB\n            Layout[\"layout\"]\n            Render[\"render\"]\n        end\n\n        Model[\"model\"]\n\n        Material[\"material\"]\n    end\n\n    style Engine fill:#fff,stroke:#000,stroke-width:2px,color:#000\n    style PluginManager fill:#fff,stroke:#000,stroke-width:2px,color:#000\n    style Workbench fill:#fff,stroke:#000,stroke-width:2px,color:#000\n    style Designer fill:#fff,stroke:#000,stroke-width:2px,color:#000\n    style Model fill:#fff,stroke:#000,stroke-width:2px,color:#000\n    style Material fill:#fff,stroke:#000,stroke-width:2px,color:#000\n    style OutlineTree fill:#fff,stroke:#000,stroke-width:1px,color:#000\n    style PropertyPanel fill:#fff,stroke:#000,stroke-width:1px,color:#000\n    style StylePanel fill:#fff,stroke:#000,stroke-width:1px,color:#000\n    style OtherPlugins fill:#fff,stroke:#000,stroke-width:1px,color:#000\n    style Layout fill:#fff,stroke:#000,stroke-width:1px,color:#000\n    style Render fill:#fff,stroke:#000,stroke-width:1px,color:#000\n```\n\n### 模块说明\n\n#### Engine（核心容器）\n\n- **职责**: 整个编辑器的核心容器，管理所有子模块\n- **功能**:\n  - 初始化和管理插件系统\n  - 管理页面模型和物料数据\n  - 提供统一的事件通信机制\n  - 协调各模块之间的交互\n\n#### PluginManager（插件管理器）\n\n- **职责**: 管理所有插件的生命周期\n- **功能**:\n  - 插件的注册、初始化、销毁\n  - 插件之间的通信协调\n  - 插件配置管理\n  - 插件依赖关系处理\n- **依赖**: Engine、Model、Layout、Material\n\n#### Model（页面模型）\n\n- **职责**: 管理页面数据结构\n- **功能**:\n  - 页面节点的增删改查\n  - 节点状态管理\n  - 数据变更通知\n  - 页面数据序列化/反序列化\n- **被依赖**: PluginManager、Render、各插件\n\n#### Material（物料系统）\n\n- **职责**: 管理组件物料信息\n- **功能**:\n  - 组件元数据管理（组件配置、属性定义等）\n  - 资源包管理（组件运行时资源）\n  - 组件与资源的关联\n- **被依赖**: Engine、PluginManager、Render\n\n#### Layout（工作台布局）\n\n- **职责**: 提供编辑器 UI 布局框架\n- **功能**:\n  - 左侧面板管理（组件库等）\n  - 中间画布区域\n  - 右侧面板管理（属性面板等）\n  - 顶部工具栏\n  - 布局可定制化\n- **被依赖**: PluginManager、各插件\n\n#### Render（渲染器）\n\n- **职责**: 将页面模型渲染为可视化界面\n- **功能**:\n  - 设计时渲染（可编辑模式）\n  - 预览渲染（只读模式）\n  - 组件实例化\n  - 事件处理\n- **被依赖**: Designer 插件、Canvas\n- **依赖**: Model、Material\n\n## 核心概念\n\n### Engine 组件\n\n`Engine` 是一个 React 类组件，是整个编辑器的核心容器。它管理着页面模型、插件系统、物料库等核心功能。\n\n### 插件系统\n\nEngine 采用插件化架构，所有功能都通过插件实现。内置了多个常用插件：\n\n- `Designer`: 设计器画布\n- `ComponentLibrary`: 组件库面板\n- `OutlineTree`: 页面结构树\n- `PropertyPanel`: 属性面板\n- `History`: 历史记录管理\n- 等等...\n\n### 页面模型 (PageModel)\n\nEngine 内部使用 `CPage` 来管理页面数据，提供了完整的页面节点操作能力。\n\n### 物料系统\n\n物料系统包括组件描述（Material）和组件库（AssetPackage），通过 `componentName` 进行关联。\n\n## 与阿里 LowCode Engine 对比\n\nChameleon Engine 和阿里 LowCode Engine 都是优秀的低代码解决方案，但在设计理念和使用场景上有所不同：\n\n### 架构设计\n\n| 特性         | Chameleon Engine         | 阿里 LowCode Engine          |\n| :----------- | :----------------------- | :--------------------------- |\n| **架构模式** | 插件化架构，高度可扩展   | 一体化架构，功能完整         |\n| **包体积**   | 轻量级，按需加载         | 体积较大，功能齐全           |\n| **定制性**   | 高度可定制，可完全自定义 | 定制性有限，主要面向企业场景 |\n| **学习曲线** | 简单直观，易于上手       | 功能丰富，学习成本较高       |\n\n### 核心优势\n\n#### Chameleon Engine 的优势\n\n✅ **轻量灵活**\n\n- 核心包体积小，按需加载插件\n- 可以只使用需要的功能模块\n- 适合中小型项目和快速原型开发\n\n✅ **高度可定制**\n\n- 插件化架构，所有功能都可通过插件扩展\n- 工作台布局完全可定制\n- 可以完全按照业务需求定制编辑器\n\n✅ **简单易用**\n\n- API 设计简洁直观\n- 文档清晰，上手快速\n- 适合快速集成到现有项目\n\n✅ **开源免费**\n\n- Apache License 2.0 协议，完全开源\n- 社区活跃，持续更新\n- 无商业限制\n\n✅ **技术栈友好**\n\n- 基于 React，与现有 React 生态无缝集成\n- 支持 TypeScript\n- 使用现代前端技术栈\n\n#### 阿里 LowCode Engine 的优势\n\n✅ **企业级功能**\n\n- 功能完整，开箱即用\n- 与阿里云生态深度集成\n- 适合大型企业级应用\n\n✅ **商业支持**\n\n- 提供商业技术支持\n- 有专业的团队维护\n- 适合对稳定性要求极高的项目\n\n✅ **功能丰富**\n\n- 内置大量企业级组件和模板\n- 提供完整的解决方案\n- 适合快速构建企业应用\n\n### 适用场景\n\n#### Chameleon Engine 适合：\n\n- 🎯 需要高度定制的编辑器场景\n- 🎯 中小型项目和快速原型开发\n- 🎯 希望完全控制编辑器行为的团队\n- 🎯 需要轻量级解决方案的项目\n- 🎯 开源项目或需要开源协议的项目\n\n#### 阿里 LowCode Engine 适合：\n\n- 🎯 大型企业级应用开发\n- 🎯 需要快速上线，对定制要求不高的场景\n- 🎯 需要商业技术支持的团队\n- 🎯 与阿里云生态深度集成的项目\n\n### 总结\n\nChameleon Engine 更适合追求**灵活性、可定制性和轻量级**的团队，而阿里 LowCode Engine 更适合需要**完整解决方案和商业支持**的企业级项目。选择哪个引擎主要取决于你的项目需求、团队规模和技术栈偏好。\n\n如果你需要：\n\n- **完全控制编辑器的行为** → 选择 Chameleon Engine\n- **快速集成，开箱即用** → 可以考虑阿里 LowCode Engine\n- **轻量级解决方案** → 选择 Chameleon Engine\n- **企业级支持和保障** → 可以考虑阿里 LowCode Engine\n\n## 下一步\n\n- 查看 [API 文档](./api/) 了解详细的 API 使用\n- 查看 [使用示例](./usage/) 学习更多用法\n- 查看 [插件开发](../plugin/plugin-develop/) 了解如何开发自定义插件\n"
  },
  {
    "path": "packages/docs-app/src/content/docs/reference/Engine/usage.mdx",
    "content": "---\ntitle: Engine 使用示例\nsidebar:\n  order: 3\n---\n\n# Engine 使用示例\n\n## 基础使用\n\n最简单的使用方式，使用默认插件列表：\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  return (\n    <Engine\n      plugins={DEFAULT_PLUGIN_LIST}\n      schema={pageSchema}\n      material={materials}\n      assetPackagesList={assetPackagesList}\n    />\n  );\n}\n```\n\n## 自定义插件列表\n\n只使用部分插件：\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\n\nconst { DesignerPlugin, ComponentLibPlugin, RightPanelPlugin } = plugins;\n\nfunction App() {\n  return (\n    <Engine plugins={[DesignerPlugin, ComponentLibPlugin, RightPanelPlugin]} schema={pageSchema} material={materials} />\n  );\n}\n```\n\n## 生命周期回调\n\n使用 `onReady` 和 `onMount` 回调：\n\n```tsx\nimport { Engine, plugins, EnginContext } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  const onMount = (ctx: EnginContext) => {\n    console.log('Engine 已挂载');\n  };\n\n  const onReady = async (ctx: EnginContext) => {\n    console.log('所有插件已加载完成');\n\n    // 可以在这里访问 pluginManager 和 engine\n    const pluginManager = ctx.pluginManager;\n    const engine = ctx.engine;\n\n    // 获取设计器插件\n    const designerPlugin = await pluginManager.get('Designer');\n\n    // 获取历史记录插件\n    const historyPlugin = await pluginManager.get('History');\n  };\n\n  return (\n    <Engine\n      plugins={DEFAULT_PLUGIN_LIST}\n      schema={pageSchema}\n      material={materials}\n      onMount={onMount}\n      onReady={onReady}\n    />\n  );\n}\n```\n\n## 自定义工作台\n\n通过 `workbenchConfig` 配置工作台：\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  return (\n    <Engine\n      plugins={DEFAULT_PLUGIN_LIST}\n      schema={pageSchema}\n      material={materials}\n      workbenchConfig={{\n        // 初始隐藏左侧面板\n        hiddenLeftPanel: true,\n        // 初始隐藏右侧面板\n        hiddenRightPanel: true,\n      }}\n    />\n  );\n}\n```\n\n## 动态控制工作台\n\n通过 `onReady` 回调动态控制工作台：\n\n```tsx\nimport { Engine, plugins, EnginContext } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\nimport { Button } from 'antd';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  const onReady = (ctx: EnginContext) => {\n    const workbench = ctx.engine.getWorkbench();\n\n    // 自定义顶部栏\n    workbench?.replaceTopBarView(\n      <div style={{ padding: '0 20px', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>\n        <div>我的编辑器</div>\n        <div>\n          <Button onClick={() => ctx.engine.preview()}>预览</Button>\n          <Button onClick={() => ctx.engine.existPreview()}>退出预览</Button>\n        </div>\n      </div>\n    );\n  };\n\n  return <Engine plugins={DEFAULT_PLUGIN_LIST} schema={pageSchema} material={materials} onReady={onReady} />;\n}\n```\n\n## 保存和加载页面数据\n\n```tsx\nimport { Engine, plugins, EnginContext } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\nimport { Button, message } from 'antd';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  const [pageSchema, setPageSchema] = useState(() => {\n    // 从本地存储加载\n    const saved = localStorage.getItem('pageSchema');\n    return saved ? JSON.parse(saved) : defaultPageSchema;\n  });\n\n  const onReady = (ctx: EnginContext) => {\n    const workbench = ctx.engine.getWorkbench();\n\n    workbench?.replaceTopBarView(\n      <div style={{ padding: '0 20px', display: 'flex', justifyContent: 'flex-end' }}>\n        <Button\n          type=\"primary\"\n          onClick={() => {\n            // 导出页面数据\n            const pageData = ctx.engine.pageModel.export();\n            localStorage.setItem('pageSchema', JSON.stringify(pageData));\n            message.success('保存成功');\n          }}\n        >\n          保存\n        </Button>\n      </div>\n    );\n  };\n\n  return <Engine plugins={DEFAULT_PLUGIN_LIST} schema={pageSchema} material={materials} onReady={onReady} />;\n}\n```\n\n## 动态更新页面\n\n```tsx\nimport { Engine, plugins, EnginContext } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\nimport { Button } from 'antd';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  const onReady = (ctx: EnginContext) => {\n    const workbench = ctx.engine.getWorkbench();\n\n    workbench?.replaceTopBarView(\n      <Button\n        onClick={() => {\n          // 从服务器获取新页面数据\n          fetch('/api/page')\n            .then((res) => res.json())\n            .then((newPageSchema) => {\n              // 更新页面\n              ctx.engine.updatePage(newPageSchema);\n            });\n        }}\n      >\n        刷新页面\n      </Button>\n    );\n  };\n\n  return <Engine plugins={DEFAULT_PLUGIN_LIST} schema={pageSchema} material={materials} onReady={onReady} />;\n}\n```\n\n## 动态更新物料\n\n```tsx\nimport { Engine, plugins, EnginContext } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\nimport { Button } from 'antd';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  const onReady = async (ctx: EnginContext) => {\n    const workbench = ctx.engine.getWorkbench();\n\n    workbench?.replaceTopBarView(\n      <Button\n        onClick={async () => {\n          // 从服务器获取新物料\n          const [newMaterials, newAssetPackages] = await Promise.all([\n            fetch('/api/materials').then((res) => res.json()),\n            fetch('/api/asset-packages').then((res) => res.json()),\n          ]);\n\n          // 更新物料\n          await ctx.engine.updateMaterials(newMaterials, newAssetPackages, {\n            formatComponents: (components) => {\n              // 自定义组件格式化逻辑\n              return components;\n            },\n          });\n        }}\n      >\n        更新物料\n      </Button>\n    );\n  };\n\n  return (\n    <Engine\n      plugins={DEFAULT_PLUGIN_LIST}\n      schema={pageSchema}\n      material={materials}\n      assetPackagesList={assetPackagesList}\n      onReady={onReady}\n    />\n  );\n}\n```\n\n## 监听节点选中\n\n```tsx\nimport { Engine, plugins, EnginContext } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  const onReady = (ctx: EnginContext) => {\n    // 监听节点选中变化\n    ctx.engine.emitter.on('onSelectNodeChange', ({ node }) => {\n      if (node) {\n        console.log('选中节点:', node);\n        console.log('节点ID:', node.id);\n        console.log('组件名:', node.componentName);\n      } else {\n        console.log('取消选中');\n      }\n    });\n  };\n\n  return <Engine plugins={DEFAULT_PLUGIN_LIST} schema={pageSchema} material={materials} onReady={onReady} />;\n}\n```\n\n## 自定义渲染器 JS 地址\n\n如果渲染器 JS 不在默认位置，可以自定义：\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  return (\n    <Engine\n      plugins={DEFAULT_PLUGIN_LIST}\n      schema={pageSchema}\n      material={materials}\n      // 使用 CDN 地址\n      renderJSUrl=\"https://cdn.example.com/render.umd.js\"\n      // 或使用相对路径\n      // renderJSUrl=\"/assets/render.umd.js\"\n    />\n  );\n}\n```\n\n## 配置 Monaco Editor CDN\n\n如果使用 CDN 加载 Monaco Editor：\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  return (\n    <Engine\n      plugins={DEFAULT_PLUGIN_LIST}\n      schema={pageSchema}\n      material={materials}\n      monacoEditor={{\n        cndUrl: 'https://cdn.jsdelivr.net/npm/monaco-editor@latest/min/vs',\n      }}\n    />\n  );\n}\n```\n\n## 自定义样式\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\nimport './custom.css';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  return (\n    <Engine\n      plugins={DEFAULT_PLUGIN_LIST}\n      schema={pageSchema}\n      material={materials}\n      className=\"my-custom-engine\"\n      style={{\n        width: '100%',\n        height: '100vh',\n      }}\n    />\n  );\n}\n```\n\n## 使用 beforeInitRender\n\n`beforeInitRender` 用于在渲染器初始化之前配置 iframe 环境。\n\n### 场景 1: 注入全局变量和配置\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  const beforePluginRun = ({ pluginManager }) => {\n    pluginManager.customPlugin('Designer', (pluginInstance) => {\n      pluginInstance.ctx.config.beforeInitRender = async ({ iframe }) => {\n        const subWin = iframe.getWindow();\n        if (!subWin) return;\n\n        // 必须：注入 React\n        (subWin as any).React = window.React;\n        (subWin as any).ReactDOM = window.ReactDOM;\n        (subWin as any).ReactDOMClient = window.ReactDOMClient;\n\n        // 注入应用配置\n        (subWin as any).APP_CONFIG = {\n          apiUrl: process.env.REACT_APP_API_URL,\n          theme: 'light',\n          language: 'zh-CN',\n        };\n\n        // 注入工具函数\n        (subWin as any).utils = {\n          formatDate: (date) => new Date(date).toLocaleDateString(),\n          formatCurrency: (amount) => `¥${amount.toFixed(2)}`,\n        };\n      };\n\n      return pluginInstance;\n    });\n  };\n\n  return (\n    <Engine plugins={DEFAULT_PLUGIN_LIST} schema={pageSchema} material={materials} beforePluginRun={beforePluginRun} />\n  );\n}\n```\n\n### 场景 2: 注入第三方库\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\nimport axios from 'axios';\nimport dayjs from 'dayjs';\nimport _ from 'lodash';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  const beforePluginRun = ({ pluginManager }) => {\n    pluginManager.customPlugin('Designer', (pluginInstance) => {\n      pluginInstance.ctx.config.beforeInitRender = async ({ iframe }) => {\n        const subWin = iframe.getWindow();\n        if (!subWin) return;\n\n        // 注入 React（必需）\n        (subWin as any).React = window.React;\n        (subWin as any).ReactDOM = window.ReactDOM;\n        (subWin as any).ReactDOMClient = window.ReactDOMClient;\n\n        // 注入第三方库\n        (subWin as any).axios = axios;\n        (subWin as any).dayjs = dayjs;\n        (subWin as any)._ = _;\n\n        console.log('第三方库注入成功');\n      };\n\n      return pluginInstance;\n    });\n  };\n\n  return (\n    <Engine plugins={DEFAULT_PLUGIN_LIST} schema={pageSchema} material={materials} beforePluginRun={beforePluginRun} />\n  );\n}\n```\n\n### 场景 3: 设置跨窗口通信\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\nimport { message } from 'antd';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  const beforePluginRun = ({ pluginManager }) => {\n    pluginManager.customPlugin('Designer', (pluginInstance) => {\n      pluginInstance.ctx.config.beforeInitRender = async ({ iframe }) => {\n        const subWin = iframe.getWindow();\n        if (!subWin) return;\n\n        // 注入 React\n        (subWin as any).React = window.React;\n        (subWin as any).ReactDOM = window.ReactDOM;\n        (subWin as any).ReactDOMClient = window.ReactDOMClient;\n\n        // 监听来自 iframe 的消息\n        subWin.addEventListener('message', (event) => {\n          if (event.data.source === 'component') {\n            console.log('收到组件消息:', event.data);\n\n            // 处理不同类型的消息\n            switch (event.data.type) {\n              case 'notification':\n                message.info(event.data.message);\n                break;\n              case 'error':\n                message.error(event.data.message);\n                break;\n            }\n          }\n        });\n\n        // 注入发送消息到父窗口的方法\n        (subWin as any).sendToEditor = (data) => {\n          window.postMessage(\n            {\n              source: 'iframe',\n              timestamp: Date.now(),\n              ...data,\n            },\n            '*'\n          );\n        };\n      };\n\n      return pluginInstance;\n    });\n  };\n\n  return (\n    <Engine plugins={DEFAULT_PLUGIN_LIST} schema={pageSchema} material={materials} beforePluginRun={beforePluginRun} />\n  );\n}\n```\n\n### 场景 4: 初始化主题和样式\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  const beforePluginRun = ({ pluginManager }) => {\n    pluginManager.customPlugin('Designer', (pluginInstance) => {\n      pluginInstance.ctx.config.beforeInitRender = async ({ iframe }) => {\n        const subWin = iframe.getWindow();\n        const subDoc = iframe.getDocument();\n        if (!subWin || !subDoc) return;\n\n        // 注入 React\n        (subWin as any).React = window.React;\n        (subWin as any).ReactDOM = window.ReactDOM;\n        (subWin as any).ReactDOMClient = window.ReactDOMClient;\n\n        // 注入主题配置\n        (subWin as any).THEME = {\n          primaryColor: '#1890ff',\n          successColor: '#52c41a',\n          warningColor: '#faad14',\n          errorColor: '#ff4d4f',\n          fontSize: '14px',\n          borderRadius: '4px',\n        };\n\n        // 添加全局样式\n        const style = subDoc.createElement('style');\n        style.textContent = `\n          :root {\n            --primary-color: #1890ff;\n            --text-color: #333;\n            --border-color: #d9d9d9;\n          }\n\n          * {\n            box-sizing: border-box;\n          }\n\n          body {\n            margin: 0;\n            padding: 0;\n            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,\n              'Helvetica Neue', Arial, sans-serif;\n            color: var(--text-color);\n          }\n        `;\n        subDoc.head.appendChild(style);\n      };\n\n      return pluginInstance;\n    });\n  };\n\n  return (\n    <Engine plugins={DEFAULT_PLUGIN_LIST} schema={pageSchema} material={materials} beforePluginRun={beforePluginRun} />\n  );\n}\n```\n\n### 场景 5: 注入模拟数据和 API\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  const beforePluginRun = ({ pluginManager }) => {\n    pluginManager.customPlugin('Designer', (pluginInstance) => {\n      pluginInstance.ctx.config.beforeInitRender = async ({ iframe }) => {\n        const subWin = iframe.getWindow();\n        if (!subWin) return;\n\n        // 注入 React\n        (subWin as any).React = window.React;\n        (subWin as any).ReactDOM = window.ReactDOM;\n        (subWin as any).ReactDOMClient = window.ReactDOMClient;\n\n        // 注入模拟 API\n        (subWin as any).mockAPI = {\n          // 获取用户信息\n          getUserInfo: async () => {\n            await new Promise((resolve) => setTimeout(resolve, 500));\n            return {\n              id: 1,\n              name: '张三',\n              email: 'zhangsan@example.com',\n              avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=Felix',\n            };\n          },\n\n          // 获取产品列表\n          getProducts: async () => {\n            await new Promise((resolve) => setTimeout(resolve, 300));\n            return [\n              { id: 1, name: '产品 A', price: 299, stock: 100 },\n              { id: 2, name: '产品 B', price: 399, stock: 50 },\n              { id: 3, name: '产品 C', price: 199, stock: 200 },\n            ];\n          },\n\n          // 提交订单\n          submitOrder: async (orderData) => {\n            await new Promise((resolve) => setTimeout(resolve, 800));\n            console.log('提交订单:', orderData);\n            return {\n              success: true,\n              orderId: `ORDER-${Date.now()}`,\n            };\n          },\n        };\n\n        // 注入模拟数据\n        (subWin as any).mockData = {\n          users: [\n            { id: 1, name: '张三', role: 'admin' },\n            { id: 2, name: '李四', role: 'user' },\n          ],\n          categories: ['电子产品', '图书', '服装', '食品'],\n        };\n      };\n\n      return pluginInstance;\n    });\n  };\n\n  return (\n    <Engine plugins={DEFAULT_PLUGIN_LIST} schema={pageSchema} material={materials} beforePluginRun={beforePluginRun} />\n  );\n}\n```\n\n### 场景 6: 开发环境调试工具\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  const beforePluginRun = ({ pluginManager }) => {\n    pluginManager.customPlugin('Designer', (pluginInstance) => {\n      pluginInstance.ctx.config.beforeInitRender = async ({ iframe }) => {\n        const subWin = iframe.getWindow();\n        if (!subWin) return;\n\n        // 注入 React\n        (subWin as any).React = window.React;\n        (subWin as any).ReactDOM = window.ReactDOM;\n        (subWin as any).ReactDOMClient = window.ReactDOMClient;\n\n        // 只在开发环境注入调试工具\n        if (process.env.NODE_ENV === 'development') {\n          // 调试工具对象\n          (subWin as any).__DEBUG__ = {\n            // 日志工具\n            log: (...args) => {\n              console.log('[iframe]', ...args);\n            },\n            error: (...args) => {\n              console.error('[iframe]', ...args);\n            },\n            warn: (...args) => {\n              console.warn('[iframe]', ...args);\n            },\n\n            // 组件调试\n            inspectComponent: (componentName) => {\n              console.log(`检查组件: ${componentName}`);\n              // 可以添加更多检查逻辑\n            },\n\n            // 性能监控\n            performance: {\n              start: (label) => {\n                console.time(`[iframe] ${label}`);\n              },\n              end: (label) => {\n                console.timeEnd(`[iframe] ${label}`);\n              },\n            },\n          };\n\n          // 启用详细日志\n          (subWin as any).__VERBOSE_LOGGING__ = true;\n\n          // 监听错误\n          subWin.addEventListener('error', (event) => {\n            console.error('[iframe error]', {\n              message: event.error?.message,\n              stack: event.error?.stack,\n              filename: event.filename,\n              lineno: event.lineno,\n            });\n          });\n\n          // 监听未捕获的 Promise 错误\n          subWin.addEventListener('unhandledrejection', (event) => {\n            console.error('[iframe unhandled promise]', event.reason);\n          });\n        }\n      };\n\n      return pluginInstance;\n    });\n  };\n\n  return (\n    <Engine plugins={DEFAULT_PLUGIN_LIST} schema={pageSchema} material={materials} beforePluginRun={beforePluginRun} />\n  );\n}\n```\n\n### 综合示例：完整的 beforeInitRender 配置\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\nimport axios from 'axios';\nimport dayjs from 'dayjs';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  const beforePluginRun = ({ pluginManager }) => {\n    pluginManager.customPlugin('Designer', (pluginInstance) => {\n      pluginInstance.ctx.config.beforeInitRender = async ({ iframe }) => {\n        const subWin = iframe.getWindow();\n        const subDoc = iframe.getDocument();\n\n        if (!subWin || !subDoc) {\n          console.error('无法获取 iframe window 或 document');\n          return;\n        }\n\n        console.log('开始初始化 iframe 环境...');\n\n        try {\n          // 1. 注入 React（必需）\n          (subWin as any).React = window.React;\n          (subWin as any).ReactDOM = window.ReactDOM;\n          (subWin as any).ReactDOMClient = window.ReactDOMClient;\n          console.log('✓ React 注入成功');\n\n          // 2. 注入应用配置\n          (subWin as any).APP_CONFIG = {\n            apiUrl: process.env.REACT_APP_API_URL || 'https://api.example.com',\n            environment: process.env.NODE_ENV,\n            version: '1.0.0',\n            features: {\n              enableAnalytics: true,\n              enableDebug: process.env.NODE_ENV === 'development',\n            },\n          };\n          console.log('✓ 应用配置注入成功');\n\n          // 3. 注入第三方库\n          (subWin as any).axios = axios;\n          (subWin as any).dayjs = dayjs;\n          console.log('✓ 第三方库注入成功');\n\n          // 4. 设置跨窗口通信\n          (subWin as any).sendToEditor = (data) => {\n            window.postMessage(\n              {\n                source: 'iframe',\n                timestamp: Date.now(),\n                ...data,\n              },\n              '*'\n            );\n          };\n\n          subWin.addEventListener('message', (event) => {\n            if (event.data.source === 'component') {\n              console.log('收到组件消息:', event.data);\n            }\n          });\n          console.log('✓ 跨窗口通信设置成功');\n\n          // 5. 初始化样式和主题\n          (subWin as any).THEME = {\n            primaryColor: '#1890ff',\n            fontSize: '14px',\n          };\n\n          const style = subDoc.createElement('style');\n          style.textContent = `\n            :root {\n              --primary-color: #1890ff;\n              --text-color: #333;\n            }\n            * { box-sizing: border-box; }\n            body {\n              margin: 0;\n              padding: 0;\n              font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto;\n            }\n          `;\n          subDoc.head.appendChild(style);\n          console.log('✓ 主题和样式初始化成功');\n\n          // 6. 开发环境配置\n          if (process.env.NODE_ENV === 'development') {\n            (subWin as any).__DEBUG__ = {\n              log: (...args) => console.log('[iframe]', ...args),\n              error: (...args) => console.error('[iframe]', ...args),\n            };\n\n            subWin.addEventListener('error', (event) => {\n              console.error('[iframe error]', event.error);\n            });\n\n            console.log('✓ 调试工具初始化成功');\n          }\n\n          console.log('iframe 环境初始化完成！');\n        } catch (error) {\n          console.error('iframe 环境初始化失败:', error);\n        }\n      };\n\n      return pluginInstance;\n    });\n  };\n\n  return (\n    <Engine plugins={DEFAULT_PLUGIN_LIST} schema={pageSchema} material={materials} beforePluginRun={beforePluginRun} />\n  );\n}\n```\n\n:::tip\n**注意事项：**\n\n1. 必须注入 React、ReactDOM 和 ReactDOMClient，否则渲染器无法正常工作\n2. `beforeInitRender` 会在每次重新加载 iframe 时执行\n3. 建议添加错误处理，确保初始化过程的健壮性\n4. 可以通过 `console.log` 验证注入是否成功\n   :::\n"
  },
  {
    "path": "packages/docs-app/src/content/docs/reference/Material/_category_.json",
    "content": "{\n  \"position\": 2,\n  \"label\": \"组件物料\",\n  \"collapsible\": true,\n  \"collapsed\": false,\n  \"customProps\": {\n    \"description\": \"This description can be used in the swizzled DocCard\"\n  }\n}\n"
  },
  {
    "path": "packages/docs-app/src/content/docs/reference/Material/advanceDevelopMaterial.mdx",
    "content": "---\ntitle: 高级物料开发\nsidebar:\n  order: 3\n---\n\nimport { Code } from '@astrojs/starlight/components';\nimport GridLayoutMeta from '../../../../codeSnippets/GridLayoutMeta.tsx?raw';\nimport GridLayoutComponent from '../../../../codeSnippets/GridLayoutComponent.tsx?raw';\nimport GridLayoutWrap from '../../../../codeSnippets/GridLayoutWrap.tsx?raw';\nimport GridItemMeta from '../../../../codeSnippets/GridItemMeta.tsx?raw';\n\n# 高级物料开发\n\n高级物料开发允许你通过 `advanceCustom` 配置项来深度定制组件在设计器中的行为，包括拖拽、选择、工具栏、右侧面板等各个方面。本文档将以 `ReactGridLayout` 组件为例，详细介绍如何使用这些高级特性。\n\n## 什么是高级物料？\n\n高级物料是指那些需要特殊编辑行为的组件，例如：\n\n- **布局容器组件**：需要自定义拖拽和放置逻辑\n- **复杂交互组件**：需要定制工具栏或选择框样式\n- **特殊渲染组件**：需要在编辑模式下包装或修改组件行为\n- **响应式组件**：需要根据断点动态调整布局\n\n通过 `advanceCustom` 配置，你可以完全控制组件在设计器中的编辑体验。\n\n## advanceCustom 配置项概览\n\n`advanceCustom` 提供了丰富的配置选项，主要包括：\n\n### 生命周期钩子\n\n- `onDragStart` / `onDragging` / `onDragEnd`：拖拽生命周期\n- `onSelect`：选中时触发\n- `onCopy`：复制时触发\n- `onDelete`：删除时触发\n- `onNewAdd`：首次添加到画布时触发\n- `onDrop`：放置到目标位置时触发\n\n### 权限控制\n\n- `canDragNode`：控制节点是否可拖拽\n- `canDropNode`：控制节点是否可被放置\n- `canAcceptNode`：控制节点是否可接受子节点\n- `disableEditorDragDom`：禁用编辑器默认的拖拽行为\n\n### 视图定制\n\n- `wrapComponent`：包装组件，定制编辑模式下的行为\n- `toolbarViewRender`：自定义工具栏视图\n- `selectRectViewRender`：自定义选中框视图\n- `hoverRectViewRender`：自定义悬停框视图\n- `dropViewRender`：自定义放置预览视图\n- `ghostViewRender`：自定义拖拽占位视图\n\n### 面板配置\n\n- `rightPanel`：配置右侧属性面板的显示和定制\n- `autoGetDom`：控制是否自动获取 DOM 元素\n\n## ReactGridLayout 示例\n\n`ReactGridLayout` 是一个完整的高级物料示例，它实现了基于 GridStack 的响应式布局系统。让我们逐步分析它的实现。\n\n### 组件结构\n\nReactGridLayout 包含两个主要组件：\n\n1. **GridLayout**：布局容器，管理整个网格系统\n2. **GridItem**：布局项，每个可拖拽的子元素\n\n### 1. GridLayout 物料定义\n\n<Code code={GridLayoutMeta} lang=\"tsx\" title=\"meta.tsx\" showLineNumbers></Code>\n\n#### 关键配置解析\n\n**`isContainer: true`**\n\n- 标记为容器组件，允许放置子元素\n\n**`advanceCustom.wrapComponent`**\n\n- 包装原始组件，注入设计器相关的逻辑\n- 获取 iframe 窗口引用（设计器运行在 iframe 中）\n- 监听 GridStack 的拖拽事件，与设计器选择状态同步\n\n**`advanceCustom.rightPanel.visual: false`**\n\n- 隐藏可视化面板，因为布局组件不需要可视化编辑\n\n**`advanceCustom.autoGetDom: false`**\n\n- 禁用自动获取 DOM，因为我们需要手动控制 DOM 引用\n\n### 2. GridLayout 组件实现\n\n<Code code={GridLayoutComponent} lang=\"tsx\" title=\"index.tsx\" showLineNumbers></Code>\n\n#### 核心功能\n\n1. **GridStack 初始化**：使用 GridStack 库创建网格布局\n2. **响应式断点**：监听窗口大小变化，切换不同的布局断点\n3. **Context 提供**：通过 React Context 向子组件提供 GridStack 实例和当前断点信息\n\n### 3. LayoutWrap 包装组件\n\n<Code code={GridLayoutWrap} lang=\"tsx\" title=\"edit/layoutWrap.tsx\" showLineNumbers></Code>\n\n#### 设计器集成\n\n`LayoutWrap` 是连接组件和设计器的桥梁：\n\n- **监听布局变化**：当用户拖拽 GridItem 时，GridStack 会触发 `change` 事件\n- **同步到页面模型**：将布局变化同步到 Chameleon 的页面模型中\n- **响应式支持**：根据当前窗口宽度确定断点，更新对应断点的布局信息\n\n### 4. GridItem 物料定义\n\n<Code code={GridItemMeta} lang=\"tsx\" title=\"item.meta.tsx\" showLineNumbers></Code>\n\n#### 高级特性解析\n\n**`disableEditorDragDom: true`**\n\n- 禁用编辑器默认的 DOM 拖拽，使用 GridStack 自己的拖拽系统\n\n**`advanceCustom.toolbarViewRender`**\n\n- 自定义工具栏，显示当前布局信息（宽度、高度、位置）\n- 实时监听窗口大小变化，更新显示的布局信息\n- 移除默认的显示/隐藏按钮（与 GridStack 拖拽冲突）\n\n**`advanceCustom.onDragStart`**\n\n- 返回 `false`，禁用编辑器默认的拖拽行为\n\n**`advanceCustom.wrapComponent`**\n\n- 包装组件，注册组件实例引用\n- 用于在工具栏中获取实时的布局信息\n\n**`advanceCustom.canDropNode`**\n\n- 只允许 GridItem 被放置在 GridLayout 中\n- 确保组件层级关系的正确性\n\n**`advanceCustom.onCopy`**\n\n- 复制时重置 x、y 坐标\n- 避免复制后的元素重叠\n\n**`advanceCustom.onNewAdd`**\n\n- 只允许从 GridLayout 拖入创建新的 GridItem\n\n**`advanceCustom.rightPanel.advanceOptions`**\n\n- 隐藏 loop 和 render 选项\n- 简化属性面板，只显示必要的配置\n\n## 常用场景示例\n\n### 场景 1：自定义拖拽行为\n\n```tsx\nadvanceCustom: {\n  onDragStart: async (node) => {\n    // 执行自定义逻辑\n    console.log('开始拖拽', node.id);\n    return true; // 返回 true 允许拖拽\n  },\n  onDragEnd: async (node) => {\n    // 拖拽结束后的清理工作\n    console.log('拖拽结束', node.id);\n  },\n}\n```\n\n### 场景 2：自定义工具栏\n\n```tsx\nadvanceCustom: {\n  toolbarViewRender: ({ node, toolBarItemList }) => {\n    return (\n      <div style={{ display: 'flex', alignItems: 'center' }}>\n        <span>自定义信息: {node.id}</span>\n        {toolBarItemList}\n      </div>\n    );\n  },\n}\n```\n\n### 场景 3：控制放置权限\n\n```tsx\nadvanceCustom: {\n  canDropNode: async (node, params) => {\n    const { dropNode } = params;\n    // 只允许放置在特定类型的容器中\n    if (dropNode?.value.componentName === 'MyContainer') {\n      return true;\n    }\n    return false;\n  },\n}\n```\n\n### 场景 4：包装组件注入逻辑\n\n```tsx\nadvanceCustom: {\n  wrapComponent: (Comp, options) => {\n    return (props) => {\n      // 注入设计器相关的逻辑\n      const designerCtx = options.ctx;\n\n      return (\n        <div className=\"design-mode-wrapper\">\n          <Comp {...props} />\n        </div>\n      );\n    };\n  },\n}\n```\n\n### 场景 5：自定义右侧面板\n\n```tsx\nadvanceCustom: {\n  rightPanel: {\n    visual: false, // 隐藏可视化面板\n    advance: true,\n    advanceOptions: {\n      loop: false, // 隐藏循环选项\n      render: true, // 显示渲染选项\n    },\n    customTabs: [\n      {\n        key: 'custom',\n        name: '自定义配置',\n        view: ({ node }) => {\n          return <div>自定义配置面板</div>;\n        },\n      },\n    ],\n  },\n}\n```\n\n## 最佳实践\n\n### 1. 合理使用 wrapComponent\n\n`wrapComponent` 是一个强大的功能，但要注意：\n\n- ✅ **适合场景**：需要访问设计器上下文、需要监听第三方库事件、需要注入特殊逻辑\n- ❌ **避免场景**：简单的样式修改（应该用 CSS）、不需要设计器交互的组件\n\n### 2. 性能优化\n\n- 使用 `debounce` 或 `throttle` 处理频繁的事件（如 resize）\n- 避免在 `toolbarViewRender` 中执行重计算\n- 合理使用 `useMemo` 和 `useCallback` 优化渲染\n\n### 3. 错误处理\n\n- 所有异步钩子都应该有错误处理\n- 返回 `false` 来阻止操作时，确保用户能理解原因\n\n### 4. 类型安全\n\n- 使用 TypeScript 确保类型安全\n- 为自定义的 props 和 context 定义类型\n\n## 总结\n\n高级物料开发通过 `advanceCustom` 配置提供了强大的定制能力，让你可以：\n\n- 🎯 **完全控制**组件的编辑行为\n- 🎨 **自定义视图**和交互体验\n- 🔌 **深度集成**第三方库（如 GridStack）\n- 📱 **实现响应式**布局和断点管理\n\n`ReactGridLayout` 是一个很好的参考示例，展示了如何将这些高级特性组合使用，创建一个功能完整的布局组件。\n\n## 相关资源\n\n- [物料开发基础文档](./developMaterial.mdx)\n- [物料协议文档](../PageSchema/material.mdx)\n- [插件开发指南](../Plugin/plugin-develop.mdx)\n- [GridStack 官方文档](https://gridstackjs.com/)\n"
  },
  {
    "path": "packages/docs-app/src/content/docs/reference/Material/developMaterial.mdx",
    "content": "---\ntitle: 物料开发\nsidebar:\n  order: 2\n---\n\n物料开发使用该[【模版工程】](https://github.com/ByteCrazy/chameleon-material-template.git)即可\n\n```bash\ngit clone https://github.com/ByteCrazy/chameleon-material-template.git\n\ncd chameleon-material-template\n\npnpm i\n\n// 开发\npnpm run start\n\n// 构建\n\npnpm run build\n```\n\n访问 [http://localhost:3000](http://localhost:3000)\n开发完成之后发布 npm 包即可\n\n### 物料包命名规范\n\n统一采用 `chamn-material-xxxx` 的方式命名\n"
  },
  {
    "path": "packages/docs-app/src/content/docs/reference/Material/introduction.mdx",
    "content": "---\ntitle: 什么是组件物料？\nsidebar:\n  order: 1\n---\n\nimport { Code } from '@astrojs/starlight/components';\nimport MetarialImg from './assets/material.png';\nimport ButtonMeta from '../../../../codeSnippets/ButtonMeta.tsx?raw';\n\n<img\n  style={{\n    marginBottom: '20px',\n    border: '1px solid #ededed',\n    height: '324px',\n  }}\n  src={MetarialImg.src}\n  alt=\"Component Metarial\"\n/>\n\n组件物料包括两部分:\n\n- 组件库\n- 组件描述\n\n组件库和组件描述之间 通过 componentName 来一一绑定对应\n\n## 组件库\n\n组件库就是组件的 JS 运行库, 比如 Ant Design、ElemtnUI、Vant UI 等等，它们是可以被运行的 JS 代码库。\n\n## 组件描述\n\n如果想要编辑器知道组件的具体功能，比如支持那些属性，那些事件等等，则需要一份对应的描述文件详细描述了组件的 props 的各种行为，以及数据结构，这样编辑器才能对组件进行控制编辑。这份描述文件就是组件描述。\n\n物料协议请参考 Schema 文档\n\n### Example:\n\n对 Ant Design UI 库的 Button 组件的物料描述:\n\n<Code code={ButtonMeta} lang=\"tsx\" title=\"Editor.tsx\" showLineNumbers></Code>\n"
  },
  {
    "path": "packages/docs-app/src/content/docs/reference/PageSchema/built-in-setter.mdx",
    "content": "---\nsidebar:\n  order: 3\ntitle: Setter\n---\n\n## 什么是 setter ？\n\nSetter 是一个输入控件，用于在组件物料协议中描述组件 props 的输入方式，同时提供对应的输入控件让用户可以准确的输入组件的 props 值\n\n## 内置 Setter 列表\n\n### StringSetter\n\n[code link](https://github.com/hlerenow/chameleon/tree/master/packages/engine/src/component/CustomSchemaForm/components/Setters/StringSetter)\n\n### ArraySetter\n\n[code link](https://github.com/hlerenow/chameleon/tree/master/packages/engine/src/component/CustomSchemaForm/components/Setters/ArraySetter/index.tsx)\n\nExample:\n\n```tsx\nconst customAttributesMeta: CMaterialPropsType[number] = {\n  name: '$$attributes',\n  title: '属性',\n  valueType: 'object',\n  setters: [\n    {\n      componentName: 'ArraySetter',\n      props: {\n        item: {\n          setters: [\n            {\n              componentName: 'ShapeSetter',\n              props: {\n                elements: [\n                  {\n                    name: 'key',\n                    title: '属性名',\n                    valueType: 'string',\n                    setters: ['StringSetter'],\n                  },\n                  {\n                    name: 'value',\n                    title: '值',\n                    valueType: 'string',\n                    setters: ['StringSetter', 'NumberSetter', 'JSONSetter', 'FunctionSetter', 'ExpressionSetter'],\n                  },\n                ],\n                collapse: false,\n              },\n              initialValue: {},\n            },\n          ],\n          initialValue: {},\n        },\n      },\n      initialValue: [],\n    },\n  ],\n};\n```\n\n### ShapeSetter\n\n[code link](https://github.com/hlerenow/chameleon/tree/master/packages/engine/src/component/CustomSchemaForm/components/Setters/ShapeSetter/index.tsx)\n\nExample:\n\n```tsx\nconst CSSBindPropsSchema: CMaterialPropsType<'CSSValueSetter'> = [\n  {\n    name: 'css',\n    title: 'CSS Variable Bind',\n    valueType: 'array',\n    setters: [\n      {\n        componentName: 'ArraySetter',\n        props: {\n          item: {\n            setters: [\n              {\n                componentName: 'ShapeSetter',\n                props: {\n                  elements: [\n                    {\n                      name: 'key',\n                      title: '属性',\n                      valueType: 'string',\n                      setters: ['StringSetter'],\n                      description: '',\n                    },\n                    {\n                      name: 'value',\n                      title: '置',\n                      valueType: 'string',\n                      setters: ['ExpressionSetter'],\n                      description: '',\n                    },\n                  ],\n                  collapse: false,\n                },\n                initialValue: {\n                  key: '',\n                  value: {\n                    type: CNodePropsTypeEnum.EXPRESSION,\n                    value: '',\n                  },\n                },\n              },\n            ],\n            initialValue: {\n              key: '',\n              value: {\n                type: CNodePropsTypeEnum.EXPRESSION,\n                value: '',\n              },\n            },\n          },\n        },\n        initialValue: [],\n      },\n    ],\n  },\n];\n```\n\n### NumberSetter\n\n[code link](https://github.com/hlerenow/chameleon/tree/master/packages/engine/src/component/CustomSchemaForm/components/Setters/NumberSetter/index.tsx)\n\n### ExpressionSetter\n\n[code link](https://github.com/hlerenow/chameleon/tree/master/packages/engine/src/component/CustomSchemaForm/components/Setters/ExpressionSetter/index.tsx)\n\n### BooleanSetter\n\n[code link](https://github.com/hlerenow/chameleon/tree/master/packages/engine/src/component/CustomSchemaForm/components/Setters/BooleanSetter/index.tsx)\n\n### SelectSetter\n\n[code link](https://github.com/hlerenow/chameleon/tree/master/packages/engine/src/component/CustomSchemaForm/components/Setters/BoolSelectSettereanSetter/index.tsx)\n\n```tsx\n  {\n    title: 'HTML 标签',\n    componentName: 'CNativeTag',\n    props: [\n      {\n        name: 'htmlTag',\n        title: '标签名',\n        valueType: 'string',\n        setters: [\n          {\n            componentName: 'SelectSetter',\n            props: {\n              options: HTMl_TAGS.map((tag) => {\n                return {\n                  name: tag,\n                  value: tag,\n                };\n              }),\n            },\n          },\n        ],\n      },\n      customAttributesMeta,\n    ],\n```\n\n### JSONSetter\n\n[code link](https://github.com/hlerenow/chameleon/tree/master/packages/engine/src/component/CustomSchemaForm/components/Setters/JSONSetter/index.tsx)\n\n### FunctionSetter\n\n[code link](https://github.com/hlerenow/chameleon/tree/master/packages/engine/src/component/CustomSchemaForm/components/Setters/FunctionSetter/index.tsx)\n\n### TextAreaSetter\n\n[code link](https://github.com/hlerenow/chameleon/tree/master/packages/engine/src/component/CustomSchemaForm/components/Setters/TextAreaSetter/index.tsx)\n\n### CSSValueSetter\n\n[code link](https://github.com/hlerenow/chameleon/tree/master/packages/engine/src/component/CustomSchemaForm/components/Setters/CSSValueSetter/index.tsx)\n\n## 自定义 Setter\n\n参考 [自定义 Setter](/plugin/custom-setter)\n"
  },
  {
    "path": "packages/docs-app/src/content/docs/reference/PageSchema/material.mdx",
    "content": "---\nsidebar:\n  order: 2\ntitle: 物料协议\n---\n\n描述请参考 [【 Material 模型定义】](https://github.com/hlerenow/chameleon/blob/master/packages/model/src/types/material.ts#L338)\n\nExample: [【示例物料】](https://github.com/hlerenow/chameleon/tree/master/packages/demo-page/src/material)\n"
  },
  {
    "path": "packages/docs-app/src/content/docs/reference/PageSchema/page.mdx",
    "content": "---\nsidebar:\n  order: 1\ntitle: 页面协议\n---\n\n# PageSchema\n\n| Key            | Value Type        | Description                           | Example  |\n| :------------- | :---------------- | :------------------------------------ | :------- |\n| version        | string            | 协议版本号                            | 1.0.0    |\n| name           | string            | 页面名称                              | TestPage |\n| css            | CSSType           | 页面 css                              | —        |\n| componentsMeta | ComponentMetaType | 页面使用的组件物料                    | —        |\n| thirdLibs      | LibMetaType       | 页面依赖使用的第三方库描述            | —        |\n| componentsTree | CRootNodeDataType | 页面结构                              | —        |\n| assets         | AssetPackage      | 页面使用的所有资源信息，包含 url 信息 | —        |\n\n更多描述请参考 [【模型定定义】](https://github.com/hlerenow/chameleon/blob/master/packages/model/src/types/page.ts)\n\n## CRootNodeDataType\n\n| Key           | Value Type                  | Description                             | Example                    |\n| :------------ | :-------------------------- | :-------------------------------------- | :------------------------- |\n| id            | string                      | 节点唯一 id                             | xassas                     |\n| title         | string                      | 节点名称                                | root node                  |\n| componentName | string                      | 组件名称                                | Button                     |\n| type          | 'lowcode'、 'normal'        | 节点类型， lowcode 表示低码组件         | normal                     |\n| props         | CPropObjDataType            | 组件传入的属性值                        | \\{'text': '我是一个按钮'\\} |\n| state         | Record\\<string, any\\>       | 组件的 state                            | -                          |\n| nodeName      | string                      | 组件的 state 别名，便于其他组件索引使用 | -                          |\n| schema        | CPageDataType               | if type is lowcode, schema is required  | -                          |\n| classNames    | ClassNameType[]             | 存储节点的 className                    | -                          |\n| css           | CSSType[]                   | 存储节点的 css 样式                     | -                          |\n| refId         | string                      | 组件的唯一引用标示                      | -                          |\n| children      | (string \\| CNodeDataType)[] | 当前节点的 children 节点                | -                          |\n\n更多描述请参考 [【模型定定义】](https://github.com/hlerenow/chameleon/blob/master/packages/model/src/types/node.ts)\n\n## ComponentMetaType\n\n更多描述请参考 [【模型定定义】](https://github.com/hlerenow/chameleon/blob/master/packages/model/src/types/page.ts)\n\n## LibMetaType\n\n更多描述请参考 [【模型定定义】](https://github.com/hlerenow/chameleon/blob/master/packages/model/src/types/base.ts)\n\n## CSSType\n\n更多描述请参考 [【模型定定义】](https://github.com/hlerenow/chameleon/blob/master/packages/model/src/types/base.ts)\n\n## AssetPackage\n\n更多描述请参考 [【模型定定义】](https://github.com/hlerenow/chameleon/blob/master/packages/model/src/types/base.ts)\n"
  },
  {
    "path": "packages/docs-app/src/content/docs/reference/Plugin/built-in-plugins-usage.mdx",
    "content": "---\ntitle: 内置插件使用指南\nsidebar:\n  order: 4\n---\n\n# 内置插件使用指南\n\nChameleon Engine 提供了一套完整的内置插件，覆盖了低代码编辑器的核心功能。本文档详细介绍每个插件的功能、配置和使用方法。\n\n## DesignerPlugin - 设计器画布\n\n设计器画布是编辑器的核心插件，负责渲染和编辑页面内容。\n\n### 功能特性\n\n- 📐 可视化画布渲染\n- 🖱️ 拖拽添加组件\n- ✏️ 组件选中和编辑\n- 📱 响应式预览\n- 🎨 实时更新\n\n### 配置选项\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\nimport { DesignerPluginInstance } from '@chamn/engine';\n\nconst beforePluginRun = ({ pluginManager }) => {\n  pluginManager.customPlugin<DesignerPluginInstance>('Designer', (pluginInstance) => {\n    // 自定义渲染器\n    pluginInstance.ctx.config.customRender = async ({ iframe, assets, page, pageModel, ready, renderJSUrl }) => {\n      // 自定义渲染逻辑\n    };\n\n    // 初始化前钩子\n    pluginInstance.ctx.config.beforeInitRender = async ({ iframe }) => {\n      // 初始化 iframe 环境\n    };\n\n    // 自定义组件\n    pluginInstance.ctx.config.components = {\n      Button: MyButton,\n      Input: MyInput,\n    };\n\n    return pluginInstance;\n  });\n};\n```\n\n### 导出方法\n\n```tsx\nconst onReady = async (ctx: EnginContext) => {\n  const designerPlugin = await ctx.pluginManager.get('Designer');\n  const designer = designerPlugin?.export;\n\n  // 设置预览模式\n  designer?.setPreviewMode();\n\n  // 设置编辑模式\n  designer?.setEditMode();\n\n  // 设置画布宽度\n  designer?.setCanvasWidth(1200);\n  designer?.setCanvasWidth('100%');\n\n  // 获取 iframe DOM\n  const iframeDom = designer?.getIframeDom();\n\n  // 获取设计器窗口\n  const designerWindow = designer?.getDesignerWindow();\n\n  // 重新加载\n  designer?.reload();\n};\n```\n\n### 使用示例\n\n```tsx\nimport { Engine, plugins, EnginContext } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\nimport { Button } from 'antd';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  const onReady = async (ctx: EnginContext) => {\n    const workbench = ctx.engine.getWorkbench();\n    const designerPlugin = await ctx.pluginManager.get('Designer');\n\n    // 自定义工具栏\n    workbench?.replaceTopBarView(\n      <div style={{ padding: '0 20px', display: 'flex', gap: '10px' }}>\n        <Button onClick={() => designerPlugin?.export.setPreviewMode()}>预览</Button>\n        <Button onClick={() => designerPlugin?.export.setEditMode()}>编辑</Button>\n        <Button onClick={() => designerPlugin?.export.setCanvasWidth(375)}>移动端</Button>\n        <Button onClick={() => designerPlugin?.export.setCanvasWidth('100%')}>全屏</Button>\n      </div>\n    );\n  };\n\n  return <Engine plugins={DEFAULT_PLUGIN_LIST} schema={pageSchema} material={materials} onReady={onReady} />;\n}\n```\n\n## HistoryPlugin - 历史记录\n\n历史记录插件提供撤销/重做功能。\n\n### 功能特性\n\n- ⏮️ 撤销操作\n- ⏭️ 重做操作\n- 🔄 重置到初始状态\n- 📝 自动记录节点变化\n\n### 导出方法\n\n```tsx\nconst onReady = async (ctx: EnginContext) => {\n  const historyPlugin = await ctx.pluginManager.get('History');\n  const history = historyPlugin?.export;\n\n  // 撤销（上一步）\n  history?.preStep();\n\n  // 重做（下一步）\n  history?.nextStep();\n\n  // 重置到初始状态\n  history?.reset();\n\n  // 判断是否可以撤销\n  const canUndo = history?.canGoPreStep();\n\n  // 判断是否可以重做\n  const canRedo = history?.canGoNextStep();\n\n  // 手动添加历史记录\n  history?.addStep();\n};\n```\n\n### 使用示例\n\n```tsx\nimport { Engine, plugins, EnginContext } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\nimport { Button } from 'antd';\nimport { RollbackOutlined } from '@ant-design/icons';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  const onReady = async (ctx: EnginContext) => {\n    const workbench = ctx.engine.getWorkbench();\n    const historyPlugin = await ctx.pluginManager.get('History');\n    const history = historyPlugin?.export;\n\n    workbench?.replaceTopBarView(\n      <div style={{ padding: '0 20px', display: 'flex', gap: '10px' }}>\n        <Button icon={<RollbackOutlined />} disabled={!history?.canGoPreStep()} onClick={() => history?.preStep()}>\n          撤销\n        </Button>\n        <Button\n          icon={<RollbackOutlined style={{ transform: 'rotateY(180deg)' }} />}\n          disabled={!history?.canGoNextStep()}\n          onClick={() => history?.nextStep()}\n        >\n          重做\n        </Button>\n        <Button onClick={() => history?.reset()}>重置</Button>\n      </div>\n    );\n  };\n\n  return <Engine plugins={DEFAULT_PLUGIN_LIST} schema={pageSchema} material={materials} onReady={onReady} />;\n}\n```\n\n## HotkeysPlugin - 快捷键\n\n快捷键插件为编辑器提供键盘快捷操作。\n\n### 内置快捷键\n\n| 快捷键             | 功能               | 说明                       |\n| :----------------- | :----------------- | :------------------------- |\n| `Ctrl + C`         | 复制节点           | 复制当前选中的节点         |\n| `Ctrl + V`         | 粘贴节点           | 粘贴已复制的节点           |\n| `Ctrl + Z`         | 撤销               | 撤销上一步操作             |\n| `Ctrl + Shift + Z` | 重做               | 重做下一步操作             |\n| `Delete`           | 删除节点           | 删除当前选中的节点         |\n| `↑` / `W`          | 向上移动           | 将节点向上移动             |\n| `↓` / `S`          | 向下移动           | 将节点向下移动             |\n| `←` / `A`          | 向左移动           | 将节点向左移动             |\n| `→` / `D`          | 向右移动           | 将节点向右移动             |\n| `Shift + W`        | 向上移动到兄弟节点 | 将节点移动到上一个兄弟位置 |\n| `Shift + S`        | 向下移动到兄弟节点 | 将节点移动到下一个兄弟位置 |\n\n### 功能特性\n\n- ⌨️ 键盘快捷操作\n- 🎯 支持组合键\n- 🔧 可扩展自定义快捷键\n- 🖥️ 支持主窗口和 iframe\n\n### 使用说明\n\n快捷键插件无需额外配置，加入插件列表即可使用：\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  return <Engine plugins={DEFAULT_PLUGIN_LIST} schema={pageSchema} material={materials} />;\n}\n```\n\n## ComponentLibPlugin - 组件库\n\n组件库插件显示可用的组件列表，支持拖拽添加到画布。\n\n### 功能特性\n\n- 📦 组件分类展示\n- 🔍 组件搜索\n- 🖱️ 拖拽添加组件\n- 🎨 自定义组件图标\n\n### 配置选项\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\nimport { PluginInstance } from '@chamn/engine';\nimport { ComponentLibPluginConfig } from '@chamn/engine';\n\nconst beforePluginRun = ({ pluginManager }) => {\n  pluginManager.customPlugin<PluginInstance<ComponentLibPluginConfig>>('ComponentLib', (pluginInstance) => {\n    // 自定义搜索栏\n    pluginInstance.ctx.config.customSearchBar = ({ defaultInputView, updateState }) => {\n      return (\n        <div>\n          {defaultInputView}\n          {/* 添加自定义元素 */}\n        </div>\n      );\n    };\n\n    return pluginInstance;\n  });\n};\n```\n\n### 使用示例\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\n// 定义组件物料\nconst materials = [\n  {\n    componentName: 'Button',\n    title: '按钮',\n    icon: <ButtonIcon />,\n    category: '基础组件',\n    snippets: [\n      {\n        title: '主要按钮',\n        snapshot: 'https://example.com/button-primary.png',\n        schema: {\n          componentName: 'Button',\n          props: {\n            type: 'primary',\n            children: '主要按钮',\n          },\n        },\n      },\n      {\n        title: '次要按钮',\n        snapshot: 'https://example.com/button-default.png',\n        schema: {\n          componentName: 'Button',\n          props: {\n            type: 'default',\n            children: '次要按钮',\n          },\n        },\n      },\n    ],\n  },\n];\n\nfunction App() {\n  return <Engine plugins={DEFAULT_PLUGIN_LIST} schema={pageSchema} material={materials} />;\n}\n```\n\n## OutlineTreePlugin - 页面结构树\n\n页面结构树插件以树形结构展示页面的组件层级。\n\n### 功能特性\n\n- 🌳 树形结构展示\n- 👁️ 显示/隐藏节点\n- 🔒 锁定/解锁节点\n- ✏️ 重命名节点\n- 🗑️ 删除节点\n- 📋 复制节点\n\n### 使用示例\n\n```tsx\nimport { Engine, plugins, EnginContext } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  const onReady = (ctx: EnginContext) => {\n    // 监听节点选中\n    ctx.engine.emitter.on('onSelectNodeChange', ({ node }) => {\n      if (node) {\n        console.log('选中节点:', node.title, node.id);\n      }\n    });\n  };\n\n  return <Engine plugins={DEFAULT_PLUGIN_LIST} schema={pageSchema} material={materials} onReady={onReady} />;\n}\n```\n\n## RightPanelPlugin - 右侧面板\n\n右侧面板插件提供属性编辑、样式编辑等功能面板的容器。\n\n### 功能特性\n\n- 📝 属性编辑面板\n- 🎨 样式编辑面板\n- 🔧 高级设置面板\n- 📊 组件状态面板\n\n### 子面板\n\n右侧面板包含多个子面板：\n\n- **PropertyPanel**: 属性面板\n- **VisualPanelPlus**: 样式面板\n- **AdvancePanel**: 高级面板\n- **ComponentStatePanel**: 组件状态面板\n- **EventPanel**: 事件面板\n\n### 配置选项\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\nimport { PluginInstance } from '@chamn/engine';\n\nconst beforePluginRun = ({ pluginManager }) => {\n  pluginManager.customPlugin('RightPanel', (pluginInstance) => {\n    // 自定义属性设置器\n    pluginInstance.ctx.config.customPropertySetterMap = {\n      MyCustomSetter: (props) => {\n        return <div>自定义设置器</div>;\n      },\n    };\n\n    return pluginInstance;\n  });\n};\n```\n\n### 使用示例\n\n```tsx\nimport { Engine, plugins, EnginContext } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  const onReady = (ctx: EnginContext) => {\n    const workbench = ctx.engine.getWorkbench();\n\n    // 可以控制右侧面板的显示/隐藏\n    workbench?.toggleRightPanel();\n  };\n\n  return <Engine plugins={DEFAULT_PLUGIN_LIST} schema={pageSchema} material={materials} onReady={onReady} />;\n}\n```\n\n## GlobalStatePanelPlugin - 全局状态管理\n\n全局状态管理插件用于管理页面的全局数据和变量。\n\n### 功能特性\n\n- 📊 管理全局变量\n- 🔄 数据绑定\n- 📝 JSON 编辑器\n- 💾 数据持久化\n\n### 使用示例\n\n```tsx\nimport { Engine, plugins, EnginContext } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  const onReady = (ctx: EnginContext) => {\n    // 访问页面模型的全局状态\n    const globalState = ctx.engine.pageModel.state;\n\n    console.log('全局状态:', globalState);\n\n    // 监听状态变化\n    ctx.engine.pageModel.emitter.on('onStateChange', () => {\n      console.log('状态已更新');\n    });\n  };\n\n  return <Engine plugins={DEFAULT_PLUGIN_LIST} schema={pageSchema} material={materials} onReady={onReady} />;\n}\n```\n\n## DisplaySourceSchema - 源码展示\n\n源码展示插件用于查看和复制页面的 JSON Schema。\n\n### 功能特性\n\n- 👀 查看页面 Schema\n- 📋 复制 Schema\n- 🎨 语法高亮\n- 💾 导出 JSON\n\n### 使用示例\n\n```tsx\nimport { Engine, plugins, EnginContext } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\nimport { Button, Modal } from 'antd';\nimport { DisplaySourceSchema } from '@chamn/engine';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  const onReady = (ctx: EnginContext) => {\n    const workbench = ctx.engine.getWorkbench();\n\n    workbench?.replaceTopBarView(\n      <div style={{ padding: '0 20px' }}>\n        <DisplaySourceSchema pageModel={ctx.engine.pageModel} engineCtx={ctx}>\n          <Button>查看源码</Button>\n        </DisplaySourceSchema>\n      </div>\n    );\n  };\n\n  return <Engine plugins={DEFAULT_PLUGIN_LIST} schema={pageSchema} material={materials} onReady={onReady} />;\n}\n```\n\n## 自定义插件列表\n\n你可以根据需要选择性地使用插件：\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\n\nconst { DesignerPlugin, ComponentLibPlugin, OutlineTreePlugin, RightPanelPlugin, HistoryPlugin, HotkeysPlugin } =\n  plugins;\n\nfunction App() {\n  // 只使用必要的插件\n  const customPluginList = [\n    DesignerPlugin, // 必需：设计器画布\n    ComponentLibPlugin, // 组件库\n    RightPanelPlugin, // 右侧面板\n    HistoryPlugin, // 历史记录\n  ];\n\n  return <Engine plugins={customPluginList} schema={pageSchema} material={materials} />;\n}\n```\n\n## 插件开发\n\n如果内置插件不能满足需求，可以开发自定义插件。详见 [插件开发文档](./plugin-develop/)。\n\n### 插件结构\n\n```tsx\nimport { CPlugin } from '@chamn/engine';\n\nconst MyPlugin: CPlugin = (ctx) => {\n  return {\n    name: 'MyPlugin',\n    async init(ctx) {\n      // 初始化逻辑\n      const workbench = ctx.getWorkbench();\n      workbench?.addLeftPanel({\n        name: 'MyPanel',\n        title: '我的面板',\n        icon: <Icon />,\n        view: <MyPanelView />,\n      });\n\n      ctx.pluginReadyOk();\n    },\n    async destroy(ctx) {\n      // 清理逻辑\n    },\n    export: (ctx) => {\n      // 导出 API\n      return {\n        doSomething() {\n          // ...\n        },\n      };\n    },\n    meta: {\n      engine: {\n        version: '1.0.0',\n      },\n    },\n  };\n};\n```\n\n## 常见问题\n\n### Q: 如何禁用某个插件？\n\nA: 从 `DEFAULT_PLUGIN_LIST` 中过滤掉不需要的插件，然后将定制后的插件列表传递给引擎。\n\n**禁用单个插件：**\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\n\nconst { DEFAULT_PLUGIN_LIST, HotkeysPlugin } = plugins;\n\nfunction App() {\n  // 过滤掉快捷键插件\n  const customPlugins = DEFAULT_PLUGIN_LIST.filter((plugin) => plugin !== HotkeysPlugin);\n\n  return <Engine plugins={customPlugins} schema={pageSchema} material={materials} />;\n}\n```\n\n**禁用多个插件：**\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\n\nconst { DEFAULT_PLUGIN_LIST, HotkeysPlugin, GlobalStatePanelPlugin } = plugins;\n\nfunction App() {\n  // 要禁用的插件列表\n  const pluginsToDisable = [HotkeysPlugin, GlobalStatePanelPlugin];\n\n  // 过滤掉要禁用的插件\n  const customPlugins = DEFAULT_PLUGIN_LIST.filter((plugin) => !pluginsToDisable.includes(plugin));\n\n  return <Engine plugins={customPlugins} schema={pageSchema} material={materials} />;\n}\n```\n\n### Q: 插件加载顺序重要吗？\n\nA: 是的。建议按以下顺序加载：\n\n1. DesignerPlugin（必须最先）\n2. 其他功能插件\n3. UI 面板插件\n\n### Q: 如何获取插件实例？\n\nA: 使用 `pluginManager.get()` 方法：\n\n```tsx\nconst onReady = async (ctx: EnginContext) => {\n  const designerPlugin = await ctx.pluginManager.get('Designer');\n  const historyPlugin = await ctx.pluginManager.get('History');\n};\n```\n\n### Q: 插件配置何时生效？\n\nA: 插件配置必须在 `beforePluginRun` 中设置：\n\n```tsx\nconst beforePluginRun = ({ pluginManager }) => {\n  pluginManager.customPlugin('Designer', (pluginInstance) => {\n    // 配置插件\n    return pluginInstance;\n  });\n};\n\n<Engine beforePluginRun={beforePluginRun} />;\n```\n"
  },
  {
    "path": "packages/docs-app/src/content/docs/reference/Plugin/custom-plugin-guide.mdx",
    "content": "---\nsidebar:\n  order: 5\ntitle: 替换内置插件实战指南\n---\n\n# 替换内置插件实战指南\n\n本指南将通过实际案例，教你如何从零开发自定义插件、替换内置插件，以及实现高度定制的引擎。\n\n## CPlugin 类型定义\n\n在开始之前，先了解 `CPlugin` 的类型定义：\n\n```typescript\n// 插件对象定义\nexport type PluginObj<C, E = any> = {\n  name: string; // 插件唯一名称（必需）\n  PLUGIN_NAME?: string; // 插件静态名称（可选，用于类型安全）\n  init: (ctx: CPluginCtx<C>) => Promise<void>; // 初始化方法（必需）\n  destroy: (ctx: CPluginCtx<C>) => Promise<void>; // 销毁方法（必需）\n  reload?: (ctx: CPluginCtx<C>) => Promise<void>; // 重载方法（可选）\n  export: (ctx: CPluginCtx<C>) => E; // 导出 API（必需）\n  meta: {\n    // 元数据（必需）\n    engine: {\n      version: string;\n    };\n  };\n};\n\n// 插件函数定义\ninterface PluginFunction<C, E> {\n  (ctx: CPluginCtx<C>): PluginObj<C, E>;\n  ['PLUGIN_NAME']?: string;\n}\n\n// CPlugin 可以是对象或函数\nexport type CPlugin<C = Record<string, any>, E = any> =\n  | PluginObj<C, E> // 直接是插件对象\n  | PluginFunction<C, E>; // 或者是返回插件对象的函数\n\n// 插件上下文定义\nexport type CPluginCtx<C = any> = {\n  name?: string; // 插件名称\n  globalEmitter: Emitter<any>; // 全局事件发射器\n  emitter: Emitter<any>; // 插件私有事件发射器\n  config: C; // 插件配置对象\n  pluginManager: PluginManager; // 插件管理器\n  pluginReadyOk: () => void; // 通知插件已准备好\n  getWorkbench: () => Workbench; // 获取工作台实例\n  pageModel: CPage; // 页面模型\n  i18n: CustomI18n; // 国际化对象\n  assetsPackageListManager: AssetsPackageListManager; // 资源包管理器\n  engine: Engine; // 引擎实例\n};\n```\n\n### 两种插件定义方式\n\n**方式 1：函数式定义（推荐）**\n\n```typescript\nexport const MyPlugin: CPlugin = (ctx) => {\n  // 可以访问 ctx 并创建闭包变量\n  let localState = {};\n\n  return {\n    name: 'MyPlugin',\n    async init(ctx) {\n      // 初始化逻辑\n      ctx.pluginReadyOk();\n    },\n    async destroy(ctx) {\n      // 清理逻辑\n    },\n    export: (ctx) => ({\n      // 导出的 API\n    }),\n    meta: {\n      engine: { version: '1.0.0' },\n    },\n  };\n};\n```\n\n**方式 2：对象式定义**\n\n```typescript\nexport const MyPlugin: CPlugin = {\n  name: 'MyPlugin',\n  async init(ctx) {\n    ctx.pluginReadyOk();\n  },\n  async destroy(ctx) {},\n  export: (ctx) => ({}),\n  meta: {\n    engine: { version: '1.0.0' },\n  },\n};\n```\n\n## 开发自定义插件\n\n### 场景 1：开发一个自定义工具栏插件\n\n假设我们要开发一个自定义工具栏插件，提供快速操作功能。\n\n#### 第一步：定义插件结构\n\n```typescript\n// plugins/CustomToolbar/index.tsx\nimport { CPlugin } from '@chamn/engine';\nimport React from 'react';\n\nconst PLUGIN_NAME = 'CustomToolbar' as const;\n\nexport const CustomToolbarPlugin: CPlugin = (ctx) => {\n  return {\n    name: PLUGIN_NAME,\n\n    async init(ctx) {\n      const workbench = ctx.getWorkbench();\n\n      // 添加顶部工具栏\n      workbench.addTopBarView({\n        name: PLUGIN_NAME,\n        view: <CustomToolbarView ctx={ctx} />,\n      });\n\n      // 通知插件已准备好\n      ctx.pluginReadyOk();\n    },\n\n    async destroy(ctx) {\n      console.log('CustomToolbar 插件已卸载');\n    },\n\n    export: (ctx) => ({\n      // 暴露给外部的 API\n      refresh() {\n        console.log('刷新工具栏');\n      },\n      addButton(config: any) {\n        console.log('添加自定义按钮', config);\n      },\n    }),\n\n    meta: {\n      engine: {\n        version: '1.0.0',\n      },\n    },\n  };\n};\n\nCustomToolbarPlugin.PLUGIN_NAME = PLUGIN_NAME;\n```\n\n#### 第二步：实现工具栏 UI\n\n```typescript\n// plugins/CustomToolbar/CustomToolbarView.tsx\nimport React, { useState } from 'react';\nimport { CPluginCtx } from '@chamn/engine';\n\ninterface CustomToolbarViewProps {\n  ctx: CPluginCtx;\n}\n\nexport const CustomToolbarView: React.FC<CustomToolbarViewProps> = ({ ctx }) => {\n  const [selectedNode, setSelectedNode] = useState<any>(null);\n\n  // 监听节点选中事件\n  React.useEffect(() => {\n    const handleSelect = (node: any) => {\n      setSelectedNode(node);\n    };\n\n    ctx.emitter.on('onSelectNode', handleSelect);\n\n    return () => {\n      ctx.emitter.off('onSelectNode', handleSelect);\n    };\n  }, [ctx]);\n\n  const handleCopy = () => {\n    if (selectedNode) {\n      const nodeData = ctx.pageModel.getNode(selectedNode.id);\n      console.log('复制节点', nodeData);\n      // 实现复制逻辑\n    }\n  };\n\n  const handleDelete = () => {\n    if (selectedNode) {\n      ctx.pageModel.deleteNode(selectedNode.id);\n      ctx.emitter.emit('onDeleteNode', selectedNode);\n    }\n  };\n\n  return (\n    <div style={{ display: 'flex', gap: '8px', padding: '8px' }}>\n      <button onClick={handleCopy} disabled={!selectedNode}>\n        复制节点\n      </button>\n      <button onClick={handleDelete} disabled={!selectedNode}>\n        删除节点\n      </button>\n      <button onClick={() => ctx.pageModel.clear()}>清空画布</button>\n    </div>\n  );\n};\n```\n\n#### 第三步：使用插件\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\nimport { CustomToolbarPlugin } from './plugins/CustomToolbar';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  const onReady = async (ctx) => {\n    // 获取自定义工具栏插件\n    const toolbar = await ctx.pluginManager.get('CustomToolbar');\n\n    // 调用插件 API\n    toolbar?.export.addButton({\n      label: '自定义按钮',\n      onClick: () => console.log('点击了自定义按钮'),\n    });\n  };\n\n  return (\n    <Engine\n      plugins={[\n        ...DEFAULT_PLUGIN_LIST,\n        CustomToolbarPlugin, // 添加自定义插件\n      ]}\n      schema={pageSchema}\n      material={materials}\n      onReady={onReady}\n    />\n  );\n}\n```\n\n### 场景 2：开发带配置的插件\n\n开发一个可配置的代码生成器插件。\n\n```typescript\n// plugins/CodeGenerator/index.tsx\nimport { CPlugin } from '@chamn/engine';\n\n// 定义插件配置类型\ninterface CodeGeneratorConfig {\n  framework: 'react' | 'vue' | 'angular';\n  typescript: boolean;\n  outputPath?: string;\n}\n\nconst PLUGIN_NAME = 'CodeGenerator' as const;\n\nexport const CodeGeneratorPlugin: CPlugin<CodeGeneratorConfig> = (ctx) => {\n  // 从上下文获取配置\n  const config = ctx.config || {\n    framework: 'react',\n    typescript: true,\n  };\n\n  return {\n    name: PLUGIN_NAME,\n\n    async init(ctx) {\n      const workbench = ctx.getWorkbench();\n\n      // 添加右侧面板\n      workbench.addRightPanel({\n        name: PLUGIN_NAME,\n        title: '代码生成',\n        view: <CodeGeneratorPanel config={config} ctx={ctx} />,\n      });\n\n      ctx.pluginReadyOk();\n    },\n\n    async destroy(ctx) {\n      console.log('CodeGenerator 插件已卸载');\n    },\n\n    export: (ctx) => ({\n      generate() {\n        const schema = ctx.pageModel.export();\n        return generateCode(schema, config);\n      },\n      setConfig(newConfig: Partial<CodeGeneratorConfig>) {\n        Object.assign(config, newConfig);\n      },\n    }),\n\n    meta: {\n      engine: {\n        version: '1.0.0',\n      },\n    },\n  };\n};\n\nfunction generateCode(schema: any, config: CodeGeneratorConfig) {\n  // 根据配置生成代码\n  if (config.framework === 'react') {\n    return generateReactCode(schema, config.typescript);\n  }\n  // ... 其他框架\n}\n\nCodeGeneratorPlugin.PLUGIN_NAME = PLUGIN_NAME;\n```\n\n#### 使用带配置的插件\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\nimport { CodeGeneratorPlugin } from './plugins/CodeGenerator';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  const beforePluginRun = ({ pluginManager }) => {\n    // 在插件初始化前配置插件\n    pluginManager.customPlugin('CodeGenerator', (pluginInstance) => {\n      pluginInstance.ctx.config = {\n        framework: 'react',\n        typescript: true,\n        outputPath: './src/generated',\n      };\n      return pluginInstance;\n    });\n  };\n\n  return (\n    <Engine\n      plugins={[...DEFAULT_PLUGIN_LIST, CodeGeneratorPlugin]}\n      schema={pageSchema}\n      material={materials}\n      beforePluginRun={beforePluginRun}\n    />\n  );\n}\n```\n\n## 替换内置插件\n\nChameleon Engine 的所有内置插件都可以被替换。替换的核心思路是：**不加载要替换的内置插件，而是加载自己的插件**。\n\n### 内置插件列表\n\n```typescript\nimport { plugins } from '@chamn/engine';\n\n// 获取默认插件列表\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\n// 单独导入内置插件\nconst {\n  DesignerPlugin, // 设计器插件\n  ComponentLibPlugin, // 组件库插件\n  RightPanelPlugin, // 右侧属性面板插件\n  OutlineTreePlugin, // 大纲树插件\n  GlobalStatePanelPlugin, // 全局状态面板插件\n  HistoryPlugin, // 历史记录插件\n  HotkeysPlugin, // 快捷键插件\n} = plugins;\n\n// 获取插件名称常量\nconst { DEFAULT_PLUGIN_NAME_MAP } = plugins;\n// {\n//   DesignerPlugin: 'Designer',\n//   ComponentLibPlugin: 'ComponentLib',\n//   RightPanelPlugin: 'RightPanel',\n//   ...\n// }\n```\n\n### 场景 1：替换属性面板插件\n\n假设内置的 `RightPanel` 不满足需求，我们要完全替换它。\n\n#### 第一步：开发自定义属性面板\n\n```typescript\n// plugins/CustomRightPanel/index.tsx\nimport { CPlugin } from '@chamn/engine';\nimport React from 'react';\n\nconst PLUGIN_NAME = 'CustomRightPanel' as const;\n\nexport const CustomRightPanelPlugin: CPlugin = (ctx) => {\n  return {\n    name: PLUGIN_NAME,\n\n    async init(ctx) {\n      const workbench = ctx.getWorkbench();\n\n      // 添加完全自定义的右侧面板\n      workbench.replaceRightPanel({\n        name: PLUGIN_NAME,\n        view: <CustomPropertyEditor ctx={ctx} />,\n      });\n\n      ctx.pluginReadyOk();\n    },\n\n    async destroy(ctx) {\n      console.log('CustomRightPanel 插件已卸载');\n    },\n\n    export: (ctx) => ({\n      // 暴露与内置插件兼容的 API\n      updateProperty(key: string, value: any) {\n        const selectedNode = ctx.pageModel.getSelectedNode();\n        if (selectedNode) {\n          ctx.pageModel.updateNode(selectedNode.id, {\n            props: {\n              ...selectedNode.props,\n              [key]: value,\n            },\n          });\n        }\n      },\n    }),\n\n    meta: {\n      engine: {\n        version: '1.0.0',\n      },\n    },\n  };\n};\n\nCustomRightPanelPlugin.PLUGIN_NAME = PLUGIN_NAME;\n```\n\n#### 第二步：实现自定义属性编辑器\n\n```tsx\n// plugins/CustomRightPanel/CustomPropertyEditor.tsx\nimport React, { useState, useEffect } from 'react';\nimport { CPluginCtx } from '@chamn/engine';\n\ninterface CustomPropertyEditorProps {\n  ctx: CPluginCtx;\n}\n\nexport const CustomPropertyEditor: React.FC<CustomPropertyEditorProps> = ({ ctx }) => {\n  const [selectedNode, setSelectedNode] = useState<any>(null);\n  const [properties, setProperties] = useState<Record<string, any>>({});\n\n  useEffect(() => {\n    const handleSelect = (node: any) => {\n      setSelectedNode(node);\n      if (node) {\n        const nodeData = ctx.pageModel.getNode(node.id);\n        setProperties(nodeData?.props || {});\n      }\n    };\n\n    ctx.emitter.on('onSelectNode', handleSelect);\n\n    return () => {\n      ctx.emitter.off('onSelectNode', handleSelect);\n    };\n  }, [ctx]);\n\n  const handlePropertyChange = (key: string, value: any) => {\n    if (selectedNode) {\n      ctx.pageModel.updateNode(selectedNode.id, {\n        props: {\n          ...properties,\n          [key]: value,\n        },\n      });\n      setProperties((prev) => ({ ...prev, [key]: value }));\n    }\n  };\n\n  if (!selectedNode) {\n    return <div style={{ padding: '16px' }}>请选择一个节点</div>;\n  }\n\n  return (\n    <div style={{ padding: '16px' }}>\n      <h3>自定义属性面板</h3>\n      <div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>\n        {Object.entries(properties).map(([key, value]) => (\n          <div key={key}>\n            <label>{key}:</label>\n            <input\n              type=\"text\"\n              value={value}\n              onChange={(e) => handlePropertyChange(key, e.target.value)}\n              style={{ width: '100%', marginTop: '4px' }}\n            />\n          </div>\n        ))}\n      </div>\n    </div>\n  );\n};\n```\n\n#### 第三步：替换内置插件\n\n**核心思路：从 `DEFAULT_PLUGIN_LIST` 中移除要替换的插件，然后添加自定义插件。**\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\nimport { CustomRightPanelPlugin } from './plugins/CustomRightPanel';\n\nconst { DEFAULT_PLUGIN_LIST, RightPanelPlugin } = plugins;\n\nfunction App() {\n  // 1. 复制默认插件列表\n  const customPlugins = [...DEFAULT_PLUGIN_LIST];\n\n  // 2. 找到要替换的插件的索引\n  const rightPanelIndex = customPlugins.findIndex((plugin) => plugin === RightPanelPlugin);\n\n  // 3. 如果找到了，移除内置插件\n  if (rightPanelIndex !== -1) {\n    customPlugins.splice(rightPanelIndex, 1);\n  }\n\n  // 4. 添加自定义插件\n  customPlugins.push(CustomRightPanelPlugin);\n\n  return <Engine plugins={customPlugins} schema={pageSchema} material={materials} />;\n}\n```\n\n**简化写法（推荐）**\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\nimport { CustomRightPanelPlugin } from './plugins/CustomRightPanel';\n\nconst { DEFAULT_PLUGIN_LIST, RightPanelPlugin } = plugins;\n\nfunction App() {\n  // 过滤掉要替换的插件，然后添加自定义插件\n  const customPlugins = [\n    ...DEFAULT_PLUGIN_LIST.filter((plugin) => plugin !== RightPanelPlugin),\n    CustomRightPanelPlugin,\n  ];\n\n  return <Engine plugins={customPlugins} schema={pageSchema} material={materials} />;\n}\n```\n\n**批量替换多个插件**\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\nimport { CustomRightPanelPlugin } from './plugins/CustomRightPanel';\nimport { CustomComponentLibPlugin } from './plugins/CustomComponentLib';\n\nconst { DEFAULT_PLUGIN_LIST, RightPanelPlugin, ComponentLibPlugin } = plugins;\n\nfunction App() {\n  // 要替换的插件列表\n  const pluginsToReplace = [RightPanelPlugin, ComponentLibPlugin];\n\n  // 过滤掉要替换的插件\n  const customPlugins = [\n    ...DEFAULT_PLUGIN_LIST.filter((plugin) => !pluginsToReplace.includes(plugin)),\n    // 添加自定义插件\n    CustomRightPanelPlugin,\n    CustomComponentLibPlugin,\n  ];\n\n  return <Engine plugins={customPlugins} schema={pageSchema} material={materials} />;\n}\n```\n\n### 场景 2：禁用某个插件\n\n如果不需要某个内置插件，可以从 `DEFAULT_PLUGIN_LIST` 中过滤掉，然后将定制后的插件列表传递给引擎。\n\n**禁用单个插件**\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\n\nconst { DEFAULT_PLUGIN_LIST, GlobalStatePanelPlugin } = plugins;\n\nfunction App() {\n  // 过滤掉不需要的插件\n  const customPlugins = DEFAULT_PLUGIN_LIST.filter((plugin) => plugin !== GlobalStatePanelPlugin);\n\n  return <Engine plugins={customPlugins} schema={pageSchema} material={materials} />;\n}\n```\n\n**禁用多个插件**\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\n\nconst { DEFAULT_PLUGIN_LIST, GlobalStatePanelPlugin, OutlineTreePlugin, HotkeysPlugin } = plugins;\n\nfunction App() {\n  // 要禁用的插件列表\n  const pluginsToDisable = [GlobalStatePanelPlugin, OutlineTreePlugin, HotkeysPlugin];\n\n  // 过滤掉要禁用的插件\n  const customPlugins = DEFAULT_PLUGIN_LIST.filter((plugin) => !pluginsToDisable.includes(plugin));\n\n  return <Engine plugins={customPlugins} schema={pageSchema} material={materials} />;\n}\n```\n\n**使用插件名称禁用**\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\n\nconst { DEFAULT_PLUGIN_LIST, DEFAULT_PLUGIN_NAME_MAP } = plugins;\n\nfunction App() {\n  // 要禁用的插件名称列表\n  const pluginNamesToDisable = [\n    DEFAULT_PLUGIN_NAME_MAP.GlobalStatePanelPlugin,\n    DEFAULT_PLUGIN_NAME_MAP.OutlineTreePlugin,\n  ];\n\n  // 通过插件名称过滤\n  const customPlugins = DEFAULT_PLUGIN_LIST.filter((plugin) => !pluginNamesToDisable.includes(plugin.PLUGIN_NAME));\n\n  return <Engine plugins={customPlugins} schema={pageSchema} material={materials} />;\n}\n```\n\n### 场景 3：使用 customPlugin 定制内置插件\n\n如果只需要修改内置插件的部分行为，可以使用 `customPlugin` 而不是完全替换。**重要：`customPlugin` 必须在插件初始化前调用，因此要在 `beforePluginRun` 回调中使用。**\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  const beforePluginRun = ({ pluginManager }) => {\n    // 在插件初始化前定制它\n    pluginManager.customPlugin('History', (pluginInstance) => {\n      // 修改插件配置\n      pluginInstance.ctx.config = {\n        ...pluginInstance.ctx.config,\n        maxHistoryLength: 100, // 自定义历史记录长度\n        enableRedo: true,\n      };\n\n      // 修改插件导出的 API\n      const originalExport = pluginInstance.export;\n      pluginInstance.export = {\n        ...originalExport,\n        // 添加自定义方法\n        clearHistory() {\n          console.log('清空历史记录');\n          originalExport.clear();\n        },\n      };\n\n      return pluginInstance;\n    });\n  };\n\n  return (\n    <Engine plugins={DEFAULT_PLUGIN_LIST} schema={pageSchema} material={materials} beforePluginRun={beforePluginRun} />\n  );\n}\n```\n\n## 实现高度定制引擎\n\n通过组合自定义插件和内置插件，可以实现完全定制的引擎。\n\n### 场景 1：构建轻量级表单设计器\n\n只保留必要的插件，移除不需要的功能。\n\n```tsx\n// FormDesignerEngine.tsx\nimport React from 'react';\nimport { Engine, plugins } from '@chamn/engine';\nimport { CustomFormToolbarPlugin } from './plugins/CustomFormToolbar';\nimport { FormFieldLibraryPlugin } from './plugins/FormFieldLibrary';\nimport { FormPropertyEditorPlugin } from './plugins/FormPropertyEditor';\n\nconst {\n  DesignerPlugin, // 保留设计器\n  HistoryPlugin, // 保留历史记录\n  HotkeysPlugin, // 保留快捷键\n  OutlineTreePlugin, // 保留大纲树\n  // 移除其他不需要的插件\n} = plugins;\n\ninterface FormDesignerEngineProps {\n  schema?: any;\n  materials?: any;\n  onSave?: (schema: any) => void;\n}\n\nexport const FormDesignerEngine: React.FC<FormDesignerEngineProps> = ({ schema, materials, onSave }) => {\n  const customPlugins = [\n    // 内置核心插件\n    DesignerPlugin,\n    HistoryPlugin,\n    HotkeysPlugin,\n    OutlineTreePlugin,\n\n    // 自定义表单插件\n    CustomFormToolbarPlugin,\n    FormFieldLibraryPlugin,\n    FormPropertyEditorPlugin,\n  ];\n\n  const beforePluginRun = ({ pluginManager }) => {\n    // 定制历史记录插件\n    pluginManager.customPlugin('History', (pluginInstance) => {\n      pluginInstance.ctx.config = {\n        maxHistoryLength: 50,\n      };\n      return pluginInstance;\n    });\n  };\n\n  const onReady = async (ctx) => {\n    // 隐藏不需要的工作台组件\n    const workbench = ctx.getWorkbench();\n    workbench.hiddenWidget(['TopBar']);\n\n    // 获取自定义工具栏并配置保存功能\n    const toolbar = await ctx.pluginManager.get('CustomFormToolbar');\n    toolbar?.export.onSave(() => {\n      const currentSchema = ctx.pageModel.export();\n      onSave?.(currentSchema);\n    });\n  };\n\n  return (\n    <Engine\n      plugins={customPlugins}\n      schema={schema}\n      material={materials}\n      beforePluginRun={beforePluginRun}\n      onReady={onReady}\n    />\n  );\n};\n\n// 使用表单设计器\nfunction App() {\n  const handleSave = (schema: any) => {\n    console.log('保存表单配置', schema);\n    // 发送到服务器\n  };\n\n  return <FormDesignerEngine schema={initialFormSchema} materials={formFieldMaterials} onSave={handleSave} />;\n}\n```\n\n### 场景 2：构建移动端页面编辑器\n\n针对移动端场景定制引擎。\n\n```tsx\n// MobilePageEditor.tsx\nimport React from 'react';\nimport { Engine, plugins } from '@chamn/engine';\nimport { MobilePreviewPlugin } from './plugins/MobilePreview';\nimport { MobileComponentLibPlugin } from './plugins/MobileComponentLib';\nimport { ResponsiveToolbarPlugin } from './plugins/ResponsiveToolbar';\n\nconst { DesignerPlugin, HistoryPlugin, HotkeysPlugin } = plugins;\n\ninterface MobilePageEditorProps {\n  schema?: any;\n  materials?: any;\n  deviceType?: 'ios' | 'android';\n}\n\nexport const MobilePageEditor: React.FC<MobilePageEditorProps> = ({ schema, materials, deviceType = 'ios' }) => {\n  const customPlugins = [\n    DesignerPlugin,\n    HistoryPlugin,\n    HotkeysPlugin,\n    MobilePreviewPlugin,\n    MobileComponentLibPlugin,\n    ResponsiveToolbarPlugin,\n  ];\n\n  const beforePluginRun = ({ pluginManager }) => {\n    // 配置移动端预览插件\n    pluginManager.customPlugin('MobilePreview', (pluginInstance) => {\n      pluginInstance.ctx.config = {\n        deviceType,\n        screenWidth: 375,\n        showDeviceFrame: true,\n      };\n      return pluginInstance;\n    });\n  };\n\n  const onReady = async (ctx) => {\n    const workbench = ctx.getWorkbench();\n\n    // 定制工作台布局\n    workbench.setLayout({\n      leftPanelWidth: 260,\n      rightPanelWidth: 320,\n      showTopBar: false,\n    });\n\n    // 配置移动端特定的快捷键\n    const hotkeys = await ctx.pluginManager.get('Hotkeys');\n    hotkeys?.export.register({\n      'cmd+p': () => {\n        // 切换预览模式\n        const preview = await ctx.pluginManager.get('MobilePreview');\n        preview?.export.togglePreview();\n      },\n      'cmd+d': () => {\n        // 切换设备类型\n        const preview = await ctx.pluginManager.get('MobilePreview');\n        preview?.export.switchDevice();\n      },\n    });\n  };\n\n  return (\n    <Engine\n      plugins={customPlugins}\n      schema={schema}\n      material={materials}\n      beforePluginRun={beforePluginRun}\n      onReady={onReady}\n    />\n  );\n};\n```\n\n### 场景 3：企业级定制引擎\n\n集成权限控制、团队协作、版本管理等企业功能。\n\n```tsx\n// EnterpriseEngine.tsx\nimport React from 'react';\nimport { Engine, plugins } from '@chamn/engine';\nimport { AuthPlugin } from './plugins/Auth';\nimport { CollaborationPlugin } from './plugins/Collaboration';\nimport { VersionControlPlugin } from './plugins/VersionControl';\nimport { AuditLogPlugin } from './plugins/AuditLog';\nimport { TemplateMarketPlugin } from './plugins/TemplateMarket';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\ninterface EnterpriseEngineProps {\n  user: {\n    id: string;\n    name: string;\n    role: 'admin' | 'editor' | 'viewer';\n  };\n  projectId: string;\n  schema?: any;\n  materials?: any;\n}\n\nexport const EnterpriseEngine: React.FC<EnterpriseEngineProps> = ({ user, projectId, schema, materials }) => {\n  const enterprisePlugins = [\n    ...DEFAULT_PLUGIN_LIST,\n    AuthPlugin,\n    CollaborationPlugin,\n    VersionControlPlugin,\n    AuditLogPlugin,\n    TemplateMarketPlugin,\n  ];\n\n  const beforePluginRun = ({ pluginManager }) => {\n    // 配置权限插件\n    pluginManager.customPlugin('Auth', (pluginInstance) => {\n      pluginInstance.ctx.config = { user };\n      return pluginInstance;\n    });\n\n    // 配置协作插件\n    pluginManager.customPlugin('Collaboration', (pluginInstance) => {\n      pluginInstance.ctx.config = {\n        projectId,\n        wsUrl: 'wss://collaboration.example.com',\n      };\n      return pluginInstance;\n    });\n\n    // 配置版本控制插件\n    pluginManager.customPlugin('VersionControl', (pluginInstance) => {\n      pluginInstance.ctx.config = {\n        autoSave: true,\n        saveInterval: 30000,\n      };\n      return pluginInstance;\n    });\n  };\n\n  const onReady = async (ctx) => {\n    // 初始化权限控制\n    const auth = await ctx.pluginManager.get('Auth');\n    const permissions = auth?.export.getPermissions();\n\n    // 根据权限控制功能\n    if (permissions?.role === 'viewer') {\n      // 只读模式\n      const designer = await ctx.pluginManager.get('Designer');\n      designer?.export.setReadonly(true);\n\n      const workbench = ctx.getWorkbench();\n      workbench.hiddenWidget(['LeftPanel', 'RightPanel']);\n    }\n\n    // 启动协作\n    const collab = await ctx.pluginManager.get('Collaboration');\n    collab?.export.connect();\n\n    // 监听协作事件\n    ctx.emitter.on('collaboration:userJoin', (user: any) => {\n      console.log(`${user.name} 加入了协作`);\n    });\n\n    ctx.emitter.on('collaboration:change', (change: any) => {\n      console.log('收到远程修改', change);\n    });\n\n    // 启动自动保存\n    const versionControl = await ctx.pluginManager.get('VersionControl');\n    versionControl?.export.startAutoSave();\n\n    // 记录审计日志\n    const auditLog = await ctx.pluginManager.get('AuditLog');\n    auditLog?.export.log('PROJECT_OPENED', {\n      projectId,\n      user: user.name,\n    });\n  };\n\n  return (\n    <Engine\n      plugins={enterprisePlugins}\n      schema={schema}\n      material={materials}\n      beforePluginRun={beforePluginRun}\n      onReady={onReady}\n    />\n  );\n};\n\n// 使用企业版引擎\nfunction App() {\n  const currentUser = {\n    id: '123',\n    name: 'John Doe',\n    role: 'editor' as const,\n  };\n\n  return (\n    <EnterpriseEngine user={currentUser} projectId=\"project-456\" schema={projectSchema} materials={projectMaterials} />\n  );\n}\n```\n\n## 插件开发最佳实践\n\n### 1. 插件命名规范\n\n```typescript\n// ✅ 推荐：使用描述性名称和常量\nconst PLUGIN_NAME = 'CustomFormBuilder' as const;\n\nexport const CustomFormBuilderPlugin: CPlugin = (ctx) => {\n  return {\n    name: PLUGIN_NAME,\n    // ...\n  };\n};\n\nCustomFormBuilderPlugin.PLUGIN_NAME = PLUGIN_NAME;\n\n// ❌ 不推荐：使用字符串字面量\nexport const MyPlugin: CPlugin = (ctx) => {\n  return {\n    name: 'my-plugin', // 难以追踪和重构\n    // ...\n  };\n};\n```\n\n### 2. 正确管理生命周期\n\n```typescript\nexport const MyPlugin: CPlugin = (ctx) => {\n  let unsubscribeList: Array<() => void> = [];\n\n  return {\n    name: 'MyPlugin',\n\n    async init(ctx) {\n      // 订阅事件\n      const off1 = ctx.emitter.on('onSelectNode', handleSelect);\n      const off2 = ctx.globalEmitter.on('onPageChange', handlePageChange);\n\n      unsubscribeList.push(off1, off2);\n\n      // ✅ 重要：通知插件已准备好\n      ctx.pluginReadyOk();\n    },\n\n    async destroy(ctx) {\n      // ✅ 清理所有订阅\n      unsubscribeList.forEach((off) => off());\n      unsubscribeList = [];\n\n      // ✅ 清理其他资源\n      // 移除 DOM 节点、取消定时器等\n    },\n\n    export: () => ({}),\n\n    meta: {\n      engine: { version: '1.0.0' },\n    },\n  };\n};\n```\n\n### 3. 使用 TypeScript 类型安全\n\n```typescript\n// 定义配置类型\ninterface MyPluginConfig {\n  theme: 'light' | 'dark';\n  locale: string;\n  features: {\n    autoSave: boolean;\n    collaboration: boolean;\n  };\n}\n\n// 定义导出 API 类型\ninterface MyPluginExport {\n  setTheme(theme: 'light' | 'dark'): void;\n  save(): Promise<void>;\n  load(id: string): Promise<any>;\n}\n\n// 使用类型\nexport const MyPlugin: CPlugin<MyPluginConfig, MyPluginExport> = (ctx) => {\n  const config = ctx.config || {\n    theme: 'light',\n    locale: 'zh-CN',\n    features: {\n      autoSave: true,\n      collaboration: false,\n    },\n  };\n\n  return {\n    name: 'MyPlugin',\n\n    async init(ctx) {\n      // ctx.config 现在有类型提示\n      console.log(config.theme);\n      ctx.pluginReadyOk();\n    },\n\n    async destroy(ctx) {},\n\n    export: (ctx): MyPluginExport => ({\n      setTheme(theme) {\n        config.theme = theme;\n      },\n      async save() {\n        // 实现保存逻辑\n      },\n      async load(id) {\n        // 实现加载逻辑\n        return {};\n      },\n    }),\n\n    meta: {\n      engine: { version: '1.0.0' },\n    },\n  };\n};\n```\n\n### 4. 插件间通信\n\n```typescript\n// PluginA：发送事件\nexport const PluginA: CPlugin = (ctx) => {\n  return {\n    name: 'PluginA',\n\n    async init(ctx) {\n      // 使用全局事件总线通信\n      ctx.globalEmitter.emit('pluginA:dataReady', {\n        data: 'some data',\n      });\n\n      ctx.pluginReadyOk();\n    },\n\n    async destroy(ctx) {},\n\n    export: (ctx) => ({\n      notifyChange(data: any) {\n        ctx.globalEmitter.emit('pluginA:change', data);\n      },\n    }),\n\n    meta: {\n      engine: { version: '1.0.0' },\n    },\n  };\n};\n\n// PluginB：接收事件\nexport const PluginB: CPlugin = (ctx) => {\n  return {\n    name: 'PluginB',\n\n    async init(ctx) {\n      // 等待 PluginA 准备好\n      await ctx.pluginManager.onPluginReadyOk('PluginA');\n\n      // 监听 PluginA 的事件\n      ctx.globalEmitter.on('pluginA:change', (data) => {\n        console.log('收到 PluginA 的变化', data);\n      });\n\n      // 获取 PluginA 实例并调用其 API\n      const pluginA = await ctx.pluginManager.get('PluginA');\n      pluginA?.export.notifyChange({ from: 'PluginB' });\n\n      ctx.pluginReadyOk();\n    },\n\n    async destroy(ctx) {},\n\n    export: () => ({}),\n\n    meta: {\n      engine: { version: '1.0.0' },\n    },\n  };\n};\n```\n\n### 5. 错误处理\n\n```typescript\nexport const MyPlugin: CPlugin = (ctx) => {\n  return {\n    name: 'MyPlugin',\n\n    async init(ctx) {\n      try {\n        // 可能失败的初始化逻辑\n        await initializePlugin();\n\n        ctx.pluginReadyOk();\n      } catch (error) {\n        console.error('MyPlugin 初始化失败', error);\n\n        // 发送错误事件\n        ctx.globalEmitter.emit('plugin:error', {\n          plugin: 'MyPlugin',\n          error,\n        });\n\n        // 仍然标记为准备好，避免阻塞其他插件\n        ctx.pluginReadyOk();\n      }\n    },\n\n    async destroy(ctx) {\n      try {\n        await cleanupPlugin();\n      } catch (error) {\n        console.error('MyPlugin 清理失败', error);\n      }\n    },\n\n    export: (ctx) => ({\n      async doSomething() {\n        try {\n          // 业务逻辑\n          return { success: true };\n        } catch (error) {\n          console.error('操作失败', error);\n          return { success: false, error };\n        }\n      },\n    }),\n\n    meta: {\n      engine: { version: '1.0.0' },\n    },\n  };\n};\n```\n\n## 总结\n\n通过本指南，你已经学会了：\n\n1. **开发自定义插件**\n\n   - 创建插件的基本结构\n   - 实现插件的生命周期\n   - 暴露插件 API\n   - 处理事件和状态\n\n2. **替换内置插件**\n\n   - 完全替换内置插件\n   - 使用 `customPlugin` 定制内置插件\n   - 保持 API 兼容性\n\n3. **实现高度定制引擎**\n\n   - 构建轻量级编辑器\n   - 针对特定场景定制\n   - 企业级功能集成\n\n4. **最佳实践**\n   - 命名规范\n   - 生命周期管理\n   - 类型安全\n   - 插件间通信\n   - 错误处理\n\n现在你可以根据自己的需求，构建完全定制化的 Chameleon Engine 了！\n"
  },
  {
    "path": "packages/docs-app/src/content/docs/reference/Plugin/custom-setter.mdx",
    "content": "---\nsidebar:\n  order: 2\ntitle: 自定义 setter\n---\n\n### Setter 定义\n\n```tsx\nimport { ConfigProvider, Switch } from 'antd';\nimport { CSetter, CSetterProps } from '@chamn/engine';\n\ntype CustomSetterProps = {\n  a?: string;\n};\n\nexport const CustomSetter: CSetter<CustomSetterProps> = ({\n  onValueChange,\n  setterContext,\n  ...props\n}: CSetterProps<CustomSetterProps>) => {\n  return (\n    <ConfigProvider\n      theme={{\n        token: {\n          borderRadius: 4,\n        },\n      }}\n    >\n      <Switch\n        {...props}\n        checked={props.value as boolean}\n        onChange={(open, e) => {\n          // 将数据同步给 engine\n          onValueChange?.(open);\n        }}\n      />\n    </ConfigProvider>\n  );\n};\n\nCustomSetter.setterName = '自定义设置器';\n```\n\n### 注册\n\n```tsx\nreturn (\n  <Engine\n    plugins={DEFAULT_PLUGIN_LIST}\n    schema={page as any}\n    // 传入组件物料\n    material={[...InnerComponentMeta, ...Material]}\n    // 组件物料对应的 js 运行库，只能使用 umd 模式的 js\n    assetPackagesList={assetPackagesList}\n    beforePluginRun={({ pluginManager }) => {\n      pluginManager.customPlugin('RightPanel', (pluginInstance) => {\n        pluginInstance.ctx.config.customPropertySetterMap = {\n          CustomSetter: CustomSetter,\n        };\n        return pluginInstance;\n      });\n    }}\n    onReady={onReady}\n  />\n);\n```\n\n### 使用\n\n在物料协议中即可使用该设置器:\n\n```tsx\nexport const ButtonMeta: CMaterialType = {\n  componentName: 'Button',\n  title: '按钮',\n  props: [\n    {\n      name: 'text',\n      title: '文本',\n      valueType: 'string',\n      setters: ['StringSetter', 'CustomSetter'],\n    },\n  ],\n  npm: {\n    package: __PACKAGE_NAME__ || '',\n    version: __PACKAGE_VERSION__,\n    destructuring: true,\n    exportName: 'Button',\n  },\n  snippets: [],\n};\n```\n"
  },
  {
    "path": "packages/docs-app/src/content/docs/reference/Plugin/innder-plugin-list.mdx",
    "content": "---\nsidebar:\n  order: 3\ntitle: 内置插件列表\n---\n\n# 内置插件列表\n\nChameleon Engine 提供了一套完整的内置插件系统。以下是所有可用的内置插件：\n\n## 核心插件\n\n### 默认插件列表 (DEFAULT_PLUGIN_LIST)\n\n以下插件包含在 `DEFAULT_PLUGIN_LIST` 中，是编辑器的核心功能：\n\n| 插件名               | 描述                           | 是否必需 | 使用文档                                                                     | 源码链接                                                                                                 |\n| :------------------- | :----------------------------- | :------- | :--------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------- |\n| **Designer**         | 设计器画布，负责页面渲染和编辑 | ✅ 必需  | [查看详情](../built-in-plugins-usage/#designerplugin---设计器画布)           | [Github](https://github.com/hlerenow/chameleon/tree/master/packages/engine/src/plugins/Designer)         |\n| **OutlineTree**      | 展示页面层级结构的树形视图     | 建议     | [查看详情](../built-in-plugins-usage/#outlinetreeplugin---页面结构树)        | [Github](https://github.com/hlerenow/chameleon/tree/master/packages/engine/src/plugins/OutlineTree)      |\n| **ComponentLibrary** | 组件库面板，显示可用组件       | 建议     | [查看详情](../built-in-plugins-usage/#componentlibplugin---组件库)           | [Github](https://github.com/hlerenow/chameleon/tree/master/packages/engine/src/plugins/ComponentLibrary) |\n| **GlobalStatePanel** | 全局数据管理面板               | 可选     | [查看详情](../built-in-plugins-usage/#globalstatepanelplugin---全局状态管理) | [Github](https://github.com/hlerenow/chameleon/tree/master/packages/engine/src/plugins/GlobalStatePanel) |\n| **RightPanel**       | 右侧面板容器                   | 建议     | [查看详情](../built-in-plugins-usage/#rightpanelplugin---右侧面板)           | [Github](https://github.com/hlerenow/chameleon/tree/master/packages/engine/src/plugins/RightPanel)       |\n| **History**          | 操作历史记录管理（撤销/重做）  | 建议     | [查看详情](../built-in-plugins-usage/#historyplugin---历史记录)              | [Github](https://github.com/hlerenow/chameleon/tree/master/packages/engine/src/plugins/History)          |\n| **Hotkeys**          | 快捷键支持                     | 建议     | [查看详情](../built-in-plugins-usage/#hotkeysplugin---快捷键)                | [Github](https://github.com/hlerenow/chameleon/tree/master/packages/engine/src/plugins/Hotkeys)          |\n\n## 右侧面板子插件\n\n以下插件在 RightPanel 中使用，提供具体的编辑功能：\n\n| 插件名                  | 描述             | 使用文档                                      | 源码链接                                                                                                    |\n| :---------------------- | :--------------- | :-------------------------------------------- | :---------------------------------------------------------------------------------------------------------- |\n| **PropertyPanel**       | 元素属性编辑面板 | [查看详情](../built-in-plugins-usage/#子面板) | [Github](https://github.com/hlerenow/chameleon/tree/master/packages/engine/src/plugins/PropertyPanel)       |\n| **VisualPanelPlus**     | 元素样式编辑面板 | [查看详情](../built-in-plugins-usage/#子面板) | [Github](https://github.com/hlerenow/chameleon/tree/master/packages/engine/src/plugins/VisualPanelPlus)     |\n| **ComponentStatePanel** | 组件状态管理面板 | [查看详情](../built-in-plugins-usage/#子面板) | [Github](https://github.com/hlerenow/chameleon/tree/master/packages/engine/src/plugins/ComponentStatePanel) |\n| **AdvancePanel**        | 组件高级设置面板 | [查看详情](../built-in-plugins-usage/#子面板) | [Github](https://github.com/hlerenow/chameleon/tree/master/packages/engine/src/plugins/AdvancePanel)        |\n| **EventPanel**          | 事件绑定面板     | [查看详情](../built-in-plugins-usage/#子面板) | [Github](https://github.com/hlerenow/chameleon/tree/master/packages/engine/src/plugins/EventPanel)          |\n\n## 独立插件\n\n以下插件可以独立使用，不包含在默认列表中：\n\n| 插件名                  | 描述                               | 使用文档                                                              | 源码链接                                                                                                    |\n| :---------------------- | :--------------------------------- | :-------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------- |\n| **DisplaySourceSchema** | 源码展示插件，查看页面 JSON Schema | [查看详情](../built-in-plugins-usage/#displaysourceschema---源码展示) | [Github](https://github.com/hlerenow/chameleon/tree/master/packages/engine/src/plugins/DisplaySourceSchema) |\n\n## 使用方式\n\n### 使用默认插件列表\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  return <Engine plugins={DEFAULT_PLUGIN_LIST} schema={pageSchema} material={materials} />;\n}\n```\n\n### 自定义插件列表\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\n\nconst { DesignerPlugin, ComponentLibPlugin, OutlineTreePlugin, RightPanelPlugin, HistoryPlugin } = plugins;\n\nfunction App() {\n  // 只使用必要的插件\n  const customPluginList = [\n    DesignerPlugin, // 必需\n    ComponentLibPlugin, // 组件库\n    OutlineTreePlugin, // 结构树\n    RightPanelPlugin, // 右侧面板\n    HistoryPlugin, // 历史记录\n  ];\n\n  return <Engine plugins={customPluginList} schema={pageSchema} material={materials} />;\n}\n```\n\n### 添加独立插件\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\nimport { Button } from 'antd';\nimport { DisplaySourceSchema } from '@chamn/engine';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  const onReady = (ctx) => {\n    const workbench = ctx.engine.getWorkbench();\n\n    // 使用 DisplaySourceSchema 插件\n    workbench?.replaceTopBarView(\n      <DisplaySourceSchema pageModel={ctx.engine.pageModel} engineCtx={ctx}>\n        <Button>查看源码</Button>\n      </DisplaySourceSchema>\n    );\n  };\n\n  return <Engine plugins={DEFAULT_PLUGIN_LIST} schema={pageSchema} material={materials} onReady={onReady} />;\n}\n```\n\n## 插件依赖关系\n\n- **DesignerPlugin** 是核心插件，其他插件都依赖它\n- **RightPanelPlugin** 是右侧面板的容器，PropertyPanel、VisualPanelPlus 等子面板依赖它\n- **HistoryPlugin** 依赖于页面模型的事件系统\n- **HotkeysPlugin** 依赖 DesignerPlugin 和 HistoryPlugin\n\n## 下一步\n\n- 查看 [内置插件使用指南](../built-in-plugins-usage/) 了解每个插件的详细用法\n- 查看 [插件开发文档](./plugin-develop/) 学习如何开发自定义插件\n- 查看 [自定义 Setter](./custom-setter/) 了解如何自定义属性设置器\n"
  },
  {
    "path": "packages/docs-app/src/content/docs/reference/Plugin/plugin-develop.mdx",
    "content": "---\nsidebar:\n  order: 1\ntitle: 插件开发指南\n---\n\n# 插件开发指南\n\nChameleon Engine 采用插件化架构，所有功能都可以通过插件扩展。本文档将详细介绍如何开发自定义插件。\n\n## 插件架构总览\n\n```mermaid\n%%{init: {'theme':'base', 'themeVariables': { 'fontSize':'14px', 'fontFamily':'Arial'}}}%%\ngraph TB\n    subgraph Engine[\"Chameleon Engine\"]\n        direction TB\n\n        subgraph CoreAPI[\"核心 API 层\"]\n            direction LR\n            CPage[\"CPage<br/><small>页面模型</small>\"]\n            Workbench[\"Workbench<br/><small>工作台</small>\"]\n            EventBus[\"Event Emitter<br/><small>事件总线</small>\"]\n            Assets[\"Assets Manager<br/><small>资源管理</small>\"]\n            I18n[\"I18n<br/><small>国际化</small>\"]\n        end\n\n        subgraph PluginSystem[\"插件系统架构\"]\n            direction TB\n\n            subgraph Manager[\"PluginManager - 插件管理器\"]\n                direction TB\n\n                subgraph Lifecycle[\"生命周期管理\"]\n                    direction LR\n                    Add[\"add()<br/><small>注册插件</small>\"]\n                    Init[\"init()<br/><small>初始化</small>\"]\n                    Ready[\"onPluginReadyOk()<br/><small>就绪通知</small>\"]\n                    Remove[\"remove()<br/><small>卸载插件</small>\"]\n                end\n\n                subgraph Extension[\"扩展能力\"]\n                    direction LR\n                    Get[\"get()<br/><small>获取插件</small>\"]\n                    Custom[\"customPlugin()<br/><small>定制插件</small>\"]\n                    Export[\"export<br/><small>API 导出</small>\"]\n                end\n\n                subgraph Context[\"插件上下文 (CPluginCtx)\"]\n                    direction LR\n                    CtxEmitter[\"事件系统\"]\n                    CtxConfig[\"配置对象\"]\n                    CtxAPI[\"核心 API\"]\n                end\n            end\n\n            subgraph PluginLayer[\"插件实例层 - 可扩展/可替换\"]\n                direction TB\n\n                subgraph BuiltIn[\"内置插件\"]\n                    Designer[\"Designer\"]\n                    History[\"History\"]\n                    RightPanel[\"RightPanel\"]\n                    Others[\"...\"]\n                end\n\n                subgraph Custom[\"自定义插件\"]\n                    CustomA[\"Custom Plugin A\"]\n                    CustomB[\"Custom Plugin B\"]\n                    CustomC[\"...\"]\n                end\n            end\n        end\n    end\n\n    style Engine fill:#f8f9fa,stroke:#212529,stroke-width:3px\n    style CoreAPI fill:#e3f2fd,stroke:#1976d2,stroke-width:2px\n    style PluginSystem fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px\n    style Manager fill:#ede7f6,stroke:#5e35b1,stroke-width:2px\n    style Lifecycle fill:#fff3e0,stroke:#ef6c00,stroke-width:1.5px\n    style Extension fill:#e0f2f1,stroke:#00796b,stroke-width:1.5px\n    style Context fill:#fce4ec,stroke:#c2185b,stroke-width:1.5px\n    style PluginLayer fill:#e8eaf6,stroke:#3f51b5,stroke-width:1.5px\n    style BuiltIn fill:#e8f5e9,stroke:#2e7d32,stroke-width:1px\n    style Custom fill:#fff9c4,stroke:#f57f17,stroke-width:1px\n\n    style CPage fill:#bbdefb,stroke:#1976d2\n    style Workbench fill:#bbdefb,stroke:#1976d2\n    style EventBus fill:#bbdefb,stroke:#1976d2\n    style Assets fill:#bbdefb,stroke:#1976d2\n    style I18n fill:#bbdefb,stroke:#1976d2\n```\n\n### 架构说明\n\n上图展示了 Chameleon Engine 的插件系统架构，核心理念是**完全插件化**和**高度可扩展**。\n\n#### 1. 核心 API 层\n\n引擎提供了一组稳定的核心 API，供插件访问和操作：\n\n- **CPage**：页面模型，管理节点树结构，提供节点增删改查能力\n- **Workbench**：工作台容器，管理面板、视图的布局和交互\n- **Event Emitter**：事件总线，实现模块间的松耦合通信\n- **Assets Manager**：资源包管理器，统一管理组件资源\n- **I18n**：国际化支持，提供多语言能力\n\n#### 2. PluginManager - 插件管理器\n\nPluginManager 是插件系统的核心，负责插件的全生命周期管理和扩展能力：\n\n**生命周期管理**：\n\n- `add(plugin)`：注册插件到系统\n- `init(ctx)`：初始化插件，注入上下文\n- `onPluginReadyOk(name)`：等待插件就绪，支持异步依赖\n- `remove(name)`：卸载插件，清理资源\n\n**扩展能力**：\n\n- `get(name)`：获取插件实例，访问插件暴露的 API\n- `customPlugin(name, hook)`：定制插件配置，实现插件的可替换性\n- `export`：插件通过 export 暴露 API，供其他插件或外部调用\n\n**插件上下文 (CPluginCtx)**：\n\n- 每个插件都拥有独立的上下文对象\n- 包含全局事件系统和插件私有事件系统\n- 可访问所有核心 API\n- 支持自定义配置对象\n\n#### 3. 插件实例层 - 可扩展/可替换\n\n**内置插件**：\n\n- 提供开箱即用的核心功能\n- 可被自定义插件完全替换\n- 包括：Designer（设计器）、History（历史记录）、RightPanel（属性面板）等\n\n**自定义插件**：\n\n- 完全自由的插件开发\n- 可替换任何内置插件\n- 可扩展新功能\n- 与内置插件享有相同的 API 访问权限\n\n### 插件系统的核心特性\n\n#### 可扩展性\n\n- **无限扩展**：通过 `pluginManager.add()` 可以添加任意数量的插件\n- **平等访问**：自定义插件与内置插件拥有相同的核心 API 访问权限\n- **独立上下文**：每个插件拥有独立的配置和事件系统\n\n#### 可替换性\n\n- **插件定制**：通过 `customPlugin()` 可以在插件初始化前修改其配置\n- **完全替换**：移除内置插件，添加自定义插件即可实现功能替换\n- **热插拔**：支持运行时动态添加和移除插件\n\n#### 松耦合\n\n- **事件驱动**：插件间通过事件系统通信，避免直接依赖\n- **API 导出**：插件通过 `export` 暴露标准 API，降低耦合度\n- **上下文隔离**：每个插件的状态和配置相互独立\n\n### 插件生命周期\n\n```mermaid\n%%{init: {'theme':'base', 'themeVariables': { 'fontSize':'14px'}}}%%\nflowchart LR\n    Start([pluginManager.add]) --> CreateCtx\n\n    subgraph Registration[\"注册阶段\"]\n        direction LR\n        CreateCtx[创建上下文<br/>CPluginCtx] --> CustomHook[自定义钩子<br/>customPlugin]\n        CustomHook --> Init[plugin.init<br/>初始化]\n        Init --> Ready[pluginReadyOk<br/>就绪通知]\n    end\n\n    Ready --> Running\n\n    subgraph RunningPhase[\"运行阶段\"]\n        direction TB\n        Running[提供服务]\n        Running -.get.-> Export[暴露 API]\n        Running -.emit.-> Events[处理事件]\n        Running -.call.-> Interact[插件交互]\n    end\n\n    Running --> Remove\n\n    subgraph CleanupPhase[\"清理阶段\"]\n        direction LR\n        Remove[pluginManager.remove] --> Destroy[plugin.destroy<br/>清理]\n        Destroy --> Release[释放资源]\n    end\n\n    Release --> End([已卸载])\n\n    style Start fill:#c8e6c9,stroke:#388e3c,stroke-width:2px\n    style End fill:#ffcdd2,stroke:#c62828,stroke-width:2px\n    style Registration fill:#e3f2fd,stroke:#1976d2,stroke-width:2px\n    style RunningPhase fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px\n    style CleanupPhase fill:#fff3e0,stroke:#ef6c00,stroke-width:2px\n\n    style CreateCtx fill:#bbdefb,stroke:#1976d2\n    style CustomHook fill:#bbdefb,stroke:#1976d2\n    style Init fill:#bbdefb,stroke:#1976d2\n    style Ready fill:#bbdefb,stroke:#1976d2\n    style Running fill:#ce93d8,stroke:#7b1fa2\n    style Remove fill:#ffcc80,stroke:#f57c00\n    style Destroy fill:#ffcc80,stroke:#f57c00\n    style Release fill:#ffcc80,stroke:#f57c00\n```\n\n**生命周期详解**：\n\n1. **注册阶段**（PluginRegistration）\n\n   - `pluginManager.add(plugin)`：注册插件到系统\n   - 创建插件上下文（CPluginCtx）：包含核心 API、事件系统、配置对象\n   - 执行自定义钩子（customPluginHooks）：可在初始化前修改插件配置\n   - 调用 `plugin.init(ctx)`：执行插件初始化逻辑\n   - 触发 `pluginReadyOk()`：通知系统插件已准备就绪\n\n2. **运行阶段**（Running）\n\n   - 插件可被其他插件通过 `pluginManager.get(name)` 获取\n   - 通过 `export` 暴露 API 供外部调用\n   - 监听和处理事件\n   - 与其他插件进行交互\n\n3. **清理阶段**（Cleanup）\n   - `pluginManager.remove(name)`：触发卸载流程\n   - 调用 `plugin.destroy(ctx)`：执行清理逻辑\n   - 注销事件监听，避免内存泄漏\n   - 清理 UI 组件和释放资源\n\n## 插件定义\n\n插件可以是一个对象，也可以是一个返回插件定义的函数：\n\n```typescript\nexport type PluginObj<C, E = any> = {\n  /** 插件名称（必需，必须唯一） */\n  name: string;\n  /** 插件静态名称（可选，用于类型安全访问） */\n  PLUGIN_NAME?: string;\n  /** 初始化方法（必需） */\n  init: (ctx: CPluginCtx<C>) => Promise<void>;\n  /** 销毁方法（必需） */\n  destroy: (ctx: CPluginCtx<C>) => Promise<void>;\n  /** 重载方法（可选） */\n  reload?: (ctx: CPluginCtx<C>) => Promise<void>;\n  /** 导出 API（必需） */\n  export: (ctx: CPluginCtx<C>) => E;\n  /** 元数据（必需） */\n  meta: {\n    engine: {\n      version: string;\n    };\n  };\n};\n\nexport type CPlugin<C = any, E = any> = PluginObj<C, E> | ((ctx: CPluginCtx<C>) => PluginObj<C, E>);\n```\n\n### 插件上下文 (CPluginCtx)\n\n插件上下文提供了访问引擎核心功能的能力：\n\n```typescript\ntype CPluginCtx<C = any> = {\n  name?: string; // 插件名称\n  globalEmitter: Emitter<any>; // 全局事件发射器\n  emitter: Emitter<any>; // 插件私有事件发射器\n  config: C; // 插件配置对象\n  getWorkbench: () => Workbench; // 获取工作台实例\n  pluginManager: PluginManager; // 插件管理器\n  pageModel: CPage; // 页面模型\n  i18n: CustomI18n; // 国际化对象\n  assetsPackageListManager: AssetsPackageListManager; // 资源包管理器\n  engine: Engine; // 引擎实例\n  pluginReadyOk: () => void; // 通知插件已准备好\n};\n```\n\n## 快速开始\n\n### 创建一个简单的插件\n\n```typescript\nimport { CPlugin } from '@chamn/engine';\n\nconst PLUGIN_NAME = 'HelloPlugin' as const;\n\nexport const HelloPlugin: CPlugin = (ctx) => {\n  return {\n    name: PLUGIN_NAME,\n    PLUGIN_NAME,\n\n    async init(ctx) {\n      console.log('Hello Plugin 初始化！');\n\n      // 添加左侧面板\n      const workbench = ctx.getWorkbench();\n      workbench.addLeftPanel({\n        name: PLUGIN_NAME,\n        title: 'Hello',\n        icon: <span>👋</span>,\n        view: <div>Hello World!</div>,\n      });\n\n      // 通知插件已准备好（重要！）\n      ctx.pluginReadyOk();\n    },\n\n    async destroy(ctx) {\n      console.log('Hello Plugin 销毁');\n    },\n\n    export: (ctx) => {\n      return {\n        sayHello() {\n          console.log('Hello from plugin!');\n        },\n      };\n    },\n\n    meta: {\n      engine: {\n        version: '1.0.0',\n      },\n    },\n  };\n};\n\n// 添加静态属性（推荐）\nHelloPlugin.PLUGIN_NAME = PLUGIN_NAME;\n```\n\n### 使用插件\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\nimport { HelloPlugin } from './HelloPlugin';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nfunction App() {\n  const onReady = async (ctx) => {\n    // 获取插件实例\n    const helloPlugin = await ctx.pluginManager.get('HelloPlugin');\n\n    // 调用插件方法\n    helloPlugin?.export.sayHello();\n  };\n\n  return (\n    <Engine\n      plugins={[...DEFAULT_PLUGIN_LIST, HelloPlugin]}\n      schema={pageSchema}\n      material={materials}\n      onReady={onReady}\n    />\n  );\n}\n```\n\n## 实战示例\n\n### 示例 1: 历史记录插件\n\n完整的历史记录插件实现，支持撤销/重做功能：\n\n```typescript\nimport { waitReactUpdate } from '@/utils';\nimport { CPageDataType } from '@chamn/model';\nimport { cloneDeep, debounce } from 'lodash-es';\nimport { CPlugin, CPluginCtx } from '@chamn/engine';\n\nconst PLUGIN_NAME = 'History' as const;\n\nexport type HistoryExport = {\n  addStep: () => void;\n  reset: () => Promise<void>;\n  preStep: () => void;\n  nextStep: () => void;\n  canGoPreStep: () => boolean;\n  canGoNextStep: () => boolean;\n};\n\nexport const HistoryPlugin: CPlugin<any, HistoryExport> = (ctx) => {\n  const CTX: CPluginCtx | null = ctx;\n  const dataStore = {\n    historyRecords: [] as CPageDataType[],\n    currentStepIndex: 0,\n  };\n\n  let originalPageRecord: CPageDataType | null = null;\n  const pageSchema = ctx.pageModel.export();\n  originalPageRecord = pageSchema;\n  dataStore.historyRecords.push(pageSchema);\n\n  const loadPage = async (page: CPageDataType) => {\n    if (!CTX) {\n      return;\n    }\n    CTX.pageModel.reloadPage(page);\n    await waitReactUpdate();\n  };\n\n  const resObj = {\n    addStep: () => {\n      const { currentStepIndex, historyRecords } = dataStore;\n      const newPage = ctx.pageModel.export();\n      if (currentStepIndex !== historyRecords.length - 1) {\n        dataStore.historyRecords = historyRecords.slice(0, currentStepIndex + 1);\n      }\n      dataStore.historyRecords.push(newPage);\n      dataStore.currentStepIndex = historyRecords.length - 1;\n    },\n    reset: async () => {\n      const ctx = CTX;\n      if (!ctx) {\n        console.warn('plugin ctx is null, pls check it');\n        return;\n      }\n      if (!originalPageRecord) {\n        return;\n      }\n      dataStore.historyRecords = [];\n      loadPage(originalPageRecord);\n    },\n    preStep: () => {\n      const { currentStepIndex, historyRecords } = dataStore;\n      if (!resObj.canGoPreStep()) {\n        return;\n      }\n      const newIndex = currentStepIndex - 1;\n      dataStore.currentStepIndex = newIndex;\n      const page = cloneDeep(historyRecords[newIndex]);\n      loadPage(page);\n    },\n    nextStep: () => {\n      if (!resObj.canGoNextStep()) {\n        return;\n      }\n      const { currentStepIndex, historyRecords } = dataStore;\n      const newIndex = currentStepIndex + 1;\n      dataStore.currentStepIndex = newIndex;\n      const page = cloneDeep(historyRecords[newIndex]);\n      return loadPage(page);\n    },\n    canGoPreStep: () => {\n      const { currentStepIndex } = dataStore;\n      if (currentStepIndex <= 0) {\n        return false;\n      }\n      return true;\n    },\n    canGoNextStep: () => {\n      const { currentStepIndex, historyRecords } = dataStore;\n      if (currentStepIndex >= historyRecords.length - 1) {\n        return false;\n      }\n      return true;\n    },\n  };\n\n  // 防抖处理，避免频繁记录\n  const debounceAddStep = debounce(() => {\n    resObj.addStep();\n  }, 500);\n\n  return {\n    name: PLUGIN_NAME,\n    PLUGIN_NAME,\n    async init(ctx) {\n      // 监听节点变化\n      ctx.pageModel.emitter.on('onNodeChange', () => {\n        debounceAddStep();\n      });\n\n      // 监听页面变化\n      ctx.pageModel.emitter.on('onPageChange', () => {\n        resObj.addStep();\n      });\n\n      // !!! 必须调用，通知 engine，插件初始化完成，可以被消费\n      ctx.pluginReadyOk();\n    },\n    async destroy(ctx) {\n      console.log('destroy', ctx);\n    },\n    // 提供给其他插件或者外部使用的方法\n    export: () => {\n      return resObj;\n    },\n    // 插件元信息，引擎的最低版本要求\n    meta: {\n      engine: {\n        version: '1.0.0',\n      },\n    },\n  };\n};\n\nHistoryPlugin.PLUGIN_NAME = PLUGIN_NAME;\n```\n\n### 示例 2: 自定义面板插件\n\n```typescript\nimport React, { useState } from 'react';\nimport { CPlugin } from '@chamn/engine';\nimport { Button, Input } from 'antd';\n\nconst PLUGIN_NAME = 'CustomPanel' as const;\n\n// 面板视图组件\nconst CustomPanelView: React.FC<{ pluginCtx: any }> = ({ pluginCtx }) => {\n  const [text, setText] = useState('');\n\n  return (\n    <div style={{ padding: '20px' }}>\n      <h3>自定义面板</h3>\n      <Input placeholder=\"输入文本\" value={text} onChange={(e) => setText(e.target.value)} />\n      <Button\n        style={{ marginTop: '10px' }}\n        onClick={() => {\n          console.log('输入的文本:', text);\n        }}\n      >\n        提交\n      </Button>\n    </div>\n  );\n};\n\nexport const CustomPanelPlugin: CPlugin = (ctx) => {\n  return {\n    name: PLUGIN_NAME,\n    PLUGIN_NAME,\n\n    async init(ctx) {\n      const workbench = ctx.getWorkbench();\n\n      // 添加左侧面板\n      workbench.addLeftPanel({\n        name: PLUGIN_NAME,\n        title: '自定义面板',\n        icon: <span>📝</span>,\n        view: <CustomPanelView pluginCtx={ctx} />,\n      });\n\n      ctx.pluginReadyOk();\n    },\n\n    async destroy(ctx) {\n      console.log('清理插件');\n    },\n\n    export: (ctx) => ({}),\n\n    meta: {\n      engine: { version: '1.0.0' },\n    },\n  };\n};\n\nCustomPanelPlugin.PLUGIN_NAME = PLUGIN_NAME;\n```\n\n### 示例 3: 自定义工具栏插件\n\n```typescript\nimport React from 'react';\nimport { CPlugin } from '@chamn/engine';\nimport { Button, Space } from 'antd';\n\nconst PLUGIN_NAME = 'CustomToolbar' as const;\n\nexport const CustomToolbarPlugin: CPlugin = (ctx) => {\n  return {\n    name: PLUGIN_NAME,\n    PLUGIN_NAME,\n\n    async init(ctx) {\n      const workbench = ctx.getWorkbench();\n      const engine = ctx.engine;\n\n      // 自定义顶部工具栏\n      workbench.replaceTopBarView(\n        <div\n          style={{\n            width: '100%',\n            height: '100%',\n            display: 'flex',\n            alignItems: 'center',\n            justifyContent: 'space-between',\n            padding: '0 20px',\n          }}\n        >\n          <div style={{ fontSize: '18px', fontWeight: 'bold' }}>我的编辑器</div>\n\n          <Space>\n            <Button onClick={() => engine.preview()}>预览</Button>\n            <Button onClick={() => engine.existPreview()}>编辑</Button>\n            <Button onClick={() => engine.refresh()}>刷新</Button>\n            <Button\n              type=\"primary\"\n              onClick={() => {\n                const pageData = engine.pageModel.export();\n                localStorage.setItem('page', JSON.stringify(pageData));\n              }}\n            >\n              保存\n            </Button>\n          </Space>\n        </div>\n      );\n\n      ctx.pluginReadyOk();\n    },\n\n    async destroy(ctx) {},\n    export: (ctx) => ({}),\n    meta: { engine: { version: '1.0.0' } },\n  };\n};\n\nCustomToolbarPlugin.PLUGIN_NAME = PLUGIN_NAME;\n```\n\n## 加载插件\n\n```tsx\nimport { Engine, EnginContext, plugins } from '@chamn/engine';\nimport { HistoryPlugin } from './HistoryPlugin';\nimport { CustomPanelPlugin } from './CustomPanelPlugin';\n\nconst { DEFAULT_PLUGIN_LIST } = plugins;\n\nexport const App = () => {\n  const onReady = useCallback(async (ctx: EnginContext) => {\n    // 等待插件准备完成\n    const designer = await ctx.pluginManager.onPluginReadyOk('Designer');\n    const history = await ctx.pluginManager.onPluginReadyOk('History');\n\n    // 获取工作台\n    const workbench = ctx.engine.getWorkbench();\n\n    // 使用插件导出的方法\n    history?.export.preStep();\n  }, []);\n\n  return (\n    <Engine\n      // 加载插件\n      plugins={[...DEFAULT_PLUGIN_LIST, HistoryPlugin, CustomPanelPlugin]}\n      schema={pageSchema}\n      material={materials}\n      onReady={onReady}\n    />\n  );\n};\n```\n\n## 插件配置\n\n通过 `beforePluginRun` 自定义插件配置：\n\n```tsx\nimport { Engine, plugins } from '@chamn/engine';\n\nconst beforePluginRun = ({ pluginManager }) => {\n  // 自定义插件配置\n  pluginManager.customPlugin('MyPlugin', (pluginInstance) => {\n    // 修改插件配置\n    pluginInstance.ctx.config.customOption = 'value';\n\n    return pluginInstance;\n  });\n};\n\n<Engine\n  beforePluginRun={beforePluginRun}\n  plugins={plugins}\n  // ...\n/>;\n```\n\n## 最佳实践\n\n### 1. 使用 TypeScript\n\n```typescript\nimport { CPlugin, CPluginCtx } from '@chamn/engine';\n\n// 定义配置类型\ntype MyPluginConfig = {\n  enabled: boolean;\n  option1: string;\n};\n\n// 定义导出类型\ntype MyPluginExport = {\n  doSomething: () => void;\n};\n\n// 使用泛型\nexport const MyPlugin: CPlugin<MyPluginConfig, MyPluginExport> = (ctx) => {\n  return {\n    name: 'MyPlugin',\n\n    async init(ctx) {\n      // 类型安全的配置访问\n      const enabled = ctx.config.enabled;\n\n      ctx.pluginReadyOk();\n    },\n\n    async destroy(ctx) {},\n\n    export: (ctx) => {\n      return {\n        doSomething() {\n          console.log('Do something');\n        },\n      };\n    },\n\n    meta: { engine: { version: '1.0.0' } },\n  };\n};\n```\n\n### 2. 命名规范\n\n- 插件命名统一采用 `PascalCase`，如 `MyPlugin`\n- 插件名称常量使用 `PLUGIN_NAME`\n- 导出的插件对象添加静态属性 `PLUGIN_NAME`\n\n### 3. 错误处理\n\n```typescript\nexport const SafePlugin: CPlugin = (ctx) => {\n  return {\n    name: 'SafePlugin',\n\n    async init(ctx) {\n      try {\n        // 可能出错的操作\n        const data = await fetchData();\n\n        // 验证依赖\n        const workbench = ctx.getWorkbench();\n        if (!workbench) {\n          throw new Error('工作台未初始化');\n        }\n\n        ctx.pluginReadyOk();\n      } catch (error) {\n        console.error('插件初始化失败:', error);\n        ctx.pluginReadyOk(); // 即使失败也通知\n      }\n    },\n\n    async destroy(ctx) {},\n    export: (ctx) => ({}),\n    meta: { engine: { version: '1.0.0' } },\n  };\n};\n```\n\n### 4. 内存管理\n\n```typescript\nexport const MemoryAwarePlugin: CPlugin = (ctx) => {\n  const disposables: (() => void)[] = [];\n\n  return {\n    name: 'MemoryAwarePlugin',\n\n    async init(ctx) {\n      // 监听事件\n      const handler = () => {\n        /* ... */\n      };\n      ctx.globalEmitter.on('event', handler);\n\n      // 记录清理函数\n      disposables.push(() => {\n        ctx.globalEmitter.off('event', handler);\n      });\n\n      ctx.pluginReadyOk();\n    },\n\n    async destroy(ctx) {\n      // 执行所有清理函数\n      disposables.forEach((dispose) => dispose());\n      disposables.length = 0;\n    },\n\n    export: (ctx) => ({}),\n    meta: { engine: { version: '1.0.0' } },\n  };\n};\n```\n\n## 常见问题\n\n### Q: 插件何时初始化？\n\nA: 插件在 Engine 组件 `componentDidMount` 时初始化，按照插件列表的顺序依次执行 `init` 方法。\n\n### Q: 如何确保插件初始化完成？\n\nA: 使用 `ctx.pluginReadyOk()` 通知插件管理器插件已准备好。其他插件可以通过 `ctx.pluginManager.onPluginReadyOk('PluginName')` 等待。\n\n### Q: 插件可以访问其他插件吗？\n\nA: 可以。使用 `ctx.pluginManager.get('PluginName')` 获取其他插件实例。\n\n## 参考资源\n\n- [内置插件列表](../innder-plugin-list/) - 查看所有内置插件\n- [内置插件使用指南](../built-in-plugins-usage/) - 学习内置插件的用法\n- [Engine API](../../engine/api/) - 了解 Engine 的 API\n- [示例项目](https://github.com/ByteCrazy/chameleon-demo) - 查看完整示例\n"
  },
  {
    "path": "packages/docs-app/src/env.d.ts",
    "content": "/* eslint-disable @typescript-eslint/triple-slash-reference */\n/// <reference path=\"../.astro/types.d.ts\" />\n/// <reference types=\"astro/client\" />\n"
  },
  {
    "path": "packages/docs-app/tsconfig.json",
    "content": "{\n  \"extends\": \"astro/tsconfigs/strict\",\n  \"include\": [\".astro/types.d.ts\", \"**/*\"],\n  \"exclude\": [\"dist\"]\n}\n"
  },
  {
    "path": "packages/engine/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n\nstats.html\n*storybook.log\n"
  },
  {
    "path": "packages/engine/.npmignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\nexample\n\nnode_modules\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n\nstats.html"
  },
  {
    "path": "packages/engine/.storybook/main.js",
    "content": "import { join, dirname } from 'path';\nimport { mergeConfig } from 'vite';\nimport commonConfig from '../build.common.config';\n\n/**\n * This function is used to resolve the absolute path of a package.\n * It is needed in projects that use Yarn PnP or are set up within a monorepo.\n */\nfunction getAbsolutePath(value) {\n  return dirname(require.resolve(join(value, 'package.json')));\n}\n\n/** @type { import('@storybook/react-vite').StorybookConfig } */\nconst config = {\n  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],\n  addons: [\n    getAbsolutePath('@storybook/addon-onboarding'),\n    getAbsolutePath('@storybook/addon-essentials'),\n    getAbsolutePath('@chromatic-com/storybook'),\n    getAbsolutePath('@storybook/addon-interactions'),\n  ],\n  framework: {\n    name: getAbsolutePath('@storybook/react-vite'),\n    options: {},\n  },\n  viteFinal: (config) => {\n    const newConfig = mergeConfig(config, commonConfig);\n    return newConfig;\n  },\n};\n\nexport default config;\n"
  },
  {
    "path": "packages/engine/.storybook/preview.js",
    "content": "/** @type { import('@storybook/react').Preview } */\nconst preview = {\n  parameters: {\n    controls: {\n      matchers: {\n        color: /(background|color)$/i,\n        date: /Date$/i,\n      },\n    },\n  },\n};\n\nexport default preview;\n"
  },
  {
    "path": "packages/engine/CHANGELOG.md",
    "content": "# Change Log\n\nAll notable changes to this project will be documented in this file.\nSee [Conventional Commits](https://conventionalcommits.org) for commit guidelines.\n\n## [0.10.4](https://github.com/ByteCrazy/chameleon/compare/v0.10.3...v0.10.4) (2026-01-17)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, engine-website-app:** 🐛 fixed pkg deps ([22b0000](https://github.com/ByteCrazy/chameleon/commit/22b0000a921ed3bd63cdf3366687184d51ee9dc5))\n\n## [0.10.3](https://github.com/ByteCrazy/chameleon/compare/v0.10.2...v0.10.3) (2026-01-17)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed  engine deps ([c1addf4](https://github.com/ByteCrazy/chameleon/commit/c1addf43214d016e466019a5aabb8960881b6418))\n\n## [0.10.2](https://github.com/ByteCrazy/chameleon/compare/v0.10.1...v0.10.2) (2026-01-17)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed string and text can not input chinese text ([3821d00](https://github.com/ByteCrazy/chameleon/commit/3821d00d5dffd7d70e995690df5358f5a596ae93))\n\n## [0.10.1](https://github.com/ByteCrazy/chameleon/compare/v0.10.0...v0.10.1) (2026-01-11)\n\n### ✨ Features | 新功能\n\n* **build-script, demo-page, engine, render:** 🎸 update build script dts & optimize modal root selector ([024d5ab](https://github.com/ByteCrazy/chameleon/commit/024d5ab26d19fc5f50172c328638a551fb99c129))\n\n## [0.10.0](https://github.com/hlerenow/chameleon/compare/v0.9.3...v0.10.0) (2025-12-25)\n\n### ✨ Features | 新功能\n\n* **engine:** 🎸 optimize emptyValueSetter auto trigger value update ([a2b0409](https://github.com/hlerenow/chameleon/commit/a2b04094035d421638d4657d31fd2b417906e9c6))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* 优化构建配置，修复 ES 模块外部化问题 ([866f0ae](https://github.com/hlerenow/chameleon/commit/866f0ae39472625137d73d143625d88d873f6c00))\n\n## [0.9.3](https://github.com/ByteCrazy/chameleon/compare/v0.9.2...v0.9.3) (2025-07-20)\n\n### ✨ Features | 新功能\n\n* **engine, render:** 🎸 add $EVENT_PARAMS and remove $Event $PARAMS_RUNTIME ([b8e58c1](https://github.com/ByteCrazy/chameleon/commit/b8e58c1e5e98d2e4b3d31e2ba52a26fda256eb47))\n\n## [0.9.2](https://github.com/ByteCrazy/chameleon/compare/v0.9.1...v0.9.2) (2025-07-13)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed call node method setting ([27ea497](https://github.com/ByteCrazy/chameleon/commit/27ea49747ca8dd24f862304ec552e805c9057e62))\n\n## [0.9.1](https://github.com/ByteCrazy/chameleon/compare/v0.9.0...v0.9.1) (2025-07-13)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, render:** 🐛 fixed CustomSchemaForm title ([178cddd](https://github.com/ByteCrazy/chameleon/commit/178cddd3e8d9147822e91fcd1ce7d8e08c70d924))\n\n## [0.9.0](https://github.com/ByteCrazy/chameleon/compare/v0.8.6...v0.9.0) (2025-07-13)\n\n### ✨ Features | 新功能\n\n* **engine, model:** 🎸 support EmptyValueSetter ([da1e36f](https://github.com/ByteCrazy/chameleon/commit/da1e36f9915282b3f7cb40672cf2a000bd4162e5))\n\n## [0.8.6](https://github.com/ByteCrazy/chameleon/compare/v0.8.5...v0.8.6) (2025-06-21)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **demo-page, engine, render:** 🐛 fixed action flow failed node not connect correct ([e235a2b](https://github.com/ByteCrazy/chameleon/commit/e235a2be2d1e1935dfc40b56936de763efc4fb67))\n\n## [0.8.5](https://github.com/ByteCrazy/chameleon/compare/v0.8.4...v0.8.5) (2025-04-13)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed custom seeter not register success ([19d739a](https://github.com/ByteCrazy/chameleon/commit/19d739a97000641846c02ec19555926fe88ce3b4))\n\n## [0.8.4](https://github.com/ByteCrazy/chameleon/compare/v0.8.3...v0.8.4) (2025-04-13)\n\n### ✨ Features | 新功能\n\n* **engine:** 🎸 optimize action flow layout ([9c970c9](https://github.com/ByteCrazy/chameleon/commit/9c970c9482274d4d5cf6beb76912a10784e62c79))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, render:** 🐛 CSS editor value update error ([209106a](https://github.com/ByteCrazy/chameleon/commit/209106a6a71819862626ee39c887d1835a8b5611))\n* **engine, render:** 🐛 fixed event not trigger when loop ([e3a10a8](https://github.com/ByteCrazy/chameleon/commit/e3a10a8ab77a3c5321f47440c7bdc2ad52e82ee2))\n\n## [0.8.3](https://github.com/ByteCrazy/chameleon/compare/v0.8.2...v0.8.3) (2025-04-12)\n\n**Note:** Version bump only for package @chamn/engine\n\n## [0.8.2](https://github.com/ByteCrazy/chameleon/compare/v0.8.1...v0.8.2) (2025-04-11)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, render:** 🐛 fixed functionSeter and expressionSetter dts ([1e326e2](https://github.com/ByteCrazy/chameleon/commit/1e326e288f8d072497c01c1ccb0c06bed0521949))\n\n## [0.8.1](https://github.com/ByteCrazy/chameleon/compare/v0.8.0...v0.8.1) (2025-04-10)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed globalStateDts ([03c0b71](https://github.com/ByteCrazy/chameleon/commit/03c0b714343a7cf76710b164a43008af430b1d7c))\n\n## [0.8.0](https://github.com/ByteCrazy/chameleon/compare/v0.7.0...v0.8.0) (2025-04-10)\n\n### ✨ Features | 新功能\n\n* **demo-page, engine, render:** 🎸 update run time var for function ([d8fb90a](https://github.com/ByteCrazy/chameleon/commit/d8fb90ac72a233d7fdb5fcb36e0b7963f83c5acf))\n* **engine, model, render:** 🎸 fixed cycle update when update value in mount ([9b30922](https://github.com/ByteCrazy/chameleon/commit/9b30922ffac4a5b4fb1fe123c4604cc0fff63911))\n* **engine, render:** 🎸 optimize expression setter ([07c11e9](https://github.com/ByteCrazy/chameleon/commit/07c11e9bdf011f70763623d6a2f185a3c3830e5e))\n* **engine, render:** 🎸 optimize globalState update ([4e7bb6a](https://github.com/ByteCrazy/chameleon/commit/4e7bb6ab68cd69fe6d429abe417587c3ac26eb18))\n* **engine:** 🎸 dynamic generate page dts ([2dc1a0b](https://github.com/ByteCrazy/chameleon/commit/2dc1a0bca0a53223b61b8c4ce216b26a70887c96))\n* **engine:** 🎸 optimize express setter ([37141f9](https://github.com/ByteCrazy/chameleon/commit/37141f97a98e53e0050e328c2de03341984b3ad5))\n\n## [0.7.0](https://github.com/ByteCrazy/chameleon/compare/v0.6.0...v0.7.0) (2025-04-06)\n\n### ✨ Features | 新功能\n\n* **engine, layout:** 🎸 optimize select node interactive time ([41d105c](https://github.com/ByteCrazy/chameleon/commit/41d105c2408458b38cda0c9fac601dd89fd744aa))\n* **engine:** 🎸 add css code editor ([7715f29](https://github.com/ByteCrazy/chameleon/commit/7715f29cd49d530e54941c85659ca3c671be91e3))\n* **engine:** 🎸 optimize style UI panel ([0d6f239](https://github.com/ByteCrazy/chameleon/commit/0d6f2394281bf4add5437b0c6e4c681f5d64d6e3))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, layout:** 🐛 optimize select active box ([b1a6041](https://github.com/ByteCrazy/chameleon/commit/b1a604140652f1c0871a463bc32020c39a07846c))\n\n## [0.6.0](https://github.com/ByteCrazy/chameleon/compare/v0.5.2...v0.6.0) (2025-03-30)\n\n### ✨ Features | 新功能\n\n* **engine:** 🎸 support config monacoEdito cdn url ([32d08bc](https://github.com/ByteCrazy/chameleon/commit/32d08bcaec7ebe8a187d0b2eae8bd5e4b64b3dc9))\n\n## [0.5.2](https://github.com/ByteCrazy/chameleon/compare/v0.5.1...v0.5.2) (2025-03-30)\n\n**Note:** Version bump only for package @chamn/engine\n\n## [0.5.1](https://github.com/ByteCrazy/chameleon/compare/v0.5.0...v0.5.1) (2025-03-30)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed search status reset ([994b36c](https://github.com/ByteCrazy/chameleon/commit/994b36ce744fd10002cf2996ae26702a959ef5b4))\n\n## [0.5.0](https://github.com/ByteCrazy/chameleon/compare/v0.4.0...v0.5.0) (2025-03-30)\n\n### ✨ Features | 新功能\n\n* **engine:** 🎸 componentLib add search and customSearchBar ([4801f40](https://github.com/ByteCrazy/chameleon/commit/4801f403da1af705d5dc5eef579e7dba6560b08f))\n\n## [0.4.0](https://github.com/ByteCrazy/chameleon/compare/v0.3.21...v0.4.0) (2025-03-29)\n\n**Note:** Version bump only for package @chamn/engine\n\n## [0.3.21](https://github.com/ByteCrazy/chameleon/compare/v0.3.20...v0.3.21) (2025-03-26)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, material:** 🐛 fixed RGLEL cycle update item ([f045818](https://github.com/ByteCrazy/chameleon/commit/f045818d6d1e1b4dbcf107d727976f6d92600b94))\n\n## [0.3.20](https://github.com/ByteCrazy/chameleon/compare/v0.3.19...v0.3.20) (2025-03-26)\n\n**Note:** Version bump only for package @chamn/engine\n\n## [0.3.19](https://github.com/ByteCrazy/chameleon/compare/v0.3.18...v0.3.19) (2025-03-26)\n\n**Note:** Version bump only for package @chamn/engine\n\n## [0.3.18](https://github.com/ByteCrazy/chameleon/compare/v0.3.17...v0.3.18) (2025-03-26)\n\n**Note:** Version bump only for package @chamn/engine\n\n## [0.3.17](https://github.com/ByteCrazy/chameleon/compare/v0.3.16...v0.3.17) (2025-03-25)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, material, model, render:** 🐛 fixed node update value material is undefined ([969174d](https://github.com/ByteCrazy/chameleon/commit/969174da968aec4a1ee1fec7ca44a5e459be56bc))\n\n## [0.3.16](https://github.com/ByteCrazy/chameleon/compare/v0.3.15...v0.3.16) (2025-03-24)\n\n### ✨ Features | 新功能\n\n* **engine:** 🎸 setter supprot get current nodemodel ([f59a136](https://github.com/ByteCrazy/chameleon/commit/f59a136cc134388c382827d731f683e9d9a298e5))\n\n## [0.3.15](https://github.com/ByteCrazy/chameleon/compare/v0.3.14...v0.3.15) (2025-03-23)\n\n**Note:** Version bump only for package @chamn/engine\n\n## [0.3.14](https://github.com/ByteCrazy/chameleon/compare/v0.3.13...v0.3.14) (2025-03-23)\n\n**Note:** Version bump only for package @chamn/engine\n\n## [0.3.13](https://github.com/ByteCrazy/chameleon/compare/v0.3.12...v0.3.13) (2025-03-23)\n\n**Note:** Version bump only for package @chamn/engine\n\n## [0.3.12](https://github.com/ByteCrazy/chameleon/compare/v0.3.11...v0.3.12) (2025-03-23)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, layout:** 🐛 fixed drag interactive and fixed accetpNode action ([c15d03c](https://github.com/ByteCrazy/chameleon/commit/c15d03cc0406533e5eab55e27ce5eb1223d91c72))\n\n## [0.3.11](https://github.com/ByteCrazy/chameleon/compare/v0.3.10...v0.3.11) (2025-03-22)\n\n**Note:** Version bump only for package @chamn/engine\n\n## [0.3.10](https://github.com/ByteCrazy/chameleon/compare/v0.3.9...v0.3.10) (2025-03-22)\n\n**Note:** Version bump only for package @chamn/engine\n\n## [0.3.9](https://github.com/ByteCrazy/chameleon/compare/v0.3.8...v0.3.9) (2025-03-22)\n\n### ✨ Features | 新功能\n\n* **engine, layout:** 🎸 optimize drag interactive ([f512f14](https://github.com/ByteCrazy/chameleon/commit/f512f14ecf3caaa148279543999a74d35ae44701))\n\n## [0.3.8](https://github.com/ByteCrazy/chameleon/compare/v0.3.7...v0.3.8) (2025-03-16)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed customAdvanceHook not rect on outline and hotkey ([c21bdb0](https://github.com/ByteCrazy/chameleon/commit/c21bdb0e275ebe4ae096d18b90bd406874c9de79))\n\n## [0.3.7](https://github.com/ByteCrazy/chameleon/compare/v0.3.6...v0.3.7) (2025-03-16)\n\n### ✨ Features | 新功能\n\n* **demo-page, engine, model, render:** 🎸 support inject eng inner env to runtime ([baa5c11](https://github.com/ByteCrazy/chameleon/commit/baa5c11d389019a7e4e4b8e000433a99038b4ae3))\n\n## [0.3.6](https://github.com/ByteCrazy/chameleon/compare/v0.3.5...v0.3.6) (2025-03-09)\n\n### ✨ Features | 新功能\n\n* **engine:** 🎸 add hiddenWidget for eng and optimize setter ([42b9846](https://github.com/ByteCrazy/chameleon/commit/42b984681a9efc7cdee7dc4287cd994fd37c592c))\n\n## [0.3.5](https://github.com/ByteCrazy/chameleon/compare/v0.3.4...v0.3.5) (2025-03-09)\n\n### ✨ Features | 新功能\n\n* **engine, layout:** 🎸 support controll preview mode ([97b83e5](https://github.com/ByteCrazy/chameleon/commit/97b83e58f0f22c76da51e5a3d22db2c82a2f70d2))\n* **engine:** 🎸 add workbench widget control config ([0fcab5b](https://github.com/ByteCrazy/chameleon/commit/0fcab5b5ad715762327c26d3f7eae4680604644b))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **demo-page, engine, render:** 🐛 fixed action flow only run first node ([c4bdb85](https://github.com/ByteCrazy/chameleon/commit/c4bdb85d0ca6ed09c6c66d15847de5bd14da556e))\n* **engine, model:** 🐛 update select setter name ([054fd48](https://github.com/ByteCrazy/chameleon/commit/054fd48f49ec1d1bdb79d7bac44acedb601d6fdf))\n* **engine:** 🐛 fixed last connect line not save ([2bacb15](https://github.com/ByteCrazy/chameleon/commit/2bacb1541cbc753df15a0216df9d6d6aac86cb1d))\n\n## [0.3.4](https://github.com/ByteCrazy/chameleon/compare/v0.3.3...v0.3.4) (2025-02-16)\n\n### ✨ Features | 新功能\n\n* **engine, layout, render:** 🎸 action node express support $response ([9bf1570](https://github.com/ByteCrazy/chameleon/commit/9bf1570c3b80404be78a5d3ca2c401464e7645d3))\n\n## [0.3.3](https://github.com/ByteCrazy/chameleon/compare/v0.3.2...v0.3.3) (2025-02-16)\n\n### ✨ Features | 新功能\n\n* **engine:** 🎸 JSON setter support reactive value ([0262067](https://github.com/ByteCrazy/chameleon/commit/0262067fc641b8dd3c812cd3cf1f114df7e33f9c))\n\n## [0.3.2](https://github.com/ByteCrazy/chameleon/compare/v0.3.1...v0.3.2) (2025-02-16)\n\n### ✨ Features | 新功能\n\n* **engine:** 🎸 optimize TCustomAPIInput type ([1b33f75](https://github.com/ByteCrazy/chameleon/commit/1b33f75a8b6e324b1f7695a62dfa1ae97d115cc7))\n\n## [0.3.1](https://github.com/ByteCrazy/chameleon/compare/v0.3.0...v0.3.1) (2025-02-16)\n\n### ✨ Features | 新功能\n\n* **engine, render:** 🎸 optimize CustomAPISelectInput from and event list label ([8dbf5af](https://github.com/ByteCrazy/chameleon/commit/8dbf5af08e50c60c5ff7adc5c7e644c4ca1a9c08))\n\n## [0.3.0](https://github.com/ByteCrazy/chameleon/compare/v0.2.4...v0.3.0) (2025-02-15)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, engine-website-app, material, model, render:** 🐛 fixed GRL hidden offsetY loop add size ([3df132b](https://github.com/ByteCrazy/chameleon/commit/3df132b6493026b42435d4868d77915b9f7316b2))\n* **engine:** 🐛 fixed ActionFlowSetter cicle deps ([b9bd177](https://github.com/ByteCrazy/chameleon/commit/b9bd177e38be054a8860d19516651d9ab813e27b))\n* **engine:** 🐛 fixed ActionFlowSetter cycle deps ([e69dfd7](https://github.com/ByteCrazy/chameleon/commit/e69dfd7df7129a88c54e238a05b96bb3270fbb2f))\n* **engine:** 🐛 fixed ActionFlowSetter update problem ([0034848](https://github.com/ByteCrazy/chameleon/commit/0034848e31c3b30b6782723af10e7f8e0152390a))\n* **engine:** 🐛 fixed outline drag excepetion after remove page ([82ff3fd](https://github.com/ByteCrazy/chameleon/commit/82ff3fd80ffce58b2b840ae219d29e61dd34a6e4))\n* **engine:** 🐛 resolve hot key confict with action flow setter ([e91898c](https://github.com/ByteCrazy/chameleon/commit/e91898c8deae98805ea2df7a1e8f3bc382bd3873))\n\n### ✨ Features | 新功能\n\n* **demo-page, engine, model, render:** 🎸 action node use link struct ([599ece4](https://github.com/ByteCrazy/chameleon/commit/599ece4927523f7c0e330cac722c9c9e6976ea3c))\n* **demo-page, engine, model, render:** 🎸 add event panel ([e8c5648](https://github.com/ByteCrazy/chameleon/commit/e8c5648017b40cbae42c576267d1e3b9d9660918))\n* **demo-page, engine, model, render:** 🎸 support TActionLogicItem prop ([e1b9d1e](https://github.com/ByteCrazy/chameleon/commit/e1b9d1e150ae810750249322ddf906b62eee9969))\n* **docs-app, engine, engine-website-app, layout, material, model, render:** 🎸 do ActionFlowSetter 50% ([6399466](https://github.com/ByteCrazy/chameleon/commit/6399466c7253436591e071df25828a357bb7089e))\n* **engine, engine-website-app, render:** 🎸 RequestAPINode support custom select ([5b72385](https://github.com/ByteCrazy/chameleon/commit/5b72385673bb646aff3ad2c5a9d8fffa142733dd))\n* **engine, model:** 🎸 add  call node method node ([1a5e79c](https://github.com/ByteCrazy/chameleon/commit/1a5e79c49964c589da167b64985327a3cbb03da8))\n* **engine, model:** 🎸 add run code node ([7ac6182](https://github.com/ByteCrazy/chameleon/commit/7ac61829586a19d4fcdc1765fa878d6a16008858))\n* **engine, model:** 🎸 request API 70% ([cee1228](https://github.com/ByteCrazy/chameleon/commit/cee1228c2a3265320cb32579f6f7b532b6962908))\n* **engine:** 🎸 add react-flow ([fafaaf9](https://github.com/ByteCrazy/chameleon/commit/fafaaf95c589c0c99ce0953f285bf53a0e423e21))\n* **engine:** 🎸 JumpLinkNode 100% ([ed0707e](https://github.com/ByteCrazy/chameleon/commit/ed0707e0232bf82bedc7b0db569c908d2001e6d7))\n* **engine:** 🎸 optimize ActionFlowSetter ([fa9af2f](https://github.com/ByteCrazy/chameleon/commit/fa9af2fba32f921411ed05e8d7e68293de5dde88))\n* **engine:** 🎸 optimize call node method node ([0b00029](https://github.com/ByteCrazy/chameleon/commit/0b00029627d6f90381b658ccc7f125e9b6116dcf))\n* **engine:** 🎸 optimize MoveableModal interactive ([2eccb94](https://github.com/ByteCrazy/chameleon/commit/2eccb942b447fa4e54bcd6f9207a2ed053e06526))\n* **engine:** 🎸 optimize RequestAPINode custom ([0440695](https://github.com/ByteCrazy/chameleon/commit/044069501608b1a8b62a08600c3c2d293e1ebe54))\n* **engine:** 🎸 optimize selectNodeByTree ([b6959d9](https://github.com/ByteCrazy/chameleon/commit/b6959d98714a16669b3896b677ebb78aa080d8f3))\n* **engine:** 🎸 optimize SetterSwitcher code struct ([d481fe6](https://github.com/ByteCrazy/chameleon/commit/d481fe68665f4fc7b1034e8b1ef9e2bfbe517012))\n* **engine:** 🎸 parseActionLogicToNodeList 30% ([f120154](https://github.com/ByteCrazy/chameleon/commit/f1201549a5537f170dbdce7efca7c85ce3add2ad))\n* **engine:** 🎸 support labelAlign config ([9590f86](https://github.com/ByteCrazy/chameleon/commit/9590f861fe8efebcd81ba38df3b159a528e066b6))\n* **engine:** 🎸 support render flow by schema data ([e04e76d](https://github.com/ByteCrazy/chameleon/commit/e04e76d23115c64823dc6fbf5d460f589df98b3d))\n\n## [0.2.4](https://github.com/ByteCrazy/chameleon/compare/v0.2.3...v0.2.4) (2024-12-08)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, material:** 🐛 fixed RGL init layout not correcnt and upgrade gridstack ([6b66b47](https://github.com/ByteCrazy/chameleon/commit/6b66b47b37e1fcd96602132bb44373a01d5de946))\n\n## [0.2.3](https://github.com/ByteCrazy/chameleon/compare/v0.2.2...v0.2.3) (2024-12-08)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed CVideo interactive ([4b1dabf](https://github.com/ByteCrazy/chameleon/commit/4b1dabfe89efbe2a3dfbb642e77ae10e74daaf0e))\n* **engine:** 🐛 fixed hotkey not work sometimes ([d029841](https://github.com/ByteCrazy/chameleon/commit/d029841679183d5cffcbb991cf64cfcfea9de34e))\n\n## [0.2.2](https://github.com/ByteCrazy/chameleon/compare/v0.2.1...v0.2.2) (2024-12-07)\n\n**Note:** Version bump only for package @chamn/engine\n\n## [0.2.1](https://github.com/ByteCrazy/chameleon/compare/v0.2.0...v0.2.1) (2024-12-07)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, engine-website-app, material, model, render:** 🎸 fixed RGL component and optimize project struct ([99f0679](https://github.com/ByteCrazy/chameleon/commit/99f0679d93a2fd696034ebed8f1abcd9d9e601d4))\n\n## [0.2.0](https://github.com/ByteCrazy/chameleon/compare/v0.1.1...v0.2.0) (2024-12-07)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed inner meta image drag problem ([f752d9e](https://github.com/ByteCrazy/chameleon/commit/f752d9ecf611cfc3f55576d0758905c6ac322a4e))\n\n### ✨ Features | 新功能\n\n* **build-script, demo-page, engine, engine-website-app, layout, material, model, render:** 🎸 upgrade vite to 6.0 ([bcac2b1](https://github.com/ByteCrazy/chameleon/commit/bcac2b15b83b41a7042ca37368c1b45302ad81d5))\n* **engine, layout, render:** 🎸 replace findDOMNode API ([af2531a](https://github.com/ByteCrazy/chameleon/commit/af2531a095124ac55d4f6dc6896d430f52f5da82))\n\n## [0.1.1](https://github.com/ByteCrazy/chameleon/compare/v0.1.0...v0.1.1) (2024-11-11)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed cycle dependencies ([cefa3f0](https://github.com/ByteCrazy/chameleon/commit/cefa3f0a4a9c72c81b5337bbdb6e0429ca247252))\n\n## [0.1.0](https://github.com/ByteCrazy/chameleon/compare/v0.0.46...v0.1.0) (2024-09-07)\n\n### 👷 Continuous Integration | CI 配置\n\n* **engine, engine-website-app, layout, model:** 🎡 update github ci ([259e9db](https://github.com/ByteCrazy/chameleon/commit/259e9db229576cd09c5aae1f28a2c228927011c7))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **demo-page, docs-app, engine, engine-website-app, layout, material:** 🐛 fixed material meta not correct ([55b755c](https://github.com/ByteCrazy/chameleon/commit/55b755ce0c833e594b46447b2d6608cf56f7a593))\n* **docs-website, engine, layout:** 🐛 fixed higligh toolbar pos ([0356109](https://github.com/ByteCrazy/chameleon/commit/0356109e8a11a5a85ab90d1610ef7ef8549db0a9))\n* **engine, engine-website-app, material:** 🐛 fixed ReactGridLayout edit mode lable not correct ([9801839](https://github.com/ByteCrazy/chameleon/commit/9801839d9ff6a6548b0e23f1e31850cd56e7fff0))\n* **engine, layout:** 🐛 fixed toolbox width not correct ([4dc159a](https://github.com/ByteCrazy/chameleon/commit/4dc159a1931a853ba4cdb664638f27ba71b1ecf2))\n* **engine:** 🐛 fixed advanceCustom hook logic ([5fb2619](https://github.com/ByteCrazy/chameleon/commit/5fb261962af141affef0e8e2cf1f0b62d16b0d45))\n* **engine:** 🐛 fixed BackgroundInput color input ([944517c](https://github.com/ByteCrazy/chameleon/commit/944517c9078b29c5076784a1df66752494125e9f))\n* **engine:** 🐛 fixed CSSUIPanel value not corrent ([1a4dc64](https://github.com/ByteCrazy/chameleon/commit/1a4dc645171253b261a3d2c9ebd3cf27ec692d34))\n* **engine:** 🐛 fixed github build ([a4ace81](https://github.com/ByteCrazy/chameleon/commit/a4ace818d20c657a255fd25d5771113e5191d55d))\n* **engine:** 🐛 fixed render url ([27736de](https://github.com/ByteCrazy/chameleon/commit/27736de41f239b4911535097c7334b73eea35224))\n* **engine:** 🐛 fixed shadow UI Input ([3fbb753](https://github.com/ByteCrazy/chameleon/commit/3fbb753bb26b03dd5a25bce0fc6621f2d8b778e1))\n\n### ✨ Features | 新功能\n\n* **build-script, demo-page, engine, layout, render:** 🎸 add lang switch ([29da65e](https://github.com/ByteCrazy/chameleon/commit/29da65ee1aa09550d910ddfbbcb9d8b4db983373))\n* **demo-page, engine, engine-website-app, material:** 🎸 add designerSizer and fixed GridItem bug ([4665aed](https://github.com/ByteCrazy/chameleon/commit/4665aed300d54c77be4abcb9a8cc0f1710ac2145))\n* **demo-page, engine, render:** 🎸 add getGlobalState and optimize node udpate ([4d3934f](https://github.com/ByteCrazy/chameleon/commit/4d3934fd8febe616a44e5d39da0e10964f3c800d))\n* **docs-app, engine, layout, material, model, render:** 🎸 optimize GL layout ([02274b4](https://github.com/ByteCrazy/chameleon/commit/02274b432903dc247c5613873f14715dd806decd))\n* **engine, layout:** 🎸 add set canvas width method ([a18369f](https://github.com/ByteCrazy/chameleon/commit/a18369f0d4bbb4bcf04ce2695be313d767d4bbe5))\n* **engine, material:** 🎸 add GRL meterial ([ae15c34](https://github.com/ByteCrazy/chameleon/commit/ae15c34a3f2736db61a933dc7d09166d9a619473))\n* **engine, model:** 🎸 add advanceOptions property ([d75f178](https://github.com/ByteCrazy/chameleon/commit/d75f178fa70d4c49b451dff9dddfb30d4c061196))\n* **engine, model:** 🎸 add hotKey plugin and fixed reloadPage event not trigge ([1cbf1e1](https://github.com/ByteCrazy/chameleon/commit/1cbf1e1a345d94c3b758b26cfbf1ecde69ce051c))\n* **engine:** 🎸 add canvas size change button ([19ebdf8](https://github.com/ByteCrazy/chameleon/commit/19ebdf8d635b6b412979db81dc5d4f1b43d793a9))\n* **engine:** 🎸 add hotAction ([c82bd21](https://github.com/ByteCrazy/chameleon/commit/c82bd21243aed9371a8576f572f600f1894f60bf))\n* **engine:** 🎸 add width input ([c38422d](https://github.com/ByteCrazy/chameleon/commit/c38422d0848c4d60e8cb5b5b480671515c1d6101))\n* **engine:** 🎸 optimize hotkeys methods ([bc83dba](https://github.com/ByteCrazy/chameleon/commit/bc83dba17fa4a23fa25548a284bbd69858bf15a0))\n\n### 📝 Documentation | 文档\n\n* **engine:** ✏️ add layout resource ([15f4325](https://github.com/ByteCrazy/chameleon/commit/15f4325e418d63bb3f046d58766e9bb1d2c96344))\n* **engine:** ✏️ update md resource ([de95460](https://github.com/ByteCrazy/chameleon/commit/de954609bbc4dc24dbf704d809ffb1de9a9116b2))\n\n## [0.0.46](https://github.com/ByteCrazy/chameleon/compare/v0.0.45...v0.0.46) (2024-06-30)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed registerCustomSetter method not valid ([9bdaa3b](https://github.com/ByteCrazy/chameleon/commit/9bdaa3b9feb1610a754b9aaa0925530f474dd3c3))\n\n## [0.0.45](https://github.com/ByteCrazy/chameleon/compare/v0.0.44...v0.0.45) (2024-06-30)\n\n**Note:** Version bump only for package @chamn/engine\n\n## [0.0.44](https://github.com/ByteCrazy/chameleon/compare/v0.0.43...v0.0.44) (2024-06-30)\n\n### ✨ Features | 新功能\n\n* **engine, model, render:** 🎸 optimize addMaterials and fixed inner mat schema ([aa77803](https://github.com/ByteCrazy/chameleon/commit/aa77803c75b203ee2a098fbee143f4a9581e15fa))\n\n## [0.0.43](https://github.com/ByteCrazy/chameleon/compare/v0.0.42...v0.0.43) (2024-06-29)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, layout:** 🎸 remove scss dts generate ([9ba7af3](https://github.com/ByteCrazy/chameleon/commit/9ba7af30601804f94e90a0408745fd48de38d8b5))\n\n## [0.0.42](https://github.com/ByteCrazy/chameleon/compare/v0.0.41...v0.0.42) (2024-06-01)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, layout:** 🐛 fixed drag and drop problem on chrome ([5c655c3](https://github.com/ByteCrazy/chameleon/commit/5c655c3a2f288bd90b70212f0f114adf3c23f8a1))\n\n## [0.0.41](https://github.com/ByteCrazy/chameleon/compare/v0.0.40...v0.0.41) (2024-05-26)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, layout, material, model, render:** 🎸 optimize build script ([0a60cc4](https://github.com/ByteCrazy/chameleon/commit/0a60cc4f5e5635d9e544b5141eaed5b8433901a5))\n\n## [0.0.40](https://github.com/ByteCrazy/chameleon/compare/v0.0.39...v0.0.40) (2024-04-28)\n\n**Note:** Version bump only for package @chamn/engine\n\n## [0.0.39](https://github.com/ByteCrazy/chameleon/compare/v0.0.38...v0.0.39) (2024-04-27)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **build-script, demo-page, engine, layout, material, model, render:** 🐛 fix flatObject collectVariable refeer pos ([1c4163a](https://github.com/ByteCrazy/chameleon/commit/1c4163aeeb89176a1ff5b50f76930889df7751fe))\n\n## [0.0.38](https://github.com/ByteCrazy/chameleon/compare/v0.0.37...v0.0.38) (2024-04-27)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, layout:** 🐛 fixed assets load iuess when relaod ([3eccc60](https://github.com/ByteCrazy/chameleon/commit/3eccc60bb7be5a469704a9c4f769161e525481f4))\n\n## [0.0.37](https://github.com/ByteCrazy/chameleon/compare/v0.0.36...v0.0.37) (2024-04-27)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed updateMaterials components map problem ([b868e88](https://github.com/ByteCrazy/chameleon/commit/b868e884a9b65021effdee8872a462fd8539c9c4))\n\n## [0.0.36](https://github.com/ByteCrazy/chameleon/compare/v0.0.35...v0.0.36) (2024-04-27)\n\n**Note:** Version bump only for package @chamn/engine\n\n## [0.0.35](https://github.com/ByteCrazy/chameleon/compare/v0.0.34...v0.0.35) (2024-04-27)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed onPluginReadyOk return value ([8550591](https://github.com/ByteCrazy/chameleon/commit/85505913af29459882caef5b83d17c2a8aca45b3))\n\n## [0.0.34](https://github.com/ByteCrazy/chameleon/compare/v0.0.33...v0.0.34) (2024-04-27)\n\n### ✨ Features | 新功能\n\n* **docs-website, engine, model:** 🎸 add update assetsPackageList logic ([2bce2f4](https://github.com/ByteCrazy/chameleon/commit/2bce2f4758b203695ec119d6e201e3186d7fab84))\n\n## [0.0.33](https://github.com/ByteCrazy/chameleon/compare/v0.0.32...v0.0.33) (2024-04-27)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fix css editor initial value not correct problem ([96c3e58](https://github.com/ByteCrazy/chameleon/commit/96c3e58755ed4fdfa3e200f3049b5024cb9c6719))\n\n### ✨ Features | 新功能\n\n* **engine, layout, render:** 🎸 optimize history step save ([dca964a](https://github.com/ByteCrazy/chameleon/commit/dca964a990a3cc0c5fd853d853a55a4957999644))\n* **engine, render:** 🎸 designer plugin add  updateComponent method ([f161177](https://github.com/ByteCrazy/chameleon/commit/f16117793402681a1eda8f8cba9c06e2ee5247ad))\n\n## [0.0.32](https://github.com/ByteCrazy/chameleon/compare/v0.0.31...v0.0.32) (2024-01-30)\n\n### ✨ Features | 新功能\n\n* **docs-website, engine, model, render:** 🎸 add updateMaterials、updatePage methods ([39ed2b6](https://github.com/ByteCrazy/chameleon/commit/39ed2b692a8a7379a79b96cb3fd8cdb76a4f01f2))\n\n## [0.0.31](https://github.com/ByteCrazy/chameleon/compare/v0.0.30...v0.0.31) (2023-10-17)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed arraySetter delete bug ([1b53538](https://github.com/ByteCrazy/chameleon/commit/1b53538a67ff35a6cfedffe661126f6912e78bbf))\n\n## [0.0.30](https://github.com/ByteCrazy/chameleon/compare/v0.0.29...v0.0.30) (2023-10-17)\n\n**Note:** Version bump only for package @chamn/engine\n\n## [0.0.29](https://github.com/ByteCrazy/chameleon/compare/v0.0.28...v0.0.29) (2023-10-17)\n\n### ✨ Features | 新功能\n\n* **build-script, engine:** 🎸 optimize css editor ([66c0541](https://github.com/ByteCrazy/chameleon/commit/66c0541eaee12b2eb0ceb4fb3a7748e1bf69768d))\n* **demo-page, engine, model:** 🎸 add ant color setter ([830de46](https://github.com/ByteCrazy/chameleon/commit/830de46babdf40a740248c37d8e6dc2043db1434))\n* **demo-page, engine, model:** 🎸 add ColorSetter ([e72b7f1](https://github.com/ByteCrazy/chameleon/commit/e72b7f15dd8bba498b776226d5debd07c8fd4234))\n* **demo-page, engine, model:** 🎸 add radio setter ([e6f9b1d](https://github.com/ByteCrazy/chameleon/commit/e6f9b1d9dba7cd3a9a82116bf5a1068c06a5a1d6))\n* **demo-page, engine:** 🎸 add cssSize setter ([74bf136](https://github.com/ByteCrazy/chameleon/commit/74bf136047000a67795e1360f2a923902366a513))\n* **demo-page, engine:** 🎸 add slider setter ([0fe1d29](https://github.com/ByteCrazy/chameleon/commit/0fe1d29257f214e69ca9fed6e5e4c11ccccb56bb))\n* **engine, model, render:** 🎸 cssEditor support cssText ([3dc74b4](https://github.com/ByteCrazy/chameleon/commit/3dc74b4895d414a718c00911a39d8491fafcfaee))\n* **engine, model:** 🎸 optimize style editor ([9c660ce](https://github.com/ByteCrazy/chameleon/commit/9c660ce694a32059450f19c824bcde9889049db8))\n* **engine, model:** 🎸 style varible、c s scss editor support styles text ([c988fcf](https://github.com/ByteCrazy/chameleon/commit/c988fcf17a2dbbc486e809a03c642726f02cf547))\n* **engine:** 🎸 add border input ([ae7d3c3](https://github.com/ByteCrazy/chameleon/commit/ae7d3c3e1c07bf68cfa74ef15ab8f499c9b7afc3))\n* **engine:** 🎸 add CSSSizeInput componrnt ([cd70933](https://github.com/ByteCrazy/chameleon/commit/cd70933fbb044058b65c402465a69139da4d8a4a))\n* **engine:** 🎸 add DimensionInput、FontInput、MarginInput、PaddingInput ([08aa4e5](https://github.com/ByteCrazy/chameleon/commit/08aa4e5a959ace882312b77a15ab5c973d2015b8))\n* **engine:** 🎸 CSSUI editor sync value ([549fa7e](https://github.com/ByteCrazy/chameleon/commit/549fa7ea676d9f31d10e3b78888ec2318df2cdc2))\n* **engine:** 🎸 optimize style setter ([eba569c](https://github.com/ByteCrazy/chameleon/commit/eba569cb5871f6cf8cce459c69d3d3beb7e0459a))\n* **engine:** 🎸 style UI finish ([a38fb84](https://github.com/ByteCrazy/chameleon/commit/a38fb846475631caa1a6253af7de11fd30027c48))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **build-script, engine:** 🐛 recocer build-script bin file ([5a2ca69](https://github.com/ByteCrazy/chameleon/commit/5a2ca69c4e5c48b3b0686478f6e5c40cd21c08ad))\n* **engine, render:** 🐛 fixed build error and add child cache for render ([9026474](https://github.com/ByteCrazy/chameleon/commit/90264746fe99b8cdd4d055f2d778e67aae38af87))\n* **engine:** 🐛 fixed MonacoEditor not trigger value change event ([0f6c3ee](https://github.com/ByteCrazy/chameleon/commit/0f6c3ee82a31b1a23388486f4cb12f9d7424ae66))\n\n## [0.0.28](https://github.com/ByteCrazy/chameleon/compare/v0.0.27...v0.0.28) (2023-08-29)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed click can't add component into canvas ([b8e8c2c](https://github.com/ByteCrazy/chameleon/commit/b8e8c2c703f20c2bc2836dc2e1f91b02b994e5ca))\n\n## [0.0.27](https://github.com/ByteCrazy/chameleon/compare/v0.0.26...v0.0.27) (2023-08-28)\n\n### ✨ Features | 新功能\n\n* **engine:** 🎸 plugin system support add addCustomView method ([ce6db8f](https://github.com/ByteCrazy/chameleon/commit/ce6db8fcf1b87e6e3e14ed7464e7615c0ecc852b))\n\n## [0.0.26](https://github.com/ByteCrazy/chameleon/compare/v0.0.25...v0.0.26) (2023-08-27)\n\n**Note:** Version bump only for package @chamn/engine\n\n## [0.0.25](https://github.com/ByteCrazy/chameleon/compare/v0.0.24...v0.0.25) (2023-08-27)\n\n**Note:** Version bump only for package @chamn/engine\n\n## [0.0.24](https://github.com/ByteCrazy/chameleon/compare/v0.0.23...v0.0.24) (2023-08-27)\n\n### ✨ Features | 新功能\n\n* **engine, model, render:** 🎸 fixed node maybe is null on rightPanel ([aee551e](https://github.com/ByteCrazy/chameleon/commit/aee551e77454f61900318003f9e3a3ffb1ef9427))\n\n## [0.0.23](https://github.com/ByteCrazy/chameleon/compare/v0.0.22...v0.0.23) (2023-08-26)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, render:** 🐛 fixed CSSPropertiesVariableBindEditor value not update ([9084c32](https://github.com/ByteCrazy/chameleon/commit/9084c32bb919c2e6191c29b871f9d1537d139050))\n\n### ✨ Features | 新功能\n\n* **engine, model:** 🎸 support custom rightPanel ([803d731](https://github.com/ByteCrazy/chameleon/commit/803d731819faa03430b2a17a154d3ff1e0daca28))\n* **engine, model:** 🎸 support inject custom setter ([a49ec4a](https://github.com/ByteCrazy/chameleon/commit/a49ec4ae4a98b42cf1cfb768990b64c981539881))\n\n## [0.0.22](https://github.com/ByteCrazy/chameleon/compare/v0.0.21...v0.0.22) (2023-08-24)\n\n### ✨ Features | 新功能\n\n* **demo-page, engine, layout, model:** 🎸 add disableEditorDragDom config ([1779f44](https://github.com/ByteCrazy/chameleon/commit/1779f44aff20370ea3336e72352032c6416f7dd3))\n\n## [0.0.21](https://github.com/ByteCrazy/chameleon/compare/v0.0.20...v0.0.21) (2023-08-19)\n\n### ✨ Features | 新功能\n\n* **engine, layout, model, render:** 🎸 optimize wrapComponent config ([d5916a7](https://github.com/ByteCrazy/chameleon/commit/d5916a7e6ee3cf79a32d4d23663b6873d86fe671))\n\n## [0.0.20](https://github.com/ByteCrazy/chameleon/compare/v0.0.19...v0.0.20) (2023-08-19)\n\n**Note:** Version bump only for package @chamn/engine\n\n## [0.0.19](https://github.com/ByteCrazy/chameleon/compare/v0.0.18...v0.0.19) (2023-08-17)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, render:** 🐛 fixed if assets is empty, loader will not success ([169d9ad](https://github.com/ByteCrazy/chameleon/commit/169d9ad9e5791353ad3a68dd0212c6ecfda64a29))\n\n### ✨ Features | 新功能\n\n* **engine:** 🎸 add HistoryPlugin type definition ([13a66fb](https://github.com/ByteCrazy/chameleon/commit/13a66fb5141b308af191394b6c377a0d387ea628))\n* **engine:** 🎸 use @monaco-editor/react replace monaco-editor ([848ee87](https://github.com/ByteCrazy/chameleon/commit/848ee87dedbccc71d3a7366320ad03122ed15d38))\n\n## [0.0.18](https://github.com/ByteCrazy/chameleon/compare/v0.0.17...v0.0.18) (2023-08-14)\n\n### ✨ Features | 新功能\n\n* **engine:** 🎸 add className and style for engine ([c2b7ef3](https://github.com/ByteCrazy/chameleon/commit/c2b7ef3c1c68708963dece239528889701eb0fd7))\n* **engine:** 🎸 add onMount for engine ([17c80ae](https://github.com/ByteCrazy/chameleon/commit/17c80aecc09f13da5f33d7a59b5849d73d99d12a))\n\n## [0.0.17](https://github.com/ByteCrazy/chameleon/compare/v0.0.16...v0.0.17) (2023-08-06)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed types error ([1b68777](https://github.com/ByteCrazy/chameleon/commit/1b68777eb58225d750f40b16a7d263acb385477a))\n\n## [0.0.16](https://github.com/ByteCrazy/chameleon/compare/v0.0.15...v0.0.16) (2023-08-06)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, layout:** 🐛 fixed some node not exits querySelector method ([2ddc186](https://github.com/ByteCrazy/chameleon/commit/2ddc1863e6eb207ae2e2b705a9fef4d23bece37c))\n\n### ✨ Features | 新功能\n\n* **build-script-example, demo-page, engine, layout, material, model, render:** 🎸 support direct import render from package on dev mode ([6bb38e2](https://github.com/ByteCrazy/chameleon/commit/6bb38e29678a8a4d08a7fcf9548fa548399259b0))\n* **demo-page, engine, layout, model:** 🎸 support  advanceCustom drag and drop hooks ([fb21e15](https://github.com/ByteCrazy/chameleon/commit/fb21e1501f6829e4f13a35ea7be942ff72b0be91))\n* **demo-page, engine, layout, model:** 🎸 support toolbarViewRender and ghostViewRedner ([43ced37](https://github.com/ByteCrazy/chameleon/commit/43ced371cfb7dbb58a96fc15e1bf635092307fa8))\n* **demo-page, engine, layout:** 🎸 support DropViewRender ([1c025a6](https://github.com/ByteCrazy/chameleon/commit/1c025a6d1f57f450b2de262a787375f3f4891008))\n* **demo-page, engine, model:** 🎸 support onDelete onCopy ([b6a76bb](https://github.com/ByteCrazy/chameleon/commit/b6a76bb244bbe2c050de17157bda97cb7ad21abc))\n* **engine, layout, model:** 🎸 add customDropViewRender prop ([cea7f1e](https://github.com/ByteCrazy/chameleon/commit/cea7f1e3c2b0a8a13aef7cdcb0191593414615aa))\n* **engine, layout, model:** 🎸 finish layout transform ([73ead35](https://github.com/ByteCrazy/chameleon/commit/73ead357b9aded2b5ee2b545da6f2b3677ce8393))\n* **engine:** 🎸 optimze plugin type definition ([da9c107](https://github.com/ByteCrazy/chameleon/commit/da9c107b20646714834642ad09b2cbdfcb6eb7cd))\n\n## [0.0.15](https://github.com/ByteCrazy/chameleon/compare/v0.0.14...v0.0.15) (2023-07-01)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, layout:** 🐛 fixed component will be add repeatedly after reload page ([1507549](https://github.com/ByteCrazy/chameleon/commit/1507549632c3c6e09d8555fd0286fcc72855c358))\n\n### ✨ Features | 新功能\n\n* **demo-page, docs-website, engine, model, render:** 🎸 optimize assets load ([3ee6d58](https://github.com/ByteCrazy/chameleon/commit/3ee6d58a88e5af3fc631723240783d5c97a273b0))\n* **demo-page, engine:** 🎸 setter support initialValue ([794d807](https://github.com/ByteCrazy/chameleon/commit/794d8072518cb3b1a897b04a2e96f5ca53fdb365))\n* **docs-website, engine, layout, material, render:** 🎸 support inject thridLib on $context ([ca2f074](https://github.com/ByteCrazy/chameleon/commit/ca2f07492b713c32e5a41d1f250f7888763cb665))\n* **engine, render:** 🎸 change internal api ([0c0c7c3](https://github.com/ByteCrazy/chameleon/commit/0c0c7c3fa8f86c08ab0cefb0396107ed8c52dcd5))\n\n## [0.0.14](https://github.com/ByteCrazy/chameleon/compare/v0.0.13...v0.0.14) (2023-05-10)\n\n### ✨ Features | 新功能\n\n* **engine:** 🎸 add node id in advance panel ([d457203](https://github.com/ByteCrazy/chameleon/commit/d45720316ab95f75f6f67a5dbf31bb0030ceb3f1))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, layout:** 🐛 fixed new component can not be add into canvas ([4057fa9](https://github.com/ByteCrazy/chameleon/commit/4057fa925c18bf06325de04b270be5f6c23351c8))\n* **engine, layout:** 🐛 fixed type problem ([751a392](https://github.com/ByteCrazy/chameleon/commit/751a39244228f74138acf7f567d23e888b8ff687))\n* **engine, render:** 🐛 fixed removeMediaCSS method run failed ([9127ec3](https://github.com/ByteCrazy/chameleon/commit/9127ec3d13f43a1c8763b2350bc0224d683da85c))\n\n## [0.0.13](https://github.com/ByteCrazy/chameleon/compare/v0.0.12...v0.0.13) (2023-05-03)\n\n### ✨ Features | 新功能\n\n* add-nearby-component ([85a301c](https://github.com/ByteCrazy/chameleon/commit/85a301c3d95e785c116ab38c0b3a452ceb33742f))\n* **build-script, build-script-example, demo-page, engine, layout, material, model, render:** 🎸 config code style config ([2325504](https://github.com/ByteCrazy/chameleon/commit/23255048fa4a3d4fc8f5cfa1312db5abd6cac70d))\n* **build-script, demo-page, engine, layout, model, render:** 🎸 update meterial schema ([14f4e5c](https://github.com/ByteCrazy/chameleon/commit/14f4e5caf0a4f6eca131e139d0c34ea21969dc0b))\n* **demo-page, docs-website, engine, layout, model, render:** 🎸 node support methods config ([e080b20](https://github.com/ByteCrazy/chameleon/commit/e080b20cea3cea27b95def196e078c1c225d3a83))\n* **demo-page, engine, layout, model:** 🎸 finish customEvent config ([84640e3](https://github.com/ByteCrazy/chameleon/commit/84640e3d06b79858590b9fe92ef4764fbe3f4f7b))\n* **demo-page, engine, layout:** 🎸 outline support cancel drag by node material ([cd428d3](https://github.com/ByteCrazy/chameleon/commit/cd428d362a8bfe7d5b6e58ed2b6290e19c00672b))\n* **demo-page, engine, layout:** 🎸 support cancel drag and custom node drag event ([825717e](https://github.com/ByteCrazy/chameleon/commit/825717e063846b72f95b0e41d8cba279ef5270c8))\n* **demo-page, engine:** 🎸 complete advanceCustom material feature ([98cf273](https://github.com/ByteCrazy/chameleon/commit/98cf273543f2fd534c254990d61052534a6649da))\n* **demo-page, engine:** 🎸 supprot onSelectNode ([f496a82](https://github.com/ByteCrazy/chameleon/commit/f496a82920cab7ae8704335b2ecd02a7887de3b2))\n* **engine, layout, model:** 🎸 support supportDispatchNativeEvent field ([489fd05](https://github.com/ByteCrazy/chameleon/commit/489fd05b588e85ed4cea81cc33ab27739a1bac59))\n* **engine, layout:** 🎸 optimize layout event system ([56f35ef](https://github.com/ByteCrazy/chameleon/commit/56f35ef83cc7e1658bfeba9081f997ff457cf09e))\n* **engine, layout:** 🎸 support isSupportDispatchNativeEvent filed ([4f830b4](https://github.com/ByteCrazy/chameleon/commit/4f830b42b6e84d74e9d462c3b3e090de89686e18))\n* **engine, render:** 🎸 html native tag component support container filed ([475fdbd](https://github.com/ByteCrazy/chameleon/commit/475fdbd5a34581ab7cafe67b5c9d3b94102b3a16))\n* remove self-executing function ([e224524](https://github.com/ByteCrazy/chameleon/commit/e224524198aa3f1339afd3bc9f1bacb72bc4d20e))\n* select element after in add node ([0c0d116](https://github.com/ByteCrazy/chameleon/commit/0c0d1164783420da306062292674fbccb0a8ffb0))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* adjust find node logic ([5f9f073](https://github.com/ByteCrazy/chameleon/commit/5f9f07320888b3a83a4a24b04cfbd2e9e82aa4b1))\n* **engine, layout, model:** 🐛 fixed type error ([d11e81f](https://github.com/ByteCrazy/chameleon/commit/d11e81f607ef6a41a2dfda0b4ac657a9a87e948c))\n* **engine:** 🐛 fixed insert node logic ([7fd4866](https://github.com/ByteCrazy/chameleon/commit/7fd4866d7daaece1c8c3b2a846b55da27a1e0936))\n\n## [0.0.12](https://github.com/ByteCrazy/chameleon/compare/v0.0.11...v0.0.12) (2023-04-23)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **build-script, build-script-example, engine, layout, material, model, render:** 🐛 fix can not find vite/client.d.ts problem ([8a53540](https://github.com/ByteCrazy/chameleon/commit/8a53540f75737707f69f93d2f701203dbf88084a))\n\n### ✨ Features | 新功能\n\n* **demo-page, docs-website, engine, model:** 🎸 update schema and change err to warn when schema check ([e753862](https://github.com/ByteCrazy/chameleon/commit/e7538626bad6681e4d488ac835fef61a603c0853))\n* **demo-page, engine, layout, model, render:** 🎸 node support config self isContainer property ([d94f1f9](https://github.com/ByteCrazy/chameleon/commit/d94f1f9203f2c273856fd4aa489a1ed9cbb0f0ec))\n\n## [0.0.11](https://github.com/ByteCrazy/chameleon/compare/v0.0.10...v0.0.11) (2023-04-21)\n\n### ✨ Features | 新功能\n\n*  add reload method of plugin ([3b2cb2a](https://github.com/ByteCrazy/chameleon/commit/3b2cb2aa3804e250ed8ce37027168282f63db879))\n* add replaceSubTopBarView method ([da32d9c](https://github.com/ByteCrazy/chameleon/commit/da32d9c67a6fff1a562886c027c1604a54e19b7e))\n* **build-script, build-script-example, demo-page, engine, layout, material, model, render:** 🎸 optimize build-script output file format ([eda44d2](https://github.com/ByteCrazy/chameleon/commit/eda44d255bd3c5af19013bdd61342e1f0817413a))\n* **build-script, docs-website, engine:** 🎸 add material develop doc ([0aacca0](https://github.com/ByteCrazy/chameleon/commit/0aacca0f726bc13606b814f8890b4e8ff8982142))\n* **engine, model:** 🎸 support custom setter ([5236c54](https://github.com/ByteCrazy/chameleon/commit/5236c543559aac517e833741ffa8046bc1c7f1a3))\n* RightPanel add replacePanel、removePanel methods ([ee6c2e1](https://github.com/ByteCrazy/chameleon/commit/ee6c2e1b45bf93075342cee060fe3038ec7b8e53))\n\n### 📝 Documentation | 文档\n\n* **docs-website, engine:** ✏️ add plugin、setter develop doc ([288edc4](https://github.com/ByteCrazy/chameleon/commit/288edc4999f732ff2f27b1c6e50900b9b0094700))\n\n## [0.0.10](https://github.com/ByteCrazy/chameleon/compare/v0.0.9...v0.0.10) (2023-04-17)\n\n**Note:** Version bump only for package @chamn/engine\n\n## [0.0.9](https://github.com/ByteCrazy/chameleon/compare/v0.0.8...v0.0.9) (2023-04-17)\n\n### ✨ Features | 新功能\n\n* **docs-website, engine, render:** 🎸 render add collectVariable flatObject util method ([ae25160](https://github.com/ByteCrazy/chameleon/commit/ae25160b568c267041b3827e836c95f60ecaee59))\n\n## [0.0.8](https://github.com/ByteCrazy/chameleon/compare/v0.0.7...v0.0.8) (2023-04-16)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, layout, model, render:** 🎸 optimize pack package ([78d99c5](https://github.com/ByteCrazy/chameleon/commit/78d99c507ad65ca39101f0239467737d2b445c87))\n* **docs-website, engine, layout:** 🎸 add defaultRender for engine, add some docs ([91f4257](https://github.com/ByteCrazy/chameleon/commit/91f4257e9f9b9391267e4b8d64e6ed811912381f))\n* **docs-website, engine:** 🎸 add docs ([0e6f605](https://github.com/ByteCrazy/chameleon/commit/0e6f6053fa3cd5e13b6a7a8258c27518c30470c5))\n\n### 📝 Documentation | 文档\n\n* **docs-website, engine:** ✏️ add doc ([2fb5cf5](https://github.com/ByteCrazy/chameleon/commit/2fb5cf5fd4dcb3859ecbdc8d42adf787d9e0255d))\n\n## [0.0.7](https://github.com/ByteCrazy/chameleon/compare/v0.0.6...v0.0.7) (2023-03-29)\n\n### ✨ Features | 新功能\n\n* **build-script, build-script-example, demo-page, engine, layout, material, model, render:** 🎸 external monaco eitor and remove style panel ([494d898](https://github.com/ByteCrazy/chameleon/commit/494d898fd75dabe84d867ff45e84ae63f9b59c5e))\n\n## [0.0.6](https://github.com/ByteCrazy/chameleon/compare/v0.0.5...v0.0.6) (2023-03-28)\n\n**Note:** Version bump only for package @chamn/engine\n\n## [0.0.5](https://github.com/ByteCrazy/chameleon/compare/v0.0.4...v0.0.5) (2023-03-28)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, layout, material, model, render:** 🎸 complete package info ([86b095d](https://github.com/ByteCrazy/chameleon/commit/86b095da6c955946300591b1775f59c1a141c065))\n\n## [0.0.4](https://github.com/ByteCrazy/chameleon/compare/v0.0.3...v0.0.4) (2023-03-27)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed engine d.ts path not correct problem ([fb20ca6](https://github.com/ByteCrazy/chameleon/commit/fb20ca604fff4e85b0264eff6383154ecfefd437))\n\n## [0.0.2](https://github.com/ByteCrazy/chameleon/compare/v0.0.1...v0.0.2) (2023-03-27)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fix engine not incluce dist folder ([fc2c39e](https://github.com/ByteCrazy/chameleon/commit/fc2c39e88aa2eeb82ba5e3989e5bdf244d112dfb))\n"
  },
  {
    "path": "packages/engine/LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "packages/engine/README.md",
    "content": "# chameleon\n\n> Chameleon is ever-changing\n\nA web visual programming engine, help to build a web page with 5 minutes. every people can use it easy.\n\n[Docs](https://hlerenow.github.io/chameleon/documents/) | [Demo](https://hlerenow.github.io/chameleon/)\n\n## Install\n\n```shell\nnpm i @chamn/engine @chamn/model @chamn/render\n```\n\n## Usage\n\n[Example Project](https://github.com/ByteCrazy/chameleon-demo)\n\n### ScreenSnapshot\n\n<img width=\"1776\" alt=\"image\" src=\"https://github.com/user-attachments/assets/7b06dc4c-80a3-455d-bc91-14a1cf1fb331\">\n\n\n![image](https://github.com/hlerenow/chameleon/blob/master/packages/engine/md-images/layout.gif)\n\n![image](https://user-images.githubusercontent.com/13299648/218920783-0d1cc275-a238-4d80-a717-dbbbf54b4713.png)\n\n![image](https://user-images.githubusercontent.com/13299648/218920845-0c4c549d-df56-4b0a-9b72-95dd0c0fcaf5.png)\n\n![image](https://user-images.githubusercontent.com/13299648/218921002-a25cfdd6-f27a-4b19-83fe-a6a264e4e4b5.png)\n\n![image](https://user-images.githubusercontent.com/13299648/218920640-9be3b1ba-1dc2-42c5-922f-f3c5f97a9d96.png)\n"
  },
  {
    "path": "packages/engine/__tests__/demo.test.ts",
    "content": "test('adds 1 + 2 to equal 3', () => {\n  expect(3).toBe(3);\n});\n"
  },
  {
    "path": "packages/engine/build.common.config.ts",
    "content": "/* eslint-disable @typescript-eslint/no-var-requires */\nimport path from 'path';\n\nexport default {\n  resolve: {\n    alias: [{ find: '@', replacement: path.resolve(__dirname, 'src') }],\n  },\n  css: {\n    preprocessorOptions: {\n      scss: {\n        additionalData: '@use \"@/assets/styles/mixin.scss\" as *;\\n',\n      },\n    },\n  },\n};\n"
  },
  {
    "path": "packages/engine/build.config.ts",
    "content": "import { viteStaticCopy } from 'vite-plugin-static-copy';\nimport pkg from './package.json';\nimport commonConfig from './build.common.config';\nimport { BuildScriptConfig } from '@chamn/build-script';\n\n// 开发模式默认读取 index.html 作为开发模式入口\n// entry 作为打包库入口\nconst plugins: any[] = [];\n\nif (process.env.BUILD_TYPE === 'APP') {\n  plugins.push(\n    viteStaticCopy({\n      targets: [\n        {\n          src: './node_modules/@chamn/render/dist/index.umd.js',\n          dest: './',\n          rename: 'render.umd.js',\n        },\n      ],\n    })\n  );\n}\n\nconst mainConfig: BuildScriptConfig = {\n  libMode: true,\n  entry: './src/index.tsx',\n  libName: 'ChamnEngine',\n  fileName: 'index',\n  global: {\n    react: 'React',\n    'react-dom': 'ReactDOM',\n  },\n  vite: {\n    ...commonConfig,\n    plugins,\n    define: {\n      global: 'globalThis',\n      'process.env': JSON.stringify('{}'),\n      __RUN_MODE__: JSON.stringify(process.env.BUILD_TYPE),\n      __PACKAGE_VERSION__: JSON.stringify(pkg.version),\n      __BUILD_VERSION__: JSON.stringify(Date.now()),\n    },\n  },\n};\n\nconst config = mainConfig;\nexport default config;\n"
  },
  {
    "path": "packages/engine/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <link rel=\"icon\" type=\"image/svg+xml\" href=\"/vite.svg\" />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n  <title>Chameleon Low-Code Engine</title>\n</head>\n\n<body>\n  <div id=\"root\"></div>\n  <script type=\"module\" src=\"/src/_dev_/index.tsx\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "packages/engine/jest.config.js",
    "content": "/* eslint-disable no-undef */\n/*\n * For a detailed explanation regarding each configuration property, visit:\n * https://jestjs.io/docs/configuration\n */\n\nmodule.exports = {\n  // All imported modules in your tests should be mocked automatically\n  // automock: false,\n\n  // Stop running tests after `n` failures\n  // bail: 0,\n\n  // The directory where Jest should store its cached dependency information\n  // cacheDirectory: \"/private/var/folders/l9/th_r5d_12wxdj16859_mctjw0000gn/T/jest_dx\",\n\n  // Automatically clear mock calls, instances, contexts and results before every test\n  // clearMocks: false,\n\n  // Indicates whether the coverage information should be collected while executing the test\n  // collectCoverage: false,\n\n  // An array of glob patterns indicating a set of files for which coverage information should be collected\n  // collectCoverageFrom: undefined,\n\n  // The directory where Jest should output its coverage files\n  // coverageDirectory: undefined,\n\n  // An array of regexp pattern strings used to skip coverage collection\n  // coveragePathIgnorePatterns: [\n  //   \"/node_modules/\"\n  // ],\n\n  // Indicates which provider should be used to instrument code for coverage\n  // coverageProvider: \"babel\",\n\n  // A list of reporter names that Jest uses when writing coverage reports\n  // coverageReporters: [\n  //   \"json\",\n  //   \"text\",\n  //   \"lcov\",\n  //   \"clover\"\n  // ],\n\n  // An object that configures minimum threshold enforcement for coverage results\n  // coverageThreshold: undefined,\n\n  // A path to a custom dependency extractor\n  // dependencyExtractor: undefined,\n\n  // Make calling deprecated APIs throw helpful error messages\n  // errorOnDeprecated: false,\n\n  // The default configuration for fake timers\n  // fakeTimers: {\n  //   \"enableGlobally\": false\n  // },\n\n  // Force coverage collection from ignored files using an array of glob patterns\n  // forceCoverageMatch: [],\n\n  // A path to a module which exports an async function that is triggered once before all test suites\n  // globalSetup: undefined,\n\n  // A path to a module which exports an async function that is triggered once after all test suites\n  // globalTeardown: undefined,\n\n  // A set of global variables that need to be available in all test environments\n  // globals: {},\n\n  // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.\n  // maxWorkers: \"50%\",\n\n  // An array of directory names to be searched recursively up from the requiring module's location\n  // moduleDirectories: [\n  //   \"node_modules\"\n  // ],\n\n  // An array of file extensions your modules use\n  // moduleFileExtensions: [\n  //   \"js\",\n  //   \"mjs\",\n  //   \"cjs\",\n  //   \"jsx\",\n  //   \"ts\",\n  //   \"tsx\",\n  //   \"json\",\n  //   \"node\"\n  // ],\n\n  // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module\n  // moduleNameMapper: {},\n\n  // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader\n  // modulePathIgnorePatterns: [],\n\n  // Activates notifications for test results\n  // notify: false,\n\n  // An enum that specifies notification mode. Requires { notify: true }\n  // notifyMode: \"failure-change\",\n\n  // A preset that is used as a base for Jest's configuration\n  preset: 'ts-jest',\n\n  // Run tests from one or more projects\n  // projects: undefined,\n\n  // Use this configuration option to add custom reporters to Jest\n  // reporters: undefined,\n\n  // Automatically reset mock state before every test\n  // resetMocks: false,\n\n  // Reset the module registry before running each individual test\n  // resetModules: false,\n\n  // A path to a custom resolver\n  // resolver: undefined,\n\n  // Automatically restore mock state and implementation before every test\n  // restoreMocks: false,\n\n  // The root directory that Jest should scan for tests and modules within\n  // rootDir: undefined,\n\n  // A list of paths to directories that Jest should use to search for files in\n  // roots: [\n  //   \"<rootDir>\"\n  // ],\n\n  // Allows you to use a custom runner instead of Jest's default test runner\n  // runner: \"jest-runner\",\n\n  // The paths to modules that run some code to configure or set up the testing environment before each test\n  // setupFiles: [],\n\n  // A list of paths to modules that run some code to configure or set up the testing framework before each test\n  // setupFilesAfterEnv: [],\n\n  // The number of seconds after which a test is considered as slow and reported as such in the results.\n  // slowTestThreshold: 5,\n\n  // A list of paths to snapshot serializer modules Jest should use for snapshot testing\n  // snapshotSerializers: [],\n\n  // The test environment that will be used for testing\n  testEnvironment: 'jsdom',\n\n  // Options that will be passed to the testEnvironment\n  // testEnvironmentOptions: {},\n\n  // Adds a location field to test results\n  // testLocationInResults: false,\n\n  // The glob patterns Jest uses to detect test files\n  // testMatch: [\n  //   \"**/__tests__/**/*.[jt]s?(x)\",\n  //   \"**/?(*.)+(spec|test).[tj]s?(x)\"\n  // ],\n\n  // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped\n  // testPathIgnorePatterns: [\n  //   \"/node_modules/\"\n  // ],\n\n  // The regexp pattern or array of patterns that Jest uses to detect test files\n  // testRegex: [],\n\n  // This option allows the use of a custom results processor\n  // testResultsProcessor: undefined,\n\n  // This option allows use of a custom test runner\n  // testRunner: \"jest-circus/runner\",\n\n  // A map from regular expressions to paths to transformers\n  // transform: undefined,\n\n  // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation\n  // transformIgnorePatterns: [\n  //   \"/node_modules/\",\n  //   \"\\\\.pnp\\\\.[^\\\\/]+$\"\n  // ],\n\n  // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them\n  // unmockedModulePathPatterns: undefined,\n\n  // Indicates whether each individual test should be reported during the run\n  // verbose: undefined,\n\n  // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode\n  // watchPathIgnorePatterns: [],\n\n  // Whether to use watchman for file crawling\n  // watchman: true,\n};\n"
  },
  {
    "path": "packages/engine/package.json",
    "content": "{\n  \"name\": \"@chamn/engine\",\n  \"version\": \"0.10.4\",\n  \"type\": \"module\",\n  \"files\": [\n    \"dist\"\n  ],\n  \"main\": \"dist/index.cjs.js\",\n  \"module\": \"dist/index.es.js\",\n  \"types\": \"dist/index.d.ts\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./dist/index.d.ts\",\n      \"module-sync\": \"./dist/index.es.js\",\n      \"import\": \"./dist/index.es.js\",\n      \"require\": \"./dist/index.cjs.js\"\n    },\n    \"./dist/*\": \"./dist/*\"\n  },\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"homepage\": \"https://github.com/hlerenow/chameleon\",\n  \"keywords\": [\n    \"engine\",\n    \"lowcode\",\n    \"react\",\n    \"web-editor\",\n    \"visual-editor\",\n    \"vite\"\n  ],\n  \"scripts\": {\n    \"start\": \"build-script\",\n    \"build\": \"cross-env BUILD_TYPE=PKG build-script --build\",\n    \"build:w\": \"cross-env BUILD_TYPE=PKG build-script --build --watch\",\n    \"build:analyze\": \"cross-env BUILD_TYPE=PKG  build-script --build --analyze\",\n    \"lint\": \"eslint ./src\",\n    \"prettier\": \"prettier --write ./src\",\n    \"test\": \"jest\",\n    \"storybook\": \"storybook dev -p 6006\",\n    \"build-storybook\": \"storybook build\"\n  },\n  \"peerDependencies\": {\n    \"react\": \">=16.9.0\",\n    \"react-dom\": \">=16.9.0\"\n  },\n  \"dependencies\": {\n    \"@ant-design/icons\": \"^5.4.0\",\n    \"@chamn/layout\": \"workspace:*\",\n    \"@chamn/model\": \"workspace:*\",\n    \"@chamn/render\": \"workspace:*\",\n    \"@dagrejs/dagre\": \"^1.1.4\",\n    \"@dnd-kit/core\": \"^6.0.7\",\n    \"@dnd-kit/modifiers\": \"^6.0.1\",\n    \"@dnd-kit/sortable\": \"^7.0.2\",\n    \"@dnd-kit/utilities\": \"^3.2.1\",\n    \"@monaco-editor/react\": \"^4.5.1\",\n    \"@xyflow/react\": \"^12.3.6\",\n    \"ahooks\": \"^3.7.4\",\n    \"antd\": \"^5.23.2\",\n    \"color\": \"^4.2.3\",\n    \"consola\": \"^3.2.3\",\n    \"css-tree\": \"^3.1.0\",\n    \"i18next\": \"^22.1.5\",\n    \"loadjs\": \"^4.3.0\",\n    \"lodash-es\": \"^4.17.21\",\n    \"lucide-react\": \"^0.487.0\",\n    \"mitt\": \"^3.0.0\",\n    \"monaco-editor\": \"^0.41.0\",\n    \"quicktype-core\": \"^23.0.171\",\n    \"react-color\": \"^2.19.3\",\n    \"react-contenteditable\": \"^3.3.7\",\n    \"react-i18next\": \"^12.1.1\"\n  },\n  \"devDependencies\": {\n    \"@babel/core\": \"^7.21.0\",\n    \"@chamn/build-script\": \"workspace:*\",\n    \"@chamn/demo-page\": \"workspace:*\",\n    \"@chromatic-com/storybook\": \"^3.2.3\",\n    \"@storybook/addon-essentials\": \"^8.4.7\",\n    \"@storybook/addon-interactions\": \"^8.4.7\",\n    \"@storybook/addon-onboarding\": \"^8.4.7\",\n    \"@storybook/blocks\": \"^8.4.7\",\n    \"@storybook/react\": \"^8.4.7\",\n    \"@storybook/react-vite\": \"^8.4.7\",\n    \"@storybook/test\": \"^8.4.7\",\n    \"@types/color\": \"^3.0.4\",\n    \"@types/css\": \"^0.0.34\",\n    \"@types/css-tree\": \"^2.3.10\",\n    \"@types/loadjs\": \"^4.0.4\",\n    \"@types/lodash-es\": \"^4.17.6\",\n    \"@types/react\": \"^18.2.0\",\n    \"@types/react-color\": \"^3.0.6\",\n    \"@types/react-dom\": \"^18.0.6\",\n    \"babel-loader\": \"^8.3.0\",\n    \"cross-env\": \"^7.0.3\",\n    \"prop-types\": \"^15.8.1\",\n    \"re-resizable\": \"^6.9.9\",\n    \"react\": \"^18.2.0\",\n    \"react-dom\": \"^18.2.0\",\n    \"react-router-dom\": \"^6.7.0\",\n    \"sass\": \"^1.54.0\",\n    \"storybook\": \"^8.4.7\",\n    \"vite-plugin-static-copy\": \"^0.17.0\"\n  },\n  \"config\": {},\n  \"gitHead\": \"dc3e55fdeb903a8012f6ebd3ebc018ed61ad89db\"\n}"
  },
  {
    "path": "packages/engine/src/Engine.module.scss",
    "content": ".engineContainer {\n  color: $fontColor;\n  font-size: $fontSize;\n  width: 100%;\n  height: 100%;\n  overflow: hidden;\n  * {\n    box-sizing: border-box;\n  }\n}\n"
  },
  {
    "path": "packages/engine/src/_dev_/index.css",
    "content": "html,\nbody,\n#root {\n  width: 100%;\n  height: 100%;\n  overflow: hidden;\n}\n\nbody {\n  margin: 0;\n  padding: 0;\n}\n\n.logo {\n  height: 100%;\n  font-size: 20px;\n  display: flex;\n  align-items: center;\n  margin-left: 20px;\n  font-weight: bolder;\n  margin-right: auto;\n}\n"
  },
  {
    "path": "packages/engine/src/_dev_/index.tsx",
    "content": "import ReactDOMClient from 'react-dom/client';\nimport { RouterProvider } from 'react-router-dom';\nimport { router } from './router';\n\nReactDOMClient.createRoot(document.getElementById('root') as HTMLElement).render(<RouterProvider router={router} />);\n"
  },
  {
    "path": "packages/engine/src/_dev_/lib/index.tsx",
    "content": "import { Resizable } from 're-resizable';\nimport { useEffect, useRef, useState } from 'react';\nimport ContentEditable from 'react-contenteditable';\n\nexport const CLayout = () => {\n  const text = useRef('123123123');\n  const contentEditableRef = useRef(null);\n\n  const handleChange = (evt: any) => {\n    text.current = evt.target.value;\n  };\n\n  const handleBlur = () => {\n    console.log(text.current);\n  };\n\n  useEffect(() => {\n    document.addEventListener('click', (e) => {\n      if (e.target && contentEditableRef.current) {\n        if (!(contentEditableRef.current as HTMLDivElement).contains(e.target as any)) {\n          (contentEditableRef.current as any)?.blur();\n          setCanEdit(false);\n        }\n      }\n    });\n  }, []);\n\n  const [canEdit, setCanEdit] = useState(false);\n\n  return (\n    <div>\n      CLayout 布局样例\n      <span\n        onDoubleClick={() => {\n          setCanEdit(true);\n          (contentEditableRef.current as any)?.focus();\n        }}\n      >\n        <ContentEditable\n          style={{\n            pointerEvents: canEdit ? 'auto' : 'none',\n          }}\n          id=\"a4455\"\n          className=\"as44556\"\n          onMouseDown={() => {\n            console.log('mouse down');\n          }}\n          innerRef={contentEditableRef}\n          html={text.current}\n          onBlur={handleBlur}\n          onChange={handleChange}\n        />\n      </span>\n      <Resizable\n        style={{\n          backgroundColor: 'red',\n        }}\n        handleClasses={{\n          left: 'resize-handle',\n          right: 'resize-handle',\n          top: 'resize-handle',\n          bottom: 'resize-handle',\n          topLeft: 'resize-handle',\n          topRight: 'resize-handle',\n          bottomLeft: 'resize-handle',\n          bottomRight: 'resize-handle',\n        }}\n        defaultSize={{\n          width: 320,\n          height: 200,\n        }}\n      >\n        Sample with default size\n      </Resizable>\n    </div>\n  );\n};\n"
  },
  {
    "path": "packages/engine/src/_dev_/page/Editor/index.tsx",
    "content": "import { BasePage } from '@chamn/demo-page';\nimport { Button, message, Modal, Select } from 'antd';\nimport React, { useCallback, useEffect, useRef, useState } from 'react';\nimport ReactDOM from 'react-dom';\nimport ReactDOMClient from 'react-dom/client';\nimport { Engine, PluginInstance } from '../../..';\nimport '../../index.css';\nimport { ComponentLibPluginConfig, DEFAULT_PLUGIN_LIST, DEFAULT_PLUGIN_NAME_MAP } from '../../../plugins';\nimport { DisplaySourceSchema } from '../../../plugins/DisplaySourceSchema';\nimport { InnerComponentMeta } from '../../../material/innerMaterial';\nimport { RollbackOutlined } from '@ant-design/icons';\nimport { LayoutPropsType } from '@chamn/layout';\n\nimport { collectVariable, flatObject, getThirdLibs } from '@chamn/render';\nimport { HistoryPluginInstance } from '@/plugins/History/type';\nimport { DesignerPluginInstance } from '@/plugins/Designer/type';\n\nimport { DesignerSizer } from '@/component/DesignerSizer';\nimport { EnginContext } from '@/type';\nimport renderAsURL from '@chamn/render/dist/index.umd.js?url';\n\nconst win = window as any;\nwin.React = React;\nwin.ReactDOM = ReactDOM;\nwin.ReactDOMClient = ReactDOMClient;\n\nconst customRender: LayoutPropsType['customRender'] = async ({\n  iframe: iframeContainer,\n  assets,\n  page,\n  pageModel,\n  beforeInitRender,\n  ready,\n}) => {\n  await iframeContainer.loadUrl('/src/_dev_/render.html');\n  // must call\n  beforeInitRender?.();\n  const iframeWindow = iframeContainer.getWindow()!;\n  const iframeDoc = iframeContainer.getDocument()!;\n  const IframeReact = iframeWindow.React!;\n  const IframeReactDOM = iframeWindow.ReactDOMClient!;\n  const CRender = iframeWindow.CRender!;\n  await new CRender.AssetLoader(assets, {\n    window: iframeWindow,\n  }).load();\n  // 从子窗口获取物料对象\n  const componentCollection = collectVariable(assets, iframeWindow);\n  const components = flatObject(componentCollection);\n  const thirdLibs = getThirdLibs(componentCollection, page?.thirdLibs || []);\n  const App = IframeReact?.createElement(CRender.DesignRender, {\n    adapter: CRender?.ReactAdapter,\n    page: page,\n    pageModel: pageModel,\n    components: {\n      ...components,\n    },\n    $$context: {\n      thirdLibs,\n      getProps: () => {\n        return {};\n      },\n      callEventMethod: (method: string, params: any) => {\n        console.log(method, params);\n      },\n    },\n    requestAPI: async (params) => {\n      return console.log(222, params);\n    },\n    onMount: (designRenderInstance) => {\n      ready(designRenderInstance);\n    },\n  });\n\n  IframeReactDOM.createRoot(iframeDoc.getElementById('app')!).render(App);\n};\n\nconst buildVersion = `t_${__BUILD_VERSION__}`;\n\nconst assetPackagesList = [] as any[];\nexport const App = () => {\n  const [ready, setReady] = useState(false);\n  const [page, setPage] = useState(BasePage);\n  const [lang, setLang] = useState(() => {\n    const lang = localStorage.getItem('lang') || 'zh_CN';\n    return lang;\n  });\n\n  const engineRef = useRef<EnginContext>();\n\n  useEffect(() => {\n    // check 本地版本号，如果不一致直接覆盖本地所有的\n    const localBuildVersion = localStorage.getItem('build_version');\n    if (localBuildVersion !== buildVersion && !import.meta.env.DEV) {\n      // 清理 schema, 因为可能 协议不兼容，demo 可以这样粗暴处理\n      localStorage.setItem('pageSchema', '');\n      localStorage.setItem('build_version', buildVersion);\n    }\n    const localPage = localStorage.getItem('pageSchema');\n    if (localPage) {\n      setPage(JSON.parse(localPage));\n    }\n    setReady(true);\n  }, []);\n  const onReady = useCallback(\n    async (ctx: EnginContext) => {\n      engineRef.current = ctx;\n      engineRef.current?.engine.getI18n()?.changeLanguage(lang);\n\n      const designer: DesignerPluginInstance = await ctx.pluginManager.onPluginReadyOk('Designer');\n\n      const reloadPage = async () => {\n        setTimeout(() => {\n          const designerExport = designer?.export;\n          console.log('to reload');\n          designerExport.reload();\n        }, 0);\n      };\n\n      const workbench = ctx.engine.getWorkbench();\n\n      // 添加自定义 view\n      // const disposeView = workbench?.addCustomView({\n      //   key: 'testView',\n      //   view: (\n      //     <div\n      //       style={{\n      //         display: 'none',\n      //       }}\n      //       onClick={() => console.log('click')}\n      //     >\n      //       123123\n      //     </div>\n      //   ),\n      // });\n\n      workbench?.replaceTopBarView(\n        <div\n          style={{\n            width: '100%',\n            height: '100%',\n            display: 'flex',\n            alignItems: 'center',\n            justifyContent: 'flex-end',\n            paddingRight: '10px',\n          }}\n        >\n          <div className=\"logo\">Chameleon EG</div>\n          <div\n            style={{\n              height: '100%',\n              display: 'flex',\n              alignItems: 'center',\n              justifyContent: 'center',\n              marginRight: '10px',\n            }}\n          >\n            {ctx && <DesignerSizer ctx={ctx} />}\n          </div>\n          <Select\n            defaultValue={lang}\n            style={{ width: 100, marginRight: '10px' }}\n            onChange={(val) => {\n              setLang(val);\n              engineRef.current?.engine.getI18n()?.changeLanguage(val);\n            }}\n            options={[\n              {\n                value: 'zh_CN',\n                label: 'Chinese',\n              },\n              {\n                value: 'en_US',\n                label: 'English',\n              },\n            ]}\n          />\n          <a target=\"_blank\" href=\"https://hlerenow.github.io/chameleon/documents/\" rel=\"noreferrer\">\n            <Button style={{ marginRight: '10px' }}>Documents </Button>\n          </a>\n          <a target=\"_blank\" href=\"https://github.com/hlerenow/chameleon\" rel=\"noreferrer\">\n            <Button style={{ marginRight: '10px' }}>Github </Button>\n          </a>\n\n          <Button\n            style={{ marginRight: '10px' }}\n            onClick={async () => {\n              const res = await ctx.pluginManager.get<HistoryPluginInstance>('History');\n              res?.export.preStep();\n            }}\n          >\n            <RollbackOutlined />\n          </Button>\n          <Button\n            style={{ marginRight: '10px' }}\n            onClick={async () => {\n              const res = await ctx.pluginManager.get<HistoryPluginInstance>('History');\n              res?.export.nextStep();\n            }}\n          >\n            <RollbackOutlined\n              style={{\n                transform: 'rotateY(180deg)',\n              }}\n            />\n          </Button>\n\n          <DisplaySourceSchema pageModel={ctx.engine.pageModel} engineCtx={ctx}>\n            <Button style={{ marginRight: '10px' }}>Source Code</Button>\n          </DisplaySourceSchema>\n\n          <Button\n            style={{ marginRight: '10px' }}\n            onClick={() => {\n              reloadPage();\n            }}\n          >\n            Refresh Page\n          </Button>\n          <Button\n            style={{ marginRight: '10px' }}\n            onClick={() => {\n              let src = '/#/preview';\n              if (location.href.includes('hlerenow')) {\n                src = '/chameleon/#/preview';\n              }\n\n              Modal.info({\n                closable: true,\n                icon: null,\n                width: 'calc(100vw - 100px)',\n                centered: true,\n                title: (\n                  <div>\n                    Preview\n                    <Button\n                      size=\"small\"\n                      style={{\n                        float: 'right',\n                        marginRight: '30px',\n                      }}\n                      onClick={() => {\n                        window.open(src);\n                      }}\n                    >\n                      Open in new window\n                    </Button>\n                  </div>\n                ),\n                content: (\n                  <div\n                    style={{\n                      width: '100%',\n                      height: 'calc(100vh - 200px)',\n                    }}\n                  >\n                    <iframe\n                      style={{\n                        border: '1px solid #e7e7e7',\n                        width: '100%',\n                        height: '100%',\n                        borderRadius: '4px',\n                        overflow: 'hidden',\n                      }}\n                      src={src}\n                    />\n                  </div>\n                ),\n                footer: <></>,\n              });\n            }}\n          >\n            Preview\n          </Button>\n          <Button\n            type=\"primary\"\n            onClick={() => {\n              const newPage = ctx.engine.pageModel.export();\n              localStorage.setItem('pageSchema', JSON.stringify(newPage));\n              message.success('Save successfully');\n            }}\n          >\n            Save\n          </Button>\n        </div>\n      );\n    },\n    [lang]\n  );\n\n  if (!ready) {\n    return <>loading...</>;\n  }\n\n  return (\n    <Engine\n      plugins={DEFAULT_PLUGIN_LIST}\n      schema={page}\n      onMount={() => {\n        // setTimeout(async () => {\n        //   const res = await ctx.engine.updateMaterials(\n        //     [],\n        //     [\n        //       {\n        //         package: 'lodash',\n        //         globalName: 'lodash',\n        //         resources: [\n        //           {\n        //             src: 'https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.js',\n        //           },\n        //         ],\n        //       },\n        //       {\n        //         package: 'dayjs',\n        //         globalName: 'dayjs',\n        //         resources: [\n        //           {\n        //             src: 'https://cdn.bootcdn.net/ajax/libs/dayjs/1.11.9/dayjs.min.js',\n        //           },\n        //         ],\n        //       },\n        //     ]\n        //   );\n        //   console.log('add material successfully');\n        // }, 2 * 1000);\n      }}\n      // 传入组件物料\n      material={[...InnerComponentMeta]}\n      // 组件物料对应的 js 运行库，只能使用 umd 模式的 js\n      assetPackagesList={assetPackagesList}\n      beforePluginRun={({ pluginManager }) => {\n        pluginManager.customPlugin('RightPanel', (pluginInstance) => {\n          pluginInstance.ctx.config.customPropertySetterMap = {\n            TestSetter: (props: any) => {\n              useEffect(() => {\n                console.log(props);\n                const currentNode = props.setterContext.pluginCtx.engine.getActiveNode();\n                currentNode.value.configure.isContainer = false;\n                currentNode.value.children = [];\n                currentNode.updateValue();\n              }, [props]);\n              return <div>123</div>;\n            },\n          };\n\n          return pluginInstance;\n        });\n\n        pluginManager.customPlugin('Designer', (pluginInstance: DesignerPluginInstance) => {\n          if (__RUN_MODE__ !== 'APP') {\n            pluginInstance.ctx.config.customRender = customRender;\n          }\n          return pluginInstance;\n        });\n        /** 自定义组件库插件的搜索框 */\n        pluginManager.customPlugin(\n          DEFAULT_PLUGIN_NAME_MAP.ComponentLibPlugin,\n          (pluginInstance: PluginInstance<ComponentLibPluginConfig>) => {\n            pluginInstance.ctx.config.customSearchBar = ({ defaultInputView }) => {\n              return <div>{defaultInputView}</div>;\n            };\n            return pluginInstance;\n          }\n        );\n      }}\n      monacoEditor={{\n        cndUrl: 'https://static.hai-fe.com/code-block/web/dist/monaco-editor/min/vs',\n      }}\n      renderJSUrl={renderAsURL}\n      onReady={onReady}\n      renderProps={{\n        requestAPI: async (params) => {\n          return console.log(7788, params);\n        },\n      }}\n    />\n  );\n};\n"
  },
  {
    "path": "packages/engine/src/_dev_/page/Preview/index.tsx",
    "content": "import { useEffect, useState } from 'react';\nimport {\n  ReactAdapter,\n  Render,\n  useRender,\n  AssetLoader,\n  collectVariable,\n  flatObject,\n  getComponentsLibs,\n  getThirdLibs,\n} from '@chamn/render';\nimport { AssetPackage, CPageDataType } from '@chamn/model';\n\nconst loadAssets = async (assets: AssetPackage[]) => {\n  // 注入组件物料资源\n  const assetLoader = new AssetLoader(assets);\n  try {\n    await assetLoader.load();\n    // 从子窗口获取物料对象\n    const componentCollection = collectVariable(assets, window);\n    return componentCollection;\n  } catch {\n    return null;\n  }\n};\n\nexport const Preview = () => {\n  const [page, setPage] = useState<CPageDataType>();\n  const renderHandle = useRender();\n  const [loading, setLoading] = useState(true);\n  const [pageComponents, setPageComponents] = useState({});\n  const [renderContext, setRenderContext] = useState<any>({});\n  // 需要区分 那些 UI 组件那些第三方库的对象，分别注入\n  const loadPageAssets = async (pageInfo: CPageDataType) => {\n    const assets = pageInfo.assets || [];\n    const allLibs = (await loadAssets(assets)) || {};\n    const componentsLibs = getComponentsLibs(flatObject(allLibs), pageInfo.componentsMeta);\n    const thirdLibs = getThirdLibs(allLibs, pageInfo.thirdLibs || []);\n    if (componentsLibs) {\n      setPageComponents(componentsLibs);\n      setRenderContext({ thirdLibs });\n      setLoading(false);\n    }\n  };\n  useEffect(() => {\n    const localPage = localStorage.getItem('pageSchema');\n    if (localPage) {\n      const page: CPageDataType = JSON.parse(localPage);\n      setPage(page);\n      loadPageAssets(page);\n    }\n  }, []);\n\n  if (loading) {\n    return <>Not find page info on local, please ensure you save it on editor</>;\n  }\n  return (\n    <div className=\"App\" style={{ overflow: 'auto', height: '100%' }}>\n      <Render\n        page={page}\n        components={{\n          ...pageComponents,\n        }}\n        render={renderHandle}\n        adapter={ReactAdapter}\n        $$context={renderContext}\n      />\n    </div>\n  );\n};\n"
  },
  {
    "path": "packages/engine/src/_dev_/page/componentEditor/index.tsx",
    "content": "import { BasePage } from '@chamn/demo-page';\nimport React, { useCallback, useEffect, useRef, useState } from 'react';\nimport ReactDOM from 'react-dom';\nimport ReactDOMClient from 'react-dom/client';\nimport { Engine } from '../../..';\nimport '../../index.css';\nimport { DEFAULT_PLUGIN_LIST } from '../../../plugins';\nimport { InnerComponentMeta } from '../../../material/innerMaterial';\nimport {} from '@ant-design/icons';\n\nimport { DesignerPluginInstance } from '@/plugins/Designer/type';\n\nimport { EnginContext } from '@/type';\nimport renderAsURL from '@chamn/render/dist/index.umd.js?url';\n\nconst win = window as any;\nwin.React = React;\nwin.ReactDOM = ReactDOM;\nwin.ReactDOMClient = ReactDOMClient;\n\nconst buildVersion = `t_${__BUILD_VERSION__}`;\n\nconst assetPackagesList = [] as any[];\nexport const ComponentEditor = () => {\n  const [ready, setReady] = useState(false);\n  const [page, setPage] = useState(BasePage);\n  const [lang] = useState(() => {\n    const lang = localStorage.getItem('lang') || 'zh_CN';\n    return lang;\n  });\n\n  const engineRef = useRef<EnginContext>();\n\n  useEffect(() => {\n    // check 本地版本号，如果不一致直接覆盖本地所有的\n    const localBuildVersion = localStorage.getItem('build_version');\n    if (localBuildVersion !== buildVersion && !import.meta.env.DEV) {\n      // 清理 schema, 因为可能 协议不兼容，demo 可以这样粗暴处理\n      localStorage.setItem('pageSchema', '');\n      localStorage.setItem('build_version', buildVersion);\n    }\n    const localPage = localStorage.getItem('pageSchema');\n    if (localPage) {\n      setPage(JSON.parse(localPage));\n    }\n    setReady(true);\n  }, []);\n  const onReady = useCallback(\n    async (ctx: EnginContext) => {\n      engineRef.current = ctx;\n      engineRef.current?.engine.getI18n()?.changeLanguage(lang);\n\n      // const designer: DesignerPluginInstance = await ctx.pluginManager.onPluginReadyOk('Designer');\n      // designer.export?.setMode?.(LayoutMode.EDIT);\n\n      // setTimeout(() => {\n      //   designer.export?.setMode?.(LayoutMode.PREVIEW);\n      //   setTimeout(() => {\n      //     designer.export?.setMode?.(LayoutMode.EDIT);\n      //   }, 3 * 1000);\n      // }, 5 * 1000);\n\n      engineRef.current?.engine.preview();\n\n      setTimeout(() => {\n        engineRef.current?.engine.existPreview();\n        setTimeout(() => {\n          engineRef.current?.engine.preview();\n        }, 5000);\n      }, 3000);\n\n      const workbench = ctx.engine.getWorkbench();\n\n      workbench?.replaceTopBarView(<></>);\n    },\n    [lang]\n  );\n\n  if (!ready) {\n    return <>loading...</>;\n  }\n\n  return (\n    <Engine\n      workbenchConfig={{\n        hiddenTopBar: true,\n        hiddenLeftPanel: true,\n      }}\n      plugins={DEFAULT_PLUGIN_LIST}\n      schema={page}\n      // 传入组件物料\n      material={[...InnerComponentMeta]}\n      // 组件物料对应的 js 运行库，只能使用 umd 模式的 js\n      assetPackagesList={assetPackagesList}\n      beforePluginRun={({ pluginManager }) => {\n        pluginManager.customPlugin('Designer', (pluginInstance: DesignerPluginInstance) => {\n          pluginInstance.ctx.config.toolbarViewRender = () => {\n            return <></>;\n          };\n          return pluginInstance;\n        });\n      }}\n      renderJSUrl={renderAsURL}\n      onReady={onReady}\n      renderProps={{\n        requestAPI: async (params) => {\n          return console.log(7788, params);\n        },\n      }}\n    />\n  );\n};\n"
  },
  {
    "path": "packages/engine/src/_dev_/render.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <link rel=\"icon\" type=\"image/svg+xml\" href=\"/vite.svg\" />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n  <title>Low-code engine</title>\n  <style>\n    * {\n      margin: 0;\n      padding: 0;\n      box-sizing: border-box;\n    }\n\n    html,\n    body,\n    #app {\n      height: 100%;\n      width: 100%;\n    }\n  </style>\n  <script>\n    window.React = window.parent.React;\n    window.ReactDOM = window.parent.ReactDOM;\n    window.ReactDOMClient = window.parent.ReactDOMClient;\n  </script>\n</head>\n\n<body>\n  <div id=\"app\"></div>\n  <script type=\"module\" src=\"./render.tsx\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "packages/engine/src/_dev_/render.tsx",
    "content": "import renderAsURL from '@chamn/render/dist/index.umd.js?url';\nimport loadjs from 'loadjs';\n\nloadjs([renderAsURL], () => {\n  console.log('load render.umd.js success');\n});\n"
  },
  {
    "path": "packages/engine/src/_dev_/router.tsx",
    "content": "import { createHashRouter } from 'react-router-dom';\nimport { App } from './page/Editor';\nimport { Preview } from './page/Preview';\nimport { ComponentEditor } from './page/componentEditor';\n\nexport const router: any = createHashRouter([\n  {\n    path: '/previewComp',\n    element: <ComponentEditor />,\n  },\n  {\n    path: '/',\n    element: <App />,\n  },\n  {\n    path: '/preview',\n    element: <Preview />,\n  },\n]);\n"
  },
  {
    "path": "packages/engine/src/assets/styles/mixin.scss",
    "content": "@use 'sass:color';\n$baseColor: #1677ff;\n/* base style variables */\n$borderColor: rgb(233 233 233);\n$baseBackgroundColor: #edeff3;\n$fontSizeBigger: 16px;\n$fontSize: 14px;\n$fontSizeSmall: 12px;\n\n$fontColor: rgb(104, 104, 104);\n\n$hoverColor: rgba(22, 119, 255, 0.3);\n$textColor: $fontColor;\n\n$borderRadius: 2px;\n"
  },
  {
    "path": "packages/engine/src/build-script-env.d.ts",
    "content": "/// <reference types=\"@chamn/build-script/client\" />\n\n/** 包版本号 */\ndeclare const __PACKAGE_VERSION__: string;\n/** 构建版本号 */\ndeclare const __BUILD_VERSION__: string;\n/** 运行模式 */\ndeclare const __RUN_MODE__: string;\n"
  },
  {
    "path": "packages/engine/src/component/CSSCodeEditor/helper.ts",
    "content": "import * as csstree from 'css-tree';\n\nexport function parseCssToObject(css: string): Record<string, Record<string, string>> {\n  const result: Record<string, Record<string, string>> = {};\n\n  const ast = csstree.parse(css, {\n    context: 'stylesheet',\n  });\n\n  csstree.walk(ast, {\n    visit: 'Rule',\n    enter(node) {\n      if (node.type === 'Rule' && node.prelude.type === 'SelectorList') {\n        const selector = csstree.generate(node.prelude);\n\n        const declarations: Record<string, string> = {};\n        if (node.block.type === 'Block') {\n          for (const declaration of node.block.children.toArray()) {\n            if (declaration.type === 'Declaration') {\n              const prop = declaration.property;\n              const value = csstree.generate(declaration.value);\n              declarations[prop] = value;\n            }\n          }\n        }\n\n        result[selector] = declarations;\n      }\n    },\n  });\n\n  return result;\n}\n"
  },
  {
    "path": "packages/engine/src/component/CSSCodeEditor/index.tsx",
    "content": "import { forwardRef, useImperativeHandle, useRef } from 'react';\nimport { MonacoEditor, MonacoEditorInstance } from '../MonacoEditor';\nimport styles from './style.module.scss';\nimport { parseCssToObject } from './helper';\nimport { InputCommonRef } from '../StylePanel/type';\nimport { isEqual } from 'lodash-es';\n\nconst getCssCodeFromStyleObj = (styleObj: Record<any, any> = {}) => {\n  let res = `.node {\\n`;\n  Object.keys(styleObj).forEach((key) => {\n    res += `  ${key}: ${styleObj[key]};\\n`;\n  });\n  res += '}';\n\n  return res;\n};\n\ntype CSSCodeEditorProps = {\n  onValueChange?: (newVal: Record<any, any>) => void;\n};\n\nexport type CSSCodeEditorRef = InputCommonRef;\n\nexport const CSSCodeEditor = forwardRef<CSSCodeEditorRef, CSSCodeEditorProps>((props, ref) => {\n  const editorRef = useRef<MonacoEditorInstance | null>(null);\n  const valueRef = useRef({});\n  useImperativeHandle(\n    ref,\n    () => {\n      return {\n        setEmptyValue: () => {\n          valueRef.current = {};\n          editorRef.current?.setValue(getCssCodeFromStyleObj({}));\n        },\n        setValue: (val) => {\n          if (isEqual(val, valueRef.current)) {\n            return;\n          }\n          valueRef.current = val;\n          editorRef.current?.setValue(getCssCodeFromStyleObj(val));\n        },\n      };\n    },\n    []\n  );\n\n  return (\n    <div\n      className={styles.cssCodeEditor}\n      style={{\n        paddingTop: '10px',\n        width: '100%',\n        height: '150px',\n        border: '1px solid rgba(0,0,0,0.2)',\n        position: 'relative',\n        borderRadius: '4px',\n      }}\n    >\n      <MonacoEditor\n        language=\"css\"\n        onDidMount={(editor) => {\n          editorRef.current = editor;\n          editorRef.current?.setValue(getCssCodeFromStyleObj(valueRef.current || {}));\n        }}\n        onChange={(newVal) => {\n          const styleObj = parseCssToObject(newVal || '');\n          const newValObj = styleObj['.node'] || {};\n          valueRef.current = newValObj;\n          props.onValueChange?.(newValObj);\n        }}\n        options={{\n          tabSize: 2,\n          minimap: { enabled: false },\n          folding: false,\n          lineNumbers: 'off',\n          hover: {\n            // enabled: false, // ✅ 禁用 hoverWidget\n          },\n        }}\n      />\n    </div>\n  );\n});\n"
  },
  {
    "path": "packages/engine/src/component/CSSCodeEditor/style.module.scss",
    "content": ".cssCodeEditor {\n  :global {\n    .monaco-editor .suggest-widget {\n      width: 300px !important;\n      left: 0 !important;\n      right: auto !important;\n      transform: none !important; // 防止默认偏移\n    }\n  }\n}\n"
  },
  {
    "path": "packages/engine/src/component/CSSEditor/index.tsx",
    "content": "import { waitReactUpdate } from '@/utils';\nimport { formatCSSTextProperty, StyleArr, styleList2Text } from '@/utils/css';\nimport { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';\nimport { Card, Collapse, Dropdown, Space } from 'antd';\nimport CheckableTag from 'antd/es/tag/CheckableTag';\nimport { MutableRefObject, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { CSSPropertiesEditor, CSSPropertiesEditorRef } from '../CSSPropertiesEditor';\nimport styles from './style.module.scss';\nimport { isEmpty } from 'lodash-es';\n// state: 'normal' | 'hover' | 'active' | 'focus' | 'first' | 'last' | 'even' | 'odd';\n\nconst DOM_CSS_STATUS = [\n  'normal' as const,\n  'hover' as const,\n  'focus' as const,\n  'focus-within' as const,\n  'focus-visible' as const,\n  'checked' as const,\n  'disable' as const,\n  'active' as const,\n];\n\ntype DomCSSStatusType = typeof DOM_CSS_STATUS[number];\n\nconst DOM_CSS_STATUS_LIST = DOM_CSS_STATUS.map((el) => {\n  return {\n    key: el,\n    label: el,\n  };\n});\n\ntype MediaQueryItem = {\n  key: string;\n  maxWidth: string;\n  label: string;\n};\n\nexport type CSSVal = Partial<\n  Record<\n    DomCSSStatusType,\n    Record<\n      /** media query key */\n      string,\n      string\n    >\n  >\n>;\n\nexport type CSSEditorRef = {\n  setValue: (val: CSSVal) => void;\n};\n\nexport type CSSEditorProps = {\n  onValueChange?: (val: CSSVal) => void;\n  initialValue?: CSSVal;\n  handler?: MutableRefObject<CSSEditorRef | null>;\n};\n\nexport const CSSEditor = (props: CSSEditorProps) => {\n  const [selectedStateTag, setSelectedStateTag] = useState<DomCSSStatusType>('normal');\n  const [mediaQueryList] = useState<MediaQueryItem[]>([\n    {\n      key: '991',\n      maxWidth: '991',\n      label: 'Medial Query ( <= 991 px )',\n    },\n    {\n      key: '767',\n      maxWidth: '767',\n      label: 'Medial Query ( <= 767 px )',\n    },\n    {\n      key: '479',\n      maxWidth: '479',\n      label: 'Medial Query ( <= 479 px )',\n    },\n  ]);\n  const cssPropertyRefMap = useRef<Record<string, CSSPropertiesEditorRef | null>>({});\n  const handleChange = (tag: DomCSSStatusType) => {\n    setSelectedStateTag(tag);\n  };\n  const [domStatusList, setDomStatusList] = useState<string[]>([]);\n  const cssStatusList = useMemo(() => {\n    return DOM_CSS_STATUS_LIST.filter((el) => {\n      return !domStatusList.includes(el.key);\n    });\n  }, [domStatusList]);\n\n  const selectCssStatusList = useMemo(() => {\n    return DOM_CSS_STATUS_LIST.filter((el) => {\n      return domStatusList.includes(el.key);\n    });\n  }, [domStatusList]);\n\n  const [cssVal, setCssVal] = useState<CSSVal>(props.initialValue ?? {});\n\n  useEffect(() => {\n    const list = Object.keys(cssVal);\n    setDomStatusList(list);\n  }, [cssVal]);\n\n  const currentCssStateVal = useMemo(() => {\n    const res = cssVal?.[selectedStateTag];\n    if (!res) {\n      return {};\n    }\n    const newVal: Record<string, ReturnType<typeof formatCSSTextProperty>> = {};\n    Object.keys(res).forEach((key) => {\n      newVal[key] = formatCSSTextProperty(res[key] || '');\n    });\n\n    return newVal;\n  }, [selectedStateTag, cssVal]);\n\n  const initVal = useCallback(() => {\n    Object.keys(cssPropertyRefMap.current).forEach((key) => {\n      const ref = cssPropertyRefMap.current?.[key];\n      const cssVal = currentCssStateVal[key] || [];\n      if (ref) {\n        ref.setValue(cssVal);\n      }\n    });\n  }, [currentCssStateVal]);\n  const initRef = useRef<() => void>();\n  initRef.current = initVal;\n\n  if (props.handler) {\n    props.handler.current = {\n      setValue: async (newVal) => {\n        if (isEmpty(newVal)) {\n          setCssVal({\n            normal: {\n              normal: '',\n            },\n          });\n        } else {\n          setCssVal(newVal);\n        }\n        await waitReactUpdate();\n        initRef.current?.();\n      },\n    };\n  }\n\n  // 初始化赋值\n  useEffect(() => {\n    initRef.current?.();\n  }, [selectedStateTag]);\n\n  const updateCss = useCallback(\n    (mediaKey: string, val: StyleArr) => {\n      const newVal = {\n        ...cssVal,\n        [selectedStateTag]: {\n          ...(cssVal[selectedStateTag] || {}),\n          [mediaKey]: styleList2Text(val),\n        },\n      };\n      props.onValueChange?.(newVal);\n    },\n    [cssVal, props, selectedStateTag]\n  );\n\n  return (\n    <>\n      <Card\n        size=\"small\"\n        type=\"inner\"\n        title={<span style={{ fontSize: '12px' }}>CSS</span>}\n        extra={\n          <Dropdown\n            menu={{\n              items: cssStatusList,\n              onClick: (el) => {\n                setDomStatusList((oldVal) => {\n                  return [...oldVal, el.key];\n                });\n              },\n            }}\n          >\n            <PlusOutlined />\n          </Dropdown>\n        }\n      >\n        <Space\n          size={[0, 8]}\n          wrap\n          style={{\n            paddingBottom: '10px',\n          }}\n        >\n          {selectCssStatusList.map((tag) => {\n            const checked = selectedStateTag.includes(tag.key);\n            return (\n              <CheckableTag\n                key={tag.key}\n                style={{\n                  border: !checked ? '1px solid rgb(216 216 216 / 82%)' : '1px solid rgba(0,0,0,0))',\n                }}\n                checked={checked}\n                onChange={() => handleChange(tag.key)}\n                className={styles.stateTag}\n              >\n                {tag.label}\n                {tag.key !== 'normal' && (\n                  <MinusCircleOutlined\n                    className={styles.stateTagClose}\n                    onClick={(e) => {\n                      e.stopPropagation();\n                      e.preventDefault();\n                      setDomStatusList((oldVal) => {\n                        return oldVal.filter((el) => {\n                          return el !== tag.key;\n                        });\n                      });\n                      setSelectedStateTag('normal');\n                    }}\n                  />\n                )}\n              </CheckableTag>\n            );\n          })}\n        </Space>\n\n        <Collapse\n          defaultActiveKey={['normal']}\n          bordered={false}\n          style={{\n            marginBottom: '10px',\n          }}\n          onChange={async () => {\n            await waitReactUpdate();\n            initRef.current?.();\n          }}\n          items={[\n            {\n              key: 'normal',\n              label: <span>Default</span>,\n              children: (\n                <CSSPropertiesEditor\n                  ref={(ref) => {\n                    cssPropertyRefMap.current['normal'] = ref;\n                  }}\n                  onValueChange={(val) => updateCss('normal', val)}\n                  initialValue={currentCssStateVal['normal']}\n                />\n              ),\n            },\n            ...mediaQueryList.map((el) => {\n              return {\n                key: el.key,\n                label: <span>{el.label}</span>,\n                children: (\n                  <CSSPropertiesEditor\n                    ref={(ref) => {\n                      cssPropertyRefMap.current[el.key] = ref;\n                    }}\n                    onValueChange={(val) => updateCss(el.key, val)}\n                  />\n                ),\n              };\n            }),\n          ]}\n        ></Collapse>\n      </Card>\n    </>\n  );\n};\n"
  },
  {
    "path": "packages/engine/src/component/CSSEditor/style.module.scss",
    "content": ".stateTag {\n  position: relative;\n  .stateTagClose {\n    position: absolute;\n    right: -10px;\n    top: -10px;\n    background-color: white;\n    color: #4d4d4d;\n    opacity: 0;\n    font-size: 20px;\n    transform: scale(0.6);\n    -webkit-font-smoothing: antialiased;\n    -moz-osx-font-smoothing: grayscale;\n    overflow: hidden;\n    border-radius: 10px;\n  }\n  &:hover {\n    .stateTagClose {\n      opacity: 1;\n    }\n  }\n}\n"
  },
  {
    "path": "packages/engine/src/component/CSSPropertiesEditor/cssProperties.ts",
    "content": "const CSSSourceInfo = {\n  cssColors: {\n    aliceblue: '#f0f8ff',\n    antiquewhite: '#faebd7',\n    aqua: '#00ffff',\n    aquamarine: '#7fffd4',\n    azure: '#f0ffff',\n    beige: '#f5f5dc',\n    bisque: '#ffe4c4',\n    black: '#000000',\n    blanchedalmond: '#ffebcd',\n    blue: '#0000ff',\n    blueviolet: '#8a2be2',\n    brown: '#a52a2a',\n    burlywood: '#deb887',\n    cadetblue: '#5f9ea0',\n    chartreuse: '#7fff00',\n    chocolate: '#d2691e',\n    coral: '#ff7f50',\n    cornflowerblue: '#6495ed',\n    cornsilk: '#fff8dc',\n    crimson: '#dc143c',\n    cyan: '#00ffff',\n    darkblue: '#00008b',\n    darkcyan: '#008b8b',\n    darkgoldenrod: '#b8860b',\n    darkgray: '#a9a9a9',\n    darkgreen: '#006400',\n    darkgrey: '#a9a9a9',\n    darkkhaki: '#bdb76b',\n    darkmagenta: '#8b008b',\n    darkolivegreen: '#556b2f',\n    darkorange: '#ff8c00',\n    darkorchid: '#9932cc',\n    darkred: '#8b0000',\n    darksalmon: '#e9967a',\n    darkseagreen: '#8fbc8f',\n    darkslateblue: '#483d8b',\n    darkslategray: '#2f4f4f',\n    darkslategrey: '#2f4f4f',\n    darkturquoise: '#00ced1',\n    darkviolet: '#9400d3',\n    deeppink: '#ff1493',\n    deepskyblue: '#00bfff',\n    dimgray: '#696969',\n    dimgrey: '#696969',\n    dodgerblue: '#1e90ff',\n    firebrick: '#b22222',\n    floralwhite: '#fffaf0',\n    forestgreen: '#228b22',\n    fuchsia: '#ff00ff',\n    gainsboro: '#dcdcdc',\n    ghostwhite: '#f8f8ff',\n    goldenrod: '#daa520',\n    gold: '#ffd700',\n    gray: '#808080',\n    green: '#008000',\n    greenyellow: '#adff2f',\n    grey: '#808080',\n    honeydew: '#f0fff0',\n    hotpink: '#ff69b4',\n    indianred: '#cd5c5c',\n    indigo: '#4b0082',\n    ivory: '#fffff0',\n    khaki: '#f0e68c',\n    lavenderblush: '#fff0f5',\n    lavender: '#e6e6fa',\n    lawngreen: '#7cfc00',\n    lemonchiffon: '#fffacd',\n    lightblue: '#add8e6',\n    lightcoral: '#f08080',\n    lightcyan: '#e0ffff',\n    lightgoldenrodyellow: '#fafad2',\n    lightgray: '#d3d3d3',\n    lightgreen: '#90ee90',\n    lightgrey: '#d3d3d3',\n    lightpink: '#ffb6c1',\n    lightsalmon: '#ffa07a',\n    lightseagreen: '#20b2aa',\n    lightskyblue: '#87cefa',\n    lightslategray: '#778899',\n    lightslategrey: '#778899',\n    lightsteelblue: '#b0c4de',\n    lightyellow: '#ffffe0',\n    lime: '#00ff00',\n    limegreen: '#32cd32',\n    linen: '#faf0e6',\n    magenta: '#ff00ff',\n    maroon: '#800000',\n    mediumaquamarine: '#66cdaa',\n    mediumblue: '#0000cd',\n    mediumorchid: '#ba55d3',\n    mediumpurple: '#9370db',\n    mediumseagreen: '#3cb371',\n    mediumslateblue: '#7b68ee',\n    mediumspringgreen: '#00fa9a',\n    mediumturquoise: '#48d1cc',\n    mediumvioletred: '#c71585',\n    midnightblue: '#191970',\n    mintcream: '#f5fffa',\n    mistyrose: '#ffe4e1',\n    moccasin: '#ffe4b5',\n    navajowhite: '#ffdead',\n    navy: '#000080',\n    oldlace: '#fdf5e6',\n    olive: '#808000',\n    olivedrab: '#6b8e23',\n    orange: '#ffa500',\n    orangered: '#ff4500',\n    orchid: '#da70d6',\n    palegoldenrod: '#eee8aa',\n    palegreen: '#98fb98',\n    paleturquoise: '#afeeee',\n    palevioletred: '#db7093',\n    papayawhip: '#ffefd5',\n    peachpuff: '#ffdab9',\n    peru: '#cd853f',\n    pink: '#ffc0cb',\n    plum: '#dda0dd',\n    powderblue: '#b0e0e6',\n    purple: '#800080',\n    rebeccapurple: '#663399',\n    red: '#ff0000',\n    rosybrown: '#bc8f8f',\n    royalblue: '#4169e1',\n    saddlebrown: '#8b4513',\n    salmon: '#fa8072',\n    sandybrown: '#f4a460',\n    seagreen: '#2e8b57',\n    seashell: '#fff5ee',\n    sienna: '#a0522d',\n    silver: '#c0c0c0',\n    skyblue: '#87ceeb',\n    slateblue: '#6a5acd',\n    slategray: '#708090',\n    slategrey: '#708090',\n    snow: '#fffafa',\n    springgreen: '#00ff7f',\n    steelblue: '#4682b4',\n    tan: '#d2b48c',\n    teal: '#008080',\n    thistle: '#d8bfd8',\n    tomato: '#ff6347',\n    turquoise: '#40e0d0',\n    violet: '#ee82ee',\n    wheat: '#f5deb3',\n    white: '#ffffff',\n    whitesmoke: '#f5f5f5',\n    yellow: '#ffff00',\n    yellowgreen: '#9acd32',\n  },\n  cssKeyValue: {\n    'align-content': {\n      values: [\n        'center',\n        'flex-end',\n        'flex-start',\n        'space-around',\n        'space-between',\n        'stretch',\n      ],\n    },\n    'align-items': {\n      values: ['baseline', 'center', 'flex-end', 'flex-start', 'stretch'],\n    },\n    'align-self': {\n      values: [\n        'auto',\n        'normal',\n        'self-start',\n        'self-end',\n        'baseline',\n        'center',\n        'start',\n        'end',\n        'flex-end',\n        'flex-start',\n        'safe',\n        'stretch',\n        'unsafe',\n      ],\n    },\n    all: { values: [] },\n    animation: { values: [] },\n    'animation-delay': { values: [] },\n    'animation-direction': {\n      values: ['alternate', 'alternate-reverse', 'normal', 'reverse'],\n    },\n    'animation-duration': { values: [] },\n    'animation-fill-mode': {\n      values: ['backwards', 'both', 'forwards', 'none'],\n    },\n    'animation-iteration-count': { values: ['infinite'] },\n    'animation-name': { values: ['none'] },\n    'animation-play-state': { values: ['paused', 'running'] },\n    'animation-timing-function': {\n      values: [\n        'cubic-bezier()',\n        'ease',\n        'ease-in',\n        'ease-in-out',\n        'ease-out',\n        'linear',\n        'step-end',\n        'step-start',\n        'steps()',\n      ],\n    },\n    'backface-visibility': { values: ['hidden', 'visible'] },\n    background: { values: [], type: 'color' },\n    'background-attachment': { values: ['fixed', 'local', 'scroll'] },\n    'background-blend-mode': {\n      values: [\n        'color',\n        'color-burn',\n        'color-dodge',\n        'darken',\n        'difference',\n        'exclusion',\n        'hard-light',\n        'hue',\n        'lighten',\n        'luminosity',\n        'multiply',\n        'normal',\n        'overlay',\n        'saturation',\n        'screen',\n        'soft-light',\n      ],\n    },\n    'background-clip': { values: ['border-box', 'content-box', 'padding-box'] },\n    'background-color': { values: [], type: 'color' },\n    'background-image': {\n      values: [\n        'image()',\n        'linear-gradient()',\n        'radial-gradient()',\n        'repeating-linear-gradient()',\n        'repeating-radial-gradient()',\n        'url()',\n      ],\n    },\n    'background-origin': {\n      values: ['border-box', 'content-box', 'padding-box'],\n    },\n    'background-position': {\n      values: ['left', 'center', 'right', 'bottom', 'top'],\n    },\n    'background-repeat': {\n      values: ['no-repeat', 'repeat', 'repeat-x', 'repeat-y', 'round', 'space'],\n    },\n    'background-size': { values: ['auto', 'contain', 'cover'] },\n    border: { values: [] },\n    'border-collapse': { values: ['collapse', 'separate'] },\n    'border-color': { values: [], type: 'color' },\n    'border-spacing': { values: [] },\n    'border-style': {\n      values: [\n        'dashed',\n        'dotted',\n        'double',\n        'groove',\n        'hidden',\n        'inset',\n        'none',\n        'outset',\n        'ridge',\n        'solid',\n      ],\n    },\n    'border-bottom': { values: [] },\n    'border-bottom-color': { values: [], type: 'color' },\n    'border-bottom-left-radius': { values: [] },\n    'border-bottom-right-radius': { values: [] },\n    'border-bottom-style': {\n      values: [\n        'dashed',\n        'dotted',\n        'double',\n        'groove',\n        'hidden',\n        'inset',\n        'none',\n        'outset',\n        'ridge',\n        'solid',\n      ],\n    },\n    'border-bottom-width': { values: ['medium', 'thin', 'thick'] },\n    'border-image': { values: ['url()'] },\n    'border-image-outset': { values: [] },\n    'border-image-slice': { values: [] },\n    'border-image-source': { values: [] },\n    'border-image-repeat': { values: ['repeat', 'round', 'space', 'stretch'] },\n    'border-image-width': { values: ['auto'] },\n    'border-left': { values: [] },\n    'border-left-color': { values: [], type: 'color' },\n    'border-left-style': {\n      values: [\n        'dashed',\n        'dotted',\n        'double',\n        'groove',\n        'hidden',\n        'inset',\n        'none',\n        'outset',\n        'ridge',\n        'solid',\n      ],\n    },\n    'border-left-width': { values: ['medium', 'thin', 'thick'] },\n    'border-radius': { values: [] },\n    'border-right': { values: [] },\n    'border-right-color': { values: [], type: 'color' },\n    'border-right-style': {\n      values: [\n        'dashed',\n        'dotted',\n        'double',\n        'groove',\n        'hidden',\n        'inset',\n        'none',\n        'outset',\n        'ridge',\n        'solid',\n      ],\n    },\n    'border-right-width': { values: ['medium', 'thin', 'thick'] },\n    'border-top': { values: [] },\n    'border-top-color': { values: [], type: 'color' },\n    'border-top-left-radius': { values: [] },\n    'border-top-right-radius': { values: [] },\n    'border-top-style': {\n      values: [\n        'dashed',\n        'dotted',\n        'double',\n        'groove',\n        'hidden',\n        'inset',\n        'none',\n        'outset',\n        'ridge',\n        'solid',\n      ],\n    },\n    'border-top-width': { values: ['medium', 'thin', 'thick'] },\n    'border-width': { values: ['medium', 'thin', 'thick'] },\n    'box-decoration-break': { values: ['clone', 'slice'] },\n    'box-shadow': { values: ['none'] },\n    'box-sizing': { values: ['border-box', 'content-box'] },\n    bottom: { values: ['auto'] },\n    'break-after': {\n      values: [\n        'always',\n        'auto',\n        'avoid',\n        'avoid-column',\n        'avoid-page',\n        'avoid-region',\n        'column',\n        'left',\n        'page',\n        'region',\n        'right',\n      ],\n    },\n    'break-before': {\n      values: [\n        'always',\n        'auto',\n        'avoid',\n        'avoid-column',\n        'avoid-page',\n        'avoid-region',\n        'column',\n        'left',\n        'page',\n        'region',\n        'right',\n      ],\n    },\n    'break-inside': {\n      values: ['auto', 'avoid', 'avoid-column', 'avoid-page', 'avoid-region'],\n    },\n    'caption-side': { values: ['bottom', 'top'] },\n    'caret-color': { values: ['auto'], type: 'color' },\n    clear: { values: ['both', 'left', 'none', 'right'] },\n    clip: { values: ['auto'] },\n    color: { values: [], type: 'color' },\n    columns: { values: [] },\n    'column-count': { values: [] },\n    'column-fill': { values: ['auto', 'balance'] },\n    'column-gap': { values: ['normal'] },\n    'column-rule': { values: [] },\n    'column-rule-color': { values: [], type: 'color' },\n    'column-rule-style': {\n      values: [\n        'dashed',\n        'dotted',\n        'double',\n        'groove',\n        'hidden',\n        'inset',\n        'none',\n        'outset',\n        'ridge',\n        'solid',\n      ],\n    },\n    'column-rule-width': { values: ['medium', 'thin', 'thick'] },\n    'column-span': { values: ['all', 'none'] },\n    'column-width': { values: ['auto'] },\n    content: {\n      values: [\n        'attr()',\n        'close-quote',\n        'no-close-quote',\n        'no-open-quote',\n        'normal',\n        'none',\n        'open-quote',\n      ],\n    },\n    'counter-increment': { values: ['none'] },\n    'counter-reset': { values: ['none'] },\n    cursor: {\n      values: [\n        'alias',\n        'all-scroll',\n        'auto',\n        'cell',\n        'col-resize',\n        'context-menu',\n        'copy',\n        'crosshair',\n        'default',\n        'e-resize',\n        'ew-resize',\n        'grab',\n        'grabbing',\n        'help',\n        'move',\n        'n-resize',\n        'ne-resize',\n        'nesw-resize',\n        'no-drop',\n        'none',\n        'not-allowed',\n        'ns-resize',\n        'nw-resize',\n        'nwse-resize',\n        'pointer',\n        'progress',\n        'row-resize',\n        's-resize',\n        'se-resize',\n        'sw-resize',\n        'text',\n        'vertical-text',\n        'w-resize',\n        'wait',\n        'zoom-in',\n        'zoom-out',\n      ],\n    },\n    direction: { values: ['ltr', 'rtl'] },\n    display: {\n      values: [\n        'block',\n        'contents',\n        'flex',\n        'flow-root',\n        'grid',\n        'inline',\n        'inline-block',\n        'inline-flex',\n        'inline-grid',\n        'inline-table',\n        'list-item',\n        'none',\n        'run-in',\n        'subgrid',\n        'table',\n        'table-caption',\n        'table-cell',\n        'table-column',\n        'table-column-group',\n        'table-footer-group',\n        'table-header-group',\n        'table-row',\n        'table-row-group',\n      ],\n    },\n    'empty-cells': { values: ['hide', 'show'] },\n    fill: { values: [] },\n    filter: {\n      values: [\n        'blur()',\n        'brightness()',\n        'contrast()',\n        'custom()',\n        'drop-shadow()',\n        'grayscale()',\n        'hue-rotate()',\n        'invert()',\n        'none',\n        'opacity()',\n        'sepia()',\n        'saturate()',\n        'url()',\n      ],\n    },\n    flex: { values: ['auto', 'none'] },\n    'flex-basis': { values: ['auto'] },\n    'flex-direction': {\n      values: ['column', 'column-reverse', 'row', 'row-reverse'],\n    },\n    'flex-flow': {\n      values: [\n        'column',\n        'column-reverse',\n        'nowrap',\n        'row',\n        'row-reverse',\n        'wrap',\n        'wrap-reverse',\n      ],\n    },\n    'flex-grow': { values: [] },\n    'flex-shrink': { values: [] },\n    'flex-wrap': { values: ['nowrap', 'wrap', 'wrap-reverse'] },\n    float: { values: ['left', 'right', 'none', 'inline-start', 'inline-end'] },\n    'flow-into': { values: ['none'], type: 'named-flow' },\n    'flow-from': { values: ['none'], type: 'named-flow' },\n    font: { values: [] },\n    'font-display': {\n      values: ['auto', 'block', 'swap', 'fallback', 'optional'],\n    },\n    'font-family': {\n      values: [\n        'auto',\n        'cursive',\n        'fantasy',\n        'monospace',\n        'sans-serif',\n        'serif',\n      ],\n    },\n    'font-feature-settings': { values: ['normal'] },\n    'font-kerning': { values: ['auto', 'none', 'normal'] },\n    'font-language-override': { values: ['normal'] },\n    'font-size': {\n      values: [\n        'xx-small',\n        'x-small',\n        'small',\n        'medium',\n        'large',\n        'x-large',\n        'xx-large',\n        'xxx-large',\n        'larger',\n        'smaller',\n      ],\n    },\n    'font-size-adjust': { values: ['auto', 'none'] },\n    'font-stretch': {\n      values: [\n        'condensed',\n        'expanded',\n        'extra-condensed',\n        'extra-expanded',\n        'normal',\n        'semi-condensed',\n        'semi-expanded',\n        'ultra-condensed',\n        'ultra-expanded',\n      ],\n    },\n    'font-style': { values: ['italic', 'normal', 'oblique'] },\n    'font-synthesis': { values: ['none', 'style', 'weight'] },\n    'font-variant': { values: ['normal', 'small-caps'] },\n    'font-variant-alternates': { values: ['normal'] },\n    'font-variant-caps': {\n      values: [\n        'normal',\n        'small-caps',\n        'all-small-caps',\n        'petite-caps',\n        'all-petite-caps',\n        'unicase',\n        'titling-caps',\n      ],\n    },\n    'font-variant-east-asian': { values: ['normal'] },\n    'font-variant-ligatures': { values: ['normal', 'none'] },\n    'font-variant-numeric': { values: ['normal'] },\n    'font-variant-position': { values: ['normal', 'sub', 'super'] },\n    'font-weight': {\n      values: [\n        'bold',\n        'bolder',\n        'lighter',\n        'normal',\n        '100',\n        '200',\n        '300',\n        '400',\n        '500',\n        '600',\n        '700',\n        '800',\n        '900',\n      ],\n    },\n    gap: { values: ['revert-layer'] },\n    grid: { values: [] },\n    'grid-area': { values: [] },\n    'grid-auto-columns': { values: [] },\n    'grid-auto-flow': { values: ['row', 'column', 'dense'] },\n    'grid-auto-rows': { values: [] },\n    'grid-column': { values: ['auto'] },\n    'grid-column-end': { values: [] },\n    'grid-column-gap': { values: [] },\n    'grid-column-start': { values: [] },\n    'grid-gap': { values: [] },\n    'grid-row': { values: ['auto'] },\n    'grid-row-end': { values: [] },\n    'grid-row-start': { values: [] },\n    'grid-row-gap': { values: [] },\n    'grid-template': { values: ['none'] },\n    'grid-template-areas': { values: [] },\n    'grid-template-columns': { values: ['auto'] },\n    'grid-template-rows': { values: ['auto'] },\n    'hanging-punctuation': {\n      values: ['allow-end', 'first', 'force-end', 'last', 'none'],\n    },\n    height: { values: ['auto', 'max-content', 'min-content', 'fit-content'] },\n    hyphens: { values: ['auto', 'manual', 'none'] },\n    'image-orientation': { values: [] },\n    'image-resolution': { values: ['from-image', 'snap'] },\n    isolation: { values: ['auto', 'isolate'] },\n    'justify-content': {\n      values: [\n        'center',\n        'flex-end',\n        'flex-start',\n        'space-around',\n        'space-between',\n      ],\n    },\n    'justify-items': {\n      values: [\n        'auto',\n        'normal',\n        'stretch',\n        'center',\n        'start',\n        'end',\n        'flex-start',\n        'flex-end',\n        'self-start',\n        'self-end',\n        'left',\n        'right',\n        'baseline',\n        'first',\n        'last',\n        'safe',\n        'unsafe',\n        'legacy',\n      ],\n    },\n    'justify-self': {\n      values: [\n        'auto',\n        'normal',\n        'stretch',\n        'center',\n        'start',\n        'end',\n        'flex-start',\n        'flex-end',\n        'self-start',\n        'self-end',\n        'left',\n        'right',\n        'baseline',\n        'first',\n        'last',\n        'safe',\n        'unsafe',\n      ],\n    },\n    left: { values: ['auto'] },\n    'letter-spacing': { values: ['normal'] },\n    'line-height': { values: ['normal'] },\n    'list-style': {\n      values: [\n        'none',\n        'url()',\n        'armenian',\n        'circle',\n        'decimal',\n        'decimal-leading-zero',\n        'disc',\n        'georgian',\n        'inside',\n        'lower-alpha',\n        'lower-greek',\n        'lower-latin',\n        'lower-roman',\n        'outside',\n        'square',\n        'upper-alpha',\n        'upper-latin',\n        'upper-roman',\n      ],\n    },\n    'list-style-image': { values: ['none', 'url()'] },\n    'list-style-position': { values: ['inside', 'outside'] },\n    'list-style-type': {\n      values: [\n        'armenian',\n        'circle',\n        'decimal',\n        'decimal-leading-zero',\n        'disc',\n        'georgian',\n        'lower-alpha',\n        'lower-greek',\n        'lower-latin',\n        'lower-roman',\n        'none',\n        'square',\n        'upper-alpha',\n        'upper-latin',\n        'upper-roman',\n      ],\n    },\n    margin: { values: ['auto'] },\n    'margin-bottom': { values: ['auto'] },\n    'margin-left': { values: ['auto'] },\n    'margin-right': { values: ['auto'] },\n    'margin-top': { values: ['auto'] },\n    'max-height': { values: ['none'] },\n    'max-width': { values: ['none'] },\n    'min-height': { values: [] },\n    'min-width': { values: [] },\n    'mix-blend-mode': {\n      values: [\n        'color',\n        'color-burn',\n        'color-dodge',\n        'darken',\n        'difference',\n        'exclusion',\n        'hard-light',\n        'hue',\n        'lighten',\n        'luminosity',\n        'multiply',\n        'normal',\n        'overlay',\n        'saturation',\n        'screen',\n        'soft-light',\n      ],\n    },\n    'object-fit': {\n      values: ['contain', 'cover', 'fill', 'none', 'scale-down'],\n    },\n    'object-position': { values: ['left', 'center', 'right', 'bottom', 'top'] },\n    opacity: { values: [] },\n    order: { values: [] },\n    orphans: { values: [] },\n    outline: { values: [] },\n    'outline-color': { values: ['invert'], type: 'color' },\n    'outline-offset': { values: [] },\n    'outline-style': {\n      values: [\n        'dashed',\n        'dotted',\n        'double',\n        'groove',\n        'hidden',\n        'inset',\n        'none',\n        'outset',\n        'ridge',\n        'solid',\n      ],\n    },\n    'outline-width': { values: ['medium', 'thin', 'thick'] },\n    overflow: { values: ['auto', 'hidden', 'scroll', 'visible'] },\n    'overflow-x': { values: ['auto', 'hidden', 'scroll', 'visible'] },\n    'overflow-y': { values: ['auto', 'hidden', 'scroll', 'visible'] },\n    'overflow-wrap': { values: ['normal', 'anywhere', 'break-word'] },\n    padding: { values: [] },\n    'padding-bottom': { values: [] },\n    'padding-left': { values: [] },\n    'padding-right': { values: [] },\n    'padding-top': { values: [] },\n    'page-break-after': {\n      values: ['always', 'auto', 'avoid', 'left', 'right'],\n    },\n    'page-break-before': {\n      values: ['always', 'auto', 'avoid', 'left', 'right'],\n    },\n    'page-break-inside': { values: ['auto', 'avoid'] },\n    perspective: { values: ['none'] },\n    'perspective-origin': {\n      values: ['bottom', 'center', 'left', 'right', 'top'],\n    },\n    'pointer-events': {\n      values: [\n        'all',\n        'auto',\n        'fill',\n        'none',\n        'painted',\n        'stroke',\n        'visible',\n        'visibleFill',\n        'visiblePainted',\n        'visibleStroke',\n      ],\n    },\n    position: { values: ['absolute', 'fixed', 'relative', 'static', 'sticky'] },\n    quotes: { values: ['none'] },\n    'region-break-after': {\n      values: [\n        'always',\n        'auto',\n        'avoid',\n        'avoid-column',\n        'avoid-page',\n        'avoid-region',\n        'column',\n        'left',\n        'page',\n        'region',\n        'right',\n      ],\n    },\n    'region-break-before': {\n      values: [\n        'always',\n        'auto',\n        'avoid',\n        'avoid-column',\n        'avoid-page',\n        'avoid-region',\n        'column',\n        'left',\n        'page',\n        'region',\n        'right',\n      ],\n    },\n    'region-break-inside': {\n      values: ['auto', 'avoid', 'avoid-column', 'avoid-page', 'avoid-region'],\n    },\n    'region-fragment': { values: ['auto', 'break'] },\n    resize: { values: ['both', 'horizontal', 'none', 'vertical'] },\n    right: { values: ['auto'] },\n    'scroll-behavior': { values: ['auto', 'smooth'] },\n    'scroll-snap-type': {\n      values: [\n        'none',\n        'x',\n        'y',\n        'block',\n        'inline',\n        'both',\n        'mandatory',\n        'proximity',\n      ],\n    },\n    src: { values: ['url()'] },\n    'shape-image-threshold': { values: [] },\n    'shape-inside': {\n      values: [\n        'auto',\n        'circle()',\n        'ellipse()',\n        'outside-shape',\n        'polygon()',\n        'rectangle()',\n      ],\n    },\n    'shape-margin': { values: [] },\n    'shape-outside': {\n      values: [\n        'none',\n        'circle()',\n        'ellipse()',\n        'polygon()',\n        'inset()',\n        'margin-box',\n        'border-box',\n        'padding-box',\n        'content-box',\n        'url()',\n        'image()',\n        'linear-gradient()',\n        'radial-gradient()',\n        'repeating-linear-gradient()',\n        'repeating-radial-gradient()',\n      ],\n    },\n    'tab-size': { values: [] },\n    'table-layout': { values: ['auto', 'fixed'] },\n    'text-align': {\n      values: [\n        'start',\n        'end',\n        'center',\n        'left',\n        'justify',\n        'right',\n        'match-parent',\n        'justify-all',\n      ],\n    },\n    'text-align-last': { values: ['center', 'left', 'justify', 'right'] },\n    'text-decoration': {\n      values: ['line-through', 'none', 'overline', 'underline'],\n    },\n    'text-decoration-color': { values: [], type: 'color' },\n    'text-decoration-line': {\n      values: ['line-through', 'none', 'overline', 'underline'],\n    },\n    'text-decoration-skip': {\n      values: ['edges', 'ink', 'none', 'objects', 'spaces'],\n    },\n    'text-decoration-style': {\n      values: ['dashed', 'dotted', 'double', 'solid', 'wavy'],\n    },\n    'text-emphasis': { values: [] },\n    'text-emphasis-color': { values: [], type: 'color' },\n    'text-emphasis-position': { values: ['above', 'below', 'left', 'right'] },\n    'text-emphasis-style': {\n      values: [\n        'circle',\n        'dot',\n        'double-circle',\n        'filled',\n        'none',\n        'open',\n        'sesame',\n        'triangle',\n      ],\n    },\n    'text-indent': { values: [] },\n    'text-justify': {\n      values: ['auto', 'none', 'inter-word', 'inter-character'],\n    },\n    'text-overflow': { values: ['clip', 'ellipsis'] },\n    'text-shadow': { values: [] },\n    'text-rendering': {\n      values: [\n        'auto',\n        'geometricPrecision',\n        'optimizeLegibility',\n        'optimizeSpeed',\n      ],\n    },\n    'text-transform': {\n      values: ['capitalize', 'full-width', 'lowercase', 'none', 'uppercase'],\n    },\n    'text-underline-position': {\n      values: ['alphabetic', 'auto', 'below', 'left', 'right'],\n    },\n    top: { values: ['auto'] },\n    transform: {\n      values: [\n        'matrix()',\n        'matrix3d()',\n        'none',\n        'perspective()',\n        'rotate()',\n        'rotate3d()',\n        'rotateX()',\n        'rotateY()',\n        'rotateZ()',\n        'scale()',\n        'scale3d()',\n        'scaleX()',\n        'scaleY()',\n        'scaleZ()',\n        'skewX()',\n        'skewY()',\n        'translate()',\n        'translate3d()',\n        'translateX()',\n        'translateY()',\n        'translateZ()',\n      ],\n    },\n    'transform-origin': {\n      values: ['bottom', 'center', 'left', 'right', 'top'],\n    },\n    'transform-style': { values: ['flat', 'preserve-3d'] },\n    transition: { values: [] },\n    'transition-delay': { values: [] },\n    'transition-duration': { values: [] },\n    'transition-property': { values: ['all', 'none'] },\n    'transition-timing-function': {\n      values: [\n        'cubic-bezier()',\n        'ease',\n        'ease-in',\n        'ease-in-out',\n        'ease-out',\n        'linear',\n        'step-end',\n        'step-start',\n        'steps()',\n      ],\n    },\n    'unicode-bidi': { values: ['bidi-override', 'embed', 'normal'] },\n    'unicode-range': { values: [] },\n    'user-select': { values: ['all', 'auto', 'contain', 'none', 'text'] },\n    'vertical-align': {\n      values: [\n        'baseline',\n        'bottom',\n        'middle',\n        'sub',\n        'super',\n        'text-bottom',\n        'text-top',\n        'top',\n      ],\n    },\n    visibility: { values: ['collapse', 'hidden', 'visible'] },\n    'white-space': {\n      values: ['normal', 'nowrap', 'pre', 'pre-line', 'pre-wrap'],\n    },\n    widows: { values: [] },\n    width: { values: ['auto', 'max-content', 'min-content', 'fit-content'] },\n    'will-change': {\n      values: ['auto', 'contents', 'opacity', 'scroll-position'],\n    },\n    'word-break': { values: ['normal', 'break-all', 'keep-all'] },\n    'word-spacing': { values: ['normal'] },\n    'z-index': { values: ['auto'] },\n  },\n};\n\nexport const CSSProperties = CSSSourceInfo.cssKeyValue;\n\nexport const CSSColors = CSSSourceInfo.cssColors;\n\nexport type CSSPropertiesKey = keyof typeof CSSProperties;\n\nexport const CSSPropertyList = Object.keys(CSSSourceInfo.cssKeyValue);\n"
  },
  {
    "path": "packages/engine/src/component/CSSPropertiesEditor/index.tsx",
    "content": "import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';\nimport { CSSPropertyList } from './cssProperties';\nimport styles from './style.module.scss';\n\nimport { ConfigProvider } from 'antd';\nimport { SinglePropertyEditorRef, SinglePropertyEditor } from './signleProperty';\n\n// eslint-disable-next-line react-refresh/only-export-components\nexport const defaultPropertyOptions = CSSPropertyList.map((el) => {\n  return {\n    value: el,\n  };\n});\n\nexport type CSSPropertiesEditorProps = {\n  initialValue?: { property: string; value: string }[];\n  onValueChange?: (val: { property: string; value: string }[]) => void;\n};\n\nexport type CSSPropertiesEditorRef = {\n  setValue: (val: { property: string; value: string }[]) => void;\n};\n\nexport const CSSPropertiesEditor = forwardRef<CSSPropertiesEditorRef, CSSPropertiesEditorProps>(\n  function CSSPropertiesEditorCore(props, ref) {\n    const [propertyList, setPropertyList] = useState<{ id?: string; property: string; value: any }[]>([]);\n    const [newAddVal, setNewAddVal] = useState({\n      property: '',\n      value: '',\n    });\n    useImperativeHandle(\n      ref,\n      () => {\n        return {\n          setValue: (val) => {\n            setPropertyList(val);\n          },\n        };\n      },\n      []\n    );\n\n    useEffect(() => {\n      if (props.initialValue) {\n        setPropertyList([...props.initialValue]);\n      }\n      // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, []);\n\n    const innerOnValueChange = (val: typeof propertyList) => {\n      props.onValueChange?.(val);\n    };\n\n    const createPropertyRef = useRef<SinglePropertyEditorRef>(null);\n    return (\n      <ConfigProvider\n        theme={{\n          token: {\n            borderRadius: 4,\n          },\n        }}\n      >\n        <div className={styles.cssBox}>\n          {propertyList.map((el, index) => {\n            return (\n              <div key={index}>\n                <SinglePropertyEditor\n                  mode=\"edit\"\n                  allValues={propertyList}\n                  value={el}\n                  onValueChange={(newVal) => {\n                    propertyList[index] = newVal;\n                    const newList = JSON.parse(JSON.stringify(propertyList));\n                    setPropertyList(newList);\n                    innerOnValueChange(newList);\n                  }}\n                  onDelete={() => {\n                    propertyList.splice(index, 1);\n                    const newList = JSON.parse(JSON.stringify(propertyList));\n                    setPropertyList(newList);\n                    innerOnValueChange(newList);\n                  }}\n                />\n              </div>\n            );\n          })}\n          <SinglePropertyEditor\n            key=\"newInput\"\n            allValues={propertyList}\n            mode=\"create\"\n            value={newAddVal}\n            ref={createPropertyRef}\n            onValueChange={(newCssVal) => {\n              setNewAddVal({\n                ...newCssVal,\n              });\n            }}\n            onCreate={(newVal) => {\n              if (newVal.property && newVal.value) {\n                const newList = [...propertyList];\n                newList.push(newVal);\n                setPropertyList(newList);\n                setNewAddVal({\n                  property: '',\n                  value: '',\n                });\n                innerOnValueChange(newList);\n                createPropertyRef.current?.reset();\n              }\n            }}\n          />\n        </div>\n      </ConfigProvider>\n    );\n  }\n);\n"
  },
  {
    "path": "packages/engine/src/component/CSSPropertiesEditor/signleProperty.tsx",
    "content": "import { MinusOutlined, PlusOutlined } from '@ant-design/icons';\nimport { AutoComplete, Button } from 'antd';\nimport { InputStatus } from 'antd/es/_util/statusUtils';\nimport clsx from 'clsx';\nimport { BaseSelectRef } from 'rc-select';\nimport { forwardRef, useState, useMemo, useRef, useImperativeHandle } from 'react';\nimport { defaultPropertyOptions } from '.';\nimport { CSSProperties, CSSPropertiesKey } from './cssProperties';\nimport styles from './style.module.scss';\n\nexport type SinglePropertyEditorProps = {\n  mode: 'create' | 'edit';\n  value?: {\n    property: string;\n    value: string;\n  };\n  allValues: {\n    property: string;\n    value: string;\n  }[];\n  onValueChange?: (value: { property: string; value: string }) => void;\n  onCreate?: (value: { property: string; value: string }) => void;\n  onDelete?: () => void;\n};\n\nexport type SinglePropertyEditorRef = {\n  reset: () => void;\n};\n\nexport const SinglePropertyEditor = forwardRef<SinglePropertyEditorRef, SinglePropertyEditorProps>(\n  function SinglePropertyEditorCore(props, ref) {\n    const [keyFormatStatus] = useState<InputStatus>('');\n    const [valueFormatStatus] = useState<InputStatus>('');\n    const { mode = 'edit' } = props;\n    const isCreate = useMemo(() => {\n      return mode === 'create';\n    }, [mode]);\n    const innerValue = props.value;\n    const [propertyOptions, setPropertyOptions] = useState(defaultPropertyOptions);\n\n    const [valueOptions, setValueOptions] = useState<{ value: string }[]>([]);\n    const [allValueOptions, setAllValueOptions] = useState<{ value: string }[]>([]);\n\n    const onSearch = (searchText: string) => {\n      const newOptions = defaultPropertyOptions.filter((el) => el.value.includes(searchText));\n      if (!searchText) {\n        setPropertyOptions(defaultPropertyOptions);\n      } else {\n        setPropertyOptions(newOptions);\n      }\n    };\n\n    const updateValueOptions = () => {\n      let res: any[] = [];\n      const tempProperty = CSSProperties[innerValue?.property as unknown as CSSPropertiesKey];\n\n      if (tempProperty) {\n        res =\n          tempProperty.values?.map((val) => {\n            return {\n              value: val,\n            };\n          }) || [];\n      }\n\n      setValueOptions(res);\n      setAllValueOptions(res);\n    };\n\n    const onValueSearch = (searchText: string) => {\n      const newOptions = allValueOptions.filter((el) => el.value.includes(searchText));\n      if (!searchText) {\n        setValueOptions(allValueOptions);\n      } else {\n        setValueOptions(newOptions);\n      }\n    };\n\n    const updateKeyValue = (keyVal: string) => {\n      props.onValueChange?.({\n        property: keyVal,\n        value: innerValue?.value || '',\n      });\n      return true;\n    };\n\n    const propertyValueRef = useRef<BaseSelectRef | null>(null);\n    const propertyKeyRef = useRef<BaseSelectRef | null>(null);\n    const [focusState, setFocusState] = useState({\n      key: false,\n      value: false,\n    });\n    useImperativeHandle(\n      ref,\n      () => {\n        return {\n          reset: () => {\n            propertyKeyRef.current?.focus();\n          },\n        };\n      },\n      []\n    );\n\n    return (\n      <div className={styles.cssFieldBox}>\n        <div\n          className={clsx([\n            styles.keyField,\n            isCreate && styles.inputAuto,\n            isCreate && focusState.key && styles.active,\n            keyFormatStatus === 'error' && styles.error,\n            !isCreate && styles.notEdit,\n          ])}\n        >\n          <AutoComplete\n            ref={propertyKeyRef}\n            variant={'borderless'}\n            disabled={!isCreate}\n            onSearch={onSearch}\n            status={keyFormatStatus}\n            popupMatchSelectWidth={200}\n            value={innerValue?.property}\n            onChange={(val) => {\n              updateKeyValue(val);\n            }}\n            style={{\n              width: '100%',\n              position: 'absolute',\n              left: 0,\n              top: 0,\n            }}\n            onFocus={() => {\n              setFocusState({\n                key: true,\n                value: false,\n              });\n            }}\n            onBlur={() => {\n              setFocusState({\n                key: false,\n                value: false,\n              });\n            }}\n            onKeyDown={(e) => {\n              if (e.code === 'Enter') {\n                if (!keyFormatStatus) {\n                  propertyValueRef.current?.focus();\n                }\n              }\n            }}\n            placeholder=\"property\"\n            options={propertyOptions}\n          />\n          <span\n            style={{\n              display: 'inline-block',\n              visibility: 'hidden',\n              minWidth: '60px',\n              padding: '0 2px',\n            }}\n          >\n            {innerValue?.property}\n          </span>\n        </div>\n        <span\n          style={{\n            padding: '0 2px',\n          }}\n        >\n          :\n        </span>\n        <AutoComplete\n          variant={'borderless'}\n          ref={propertyValueRef}\n          status={valueFormatStatus}\n          popupMatchSelectWidth={200}\n          value={innerValue?.value}\n          onChange={(val) => {\n            updateValueOptions();\n            props.onValueChange?.({\n              property: innerValue?.property || '',\n              value: val || '',\n            });\n          }}\n          style={{\n            flex: 1,\n          }}\n          onFocus={() => {\n            setFocusState({\n              key: false,\n              value: true,\n            });\n          }}\n          onBlur={() => {\n            setFocusState({\n              key: false,\n              value: false,\n            });\n          }}\n          className={clsx([styles.inputAuto, focusState.value && styles.active])}\n          placeholder=\"value\"\n          onSearch={onValueSearch}\n          options={valueOptions}\n          onKeyDown={(e) => {\n            if (e.code === 'Enter') {\n              if (isCreate) {\n                props.onCreate?.({\n                  property: innerValue?.property || '',\n                  value: innerValue?.value || '',\n                });\n              }\n            }\n          }}\n        ></AutoComplete>\n\n        {props.onDelete && mode === 'edit' && (\n          <Button\n            size=\"small\"\n            type=\"text\"\n            onClick={() => {\n              props.onDelete?.();\n            }}\n          >\n            <MinusOutlined\n              style={{\n                display: 'inline-flex',\n                fontSize: '12px',\n              }}\n            />\n          </Button>\n        )}\n        {props.onCreate && mode === 'create' && (\n          <div>\n            <Button\n              size=\"small\"\n              type=\"text\"\n              icon={\n                <PlusOutlined\n                  style={{\n                    fontSize: '12px',\n                    display: 'inline-flex',\n                  }}\n                />\n              }\n              onClick={() => {\n                props.onCreate?.({\n                  property: innerValue?.property || '',\n                  value: innerValue?.value || '',\n                });\n              }}\n            ></Button>\n          </div>\n        )}\n      </div>\n    );\n  }\n);\n"
  },
  {
    "path": "packages/engine/src/component/CSSPropertiesEditor/style.module.scss",
    "content": ".cssFieldBox {\n  display: flex;\n  align-items: center;\n  color: $textColor !important;\n  :global {\n    .ant-select-selector {\n      padding: 0 !important;\n    }\n\n    .ant-select-single .ant-select-selector .ant-select-selection-search {\n      inset-inline-start: 0px;\n      inset-inline-end: 0px;\n    }\n\n    .ant-select-selector input {\n      color: $textColor !important;\n    }\n  }\n\n  .inputAuto {\n    border-bottom: 1px solid rgba(128, 128, 128, 0.23);\n    transition: all 0.1s;\n    height: 30px;\n\n    &.error {\n      border-bottom: 1px solid red !important;\n    }\n  }\n\n  .keyField {\n    position: relative;\n    font-size: 14px;\n    height: 30px;\n    &.notEdit {\n      border-bottom: 1px solid transparent !important;\n    }\n  }\n\n  .active {\n    border-bottom: 1px solid rgb(128, 177, 255);\n  }\n}\n\n.cssBox {\n  :global {\n    .ant-collapse-borderless > .ant-collapse-item {\n      border-bottom: 1px solid #e4e4e4;\n    }\n\n    .ant-collapse-borderless > .ant-collapse-item:last-child {\n      border-bottom: none;\n    }\n    .ant-collapse-content-box {\n      padding-bottom: 30px !important;\n    }\n  }\n}\n"
  },
  {
    "path": "packages/engine/src/component/CSSPropertiesEditor/util.ts",
    "content": "export const getTextWidth = async (text: string, fontSize = 14) => {\n  return new Promise<string>((resolve) => {\n    const span = document.createElement('span');\n    span.innerHTML = text;\n    span.style.fontSize = `${fontSize}px`;\n    span.style.position = 'fixed';\n    span.style.left = '-1000px';\n    span.style.bottom = '-1000px';\n    span.style.display = 'inline-block';\n    document.body.appendChild(span);\n    setTimeout(() => {\n      const style = window.getComputedStyle(span);\n      resolve(style.width);\n      setTimeout(() => {\n        document.body.removeChild(span);\n      });\n    });\n  });\n};\n"
  },
  {
    "path": "packages/engine/src/component/CSSPropertiesVariableBindEditor/SingleProperty.tsx",
    "content": "import { AutoComplete, Button, ConfigProvider } from 'antd';\nimport { MinusOutlined, PlusOutlined } from '@ant-design/icons';\nimport { BaseSelectRef } from 'rc-select';\nimport clsx from 'clsx';\nimport { CNodePropsTypeEnum, JSExpressionPropType } from '@chamn/model';\nimport { InputStatus } from 'antd/es/_util/statusUtils';\nimport { CSSPropertyList } from './cssProperties';\nimport { forwardRef, useState, useEffect, useRef, useImperativeHandle } from 'react';\nimport { ExpressionSetter } from '../CustomSchemaForm/components/Setters/ExpressionSetter';\nimport { UseCRightPanelContext } from '@/plugins/RightPanel/context';\nimport styles from './style.module.scss';\n\nconst defaultPropertyOptions = CSSPropertyList.map((el) => {\n  return {\n    value: el,\n  };\n});\n\nexport type InnerSinglePropertyEditorProps = {\n  value?: {\n    property: string;\n    value: JSExpressionPropType | string;\n  };\n  onValueChange: (value: { property: string; value: JSExpressionPropType | string }) => void;\n  onDelete?: () => void;\n  onCreate?: (value: { property: string; value: JSExpressionPropType | string }) => {\n    errorKey?: string[];\n  } | void;\n  mod?: 'create' | 'edit';\n};\n\nexport type InnerSinglePropertyEditorRef = {\n  reset: () => void;\n};\n\nexport const SinglePropertyEditor = forwardRef<InnerSinglePropertyEditorRef, InnerSinglePropertyEditorProps>(\n  function SinglePropertyEditorCore(props, ref) {\n    const [keyFormatStatus, setKeyFormatStatus] = useState<InputStatus>('');\n    const rightPanelContext = UseCRightPanelContext();\n    const { mod = 'create' } = props;\n\n    const [innerValue, setInnerVal] = useState<{\n      property: string;\n      value: JSExpressionPropType | string;\n    }>({\n      property: props.value?.property || '',\n      value: props.value?.value || '',\n    });\n\n    useEffect(() => {\n      if (props.value) {\n        setInnerVal(props.value);\n      }\n    }, [props.value]);\n\n    const [propertyOptions, setPropertyOptions] = useState(defaultPropertyOptions);\n\n    const onSearch = (searchText: string) => {\n      const newOptions = defaultPropertyOptions.filter((el) => el.value.includes(searchText));\n      if (!searchText) {\n        setPropertyOptions(defaultPropertyOptions);\n      } else {\n        setPropertyOptions(newOptions);\n      }\n    };\n\n    const updateOuterValue = (newValue: typeof innerValue) => {\n      props.onValueChange({\n        ...newValue,\n      });\n    };\n\n    const propertyKeyRef = useRef<BaseSelectRef | null>(null);\n\n    useImperativeHandle(\n      ref,\n      () => {\n        return {\n          reset: () => {\n            setInnerVal({\n              property: '',\n              value: {\n                type: CNodePropsTypeEnum.EXPRESSION,\n                value: '',\n              },\n            });\n            propertyKeyRef.current?.focus();\n          },\n        };\n      },\n      []\n    );\n\n    /// 创建时\n    const innerOnCreate = () => {\n      if (innerValue.property === '') {\n        setKeyFormatStatus('error');\n        return;\n      }\n\n      setKeyFormatStatus('');\n      const res = props.onCreate?.(innerValue);\n      if (res?.errorKey?.includes('key')) {\n        setKeyFormatStatus('error');\n      }\n    };\n\n    return (\n      <>\n        <div className={styles.cssFieldBox}>\n          <div className={styles.leftBox}>\n            <div className={styles.row}>\n              <span className={styles.fieldLabel}>Name</span>\n              <AutoComplete\n                status={keyFormatStatus}\n                disabled={mod === 'edit'}\n                ref={propertyKeyRef}\n                onSearch={onSearch}\n                popupMatchSelectWidth={200}\n                value={innerValue.property}\n                onChange={(val) => {\n                  setKeyFormatStatus('');\n                  const newVal = {\n                    ...innerValue,\n                    property: val,\n                  };\n                  setInnerVal(newVal);\n                  updateOuterValue(newVal);\n                }}\n                className={clsx([styles.inputBox])}\n                onBlur={() => {\n                  updateOuterValue(innerValue);\n                }}\n                placeholder=\"property\"\n                options={propertyOptions}\n              />\n            </div>\n            {mod === 'create' && (\n              <div>\n                <Button\n                  size=\"small\"\n                  block\n                  icon={\n                    <PlusOutlined\n                      style={{\n                        fontSize: '12px',\n                        display: 'inline-flex',\n                      }}\n                    />\n                  }\n                  onClick={innerOnCreate}\n                ></Button>\n              </div>\n            )}\n            {mod !== 'create' && (\n              <div className={styles.row}>\n                <span className={styles.fieldLabel}>Value</span>\n                <ConfigProvider\n                  theme={{\n                    token: {\n                      borderRadius: 4,\n                    },\n                  }}\n                >\n                  <div\n                    style={{\n                      width: '100%',\n                    }}\n                  >\n                    <ExpressionSetter\n                      value={innerValue.value as any}\n                      setterContext={{\n                        pluginCtx: rightPanelContext.pluginCtx!,\n                        setCollapseHeaderExt: undefined,\n                        onSetterChange: function () {},\n                        keyPaths: [],\n                        label: '',\n                        nodeModel: rightPanelContext.nodeModel as any,\n                      }}\n                      onValueChange={function (val) {\n                        const newVal = {\n                          ...innerValue,\n                          value: val,\n                        };\n                        console.log('newVal', newVal);\n                        setInnerVal(newVal);\n                        updateOuterValue(newVal);\n                      }}\n                      mode={'modal'}\n                    />\n                  </div>\n                </ConfigProvider>\n              </div>\n            )}\n          </div>\n          <div className={styles.rightBox}>\n            <div\n              style={{\n                height: '30px',\n              }}\n            >\n              {props.onDelete && mod === 'edit' && (\n                <Button\n                  size=\"small\"\n                  type=\"text\"\n                  onClick={() => {\n                    props.onDelete?.();\n                  }}\n                >\n                  <MinusOutlined\n                    style={{\n                      display: 'inline-flex',\n                      fontSize: '12px',\n                    }}\n                  />\n                </Button>\n              )}\n            </div>\n          </div>\n        </div>\n      </>\n    );\n  }\n);\n"
  },
  {
    "path": "packages/engine/src/component/CSSPropertiesVariableBindEditor/cssProperties.ts",
    "content": "const CSSSourceInfo = {\n  cssColors: {\n    aliceblue: '#f0f8ff',\n    antiquewhite: '#faebd7',\n    aqua: '#00ffff',\n    aquamarine: '#7fffd4',\n    azure: '#f0ffff',\n    beige: '#f5f5dc',\n    bisque: '#ffe4c4',\n    black: '#000000',\n    blanchedalmond: '#ffebcd',\n    blue: '#0000ff',\n    blueviolet: '#8a2be2',\n    brown: '#a52a2a',\n    burlywood: '#deb887',\n    cadetblue: '#5f9ea0',\n    chartreuse: '#7fff00',\n    chocolate: '#d2691e',\n    coral: '#ff7f50',\n    cornflowerblue: '#6495ed',\n    cornsilk: '#fff8dc',\n    crimson: '#dc143c',\n    cyan: '#00ffff',\n    darkblue: '#00008b',\n    darkcyan: '#008b8b',\n    darkgoldenrod: '#b8860b',\n    darkgray: '#a9a9a9',\n    darkgreen: '#006400',\n    darkgrey: '#a9a9a9',\n    darkkhaki: '#bdb76b',\n    darkmagenta: '#8b008b',\n    darkolivegreen: '#556b2f',\n    darkorange: '#ff8c00',\n    darkorchid: '#9932cc',\n    darkred: '#8b0000',\n    darksalmon: '#e9967a',\n    darkseagreen: '#8fbc8f',\n    darkslateblue: '#483d8b',\n    darkslategray: '#2f4f4f',\n    darkslategrey: '#2f4f4f',\n    darkturquoise: '#00ced1',\n    darkviolet: '#9400d3',\n    deeppink: '#ff1493',\n    deepskyblue: '#00bfff',\n    dimgray: '#696969',\n    dimgrey: '#696969',\n    dodgerblue: '#1e90ff',\n    firebrick: '#b22222',\n    floralwhite: '#fffaf0',\n    forestgreen: '#228b22',\n    fuchsia: '#ff00ff',\n    gainsboro: '#dcdcdc',\n    ghostwhite: '#f8f8ff',\n    goldenrod: '#daa520',\n    gold: '#ffd700',\n    gray: '#808080',\n    green: '#008000',\n    greenyellow: '#adff2f',\n    grey: '#808080',\n    honeydew: '#f0fff0',\n    hotpink: '#ff69b4',\n    indianred: '#cd5c5c',\n    indigo: '#4b0082',\n    ivory: '#fffff0',\n    khaki: '#f0e68c',\n    lavenderblush: '#fff0f5',\n    lavender: '#e6e6fa',\n    lawngreen: '#7cfc00',\n    lemonchiffon: '#fffacd',\n    lightblue: '#add8e6',\n    lightcoral: '#f08080',\n    lightcyan: '#e0ffff',\n    lightgoldenrodyellow: '#fafad2',\n    lightgray: '#d3d3d3',\n    lightgreen: '#90ee90',\n    lightgrey: '#d3d3d3',\n    lightpink: '#ffb6c1',\n    lightsalmon: '#ffa07a',\n    lightseagreen: '#20b2aa',\n    lightskyblue: '#87cefa',\n    lightslategray: '#778899',\n    lightslategrey: '#778899',\n    lightsteelblue: '#b0c4de',\n    lightyellow: '#ffffe0',\n    lime: '#00ff00',\n    limegreen: '#32cd32',\n    linen: '#faf0e6',\n    magenta: '#ff00ff',\n    maroon: '#800000',\n    mediumaquamarine: '#66cdaa',\n    mediumblue: '#0000cd',\n    mediumorchid: '#ba55d3',\n    mediumpurple: '#9370db',\n    mediumseagreen: '#3cb371',\n    mediumslateblue: '#7b68ee',\n    mediumspringgreen: '#00fa9a',\n    mediumturquoise: '#48d1cc',\n    mediumvioletred: '#c71585',\n    midnightblue: '#191970',\n    mintcream: '#f5fffa',\n    mistyrose: '#ffe4e1',\n    moccasin: '#ffe4b5',\n    navajowhite: '#ffdead',\n    navy: '#000080',\n    oldlace: '#fdf5e6',\n    olive: '#808000',\n    olivedrab: '#6b8e23',\n    orange: '#ffa500',\n    orangered: '#ff4500',\n    orchid: '#da70d6',\n    palegoldenrod: '#eee8aa',\n    palegreen: '#98fb98',\n    paleturquoise: '#afeeee',\n    palevioletred: '#db7093',\n    papayawhip: '#ffefd5',\n    peachpuff: '#ffdab9',\n    peru: '#cd853f',\n    pink: '#ffc0cb',\n    plum: '#dda0dd',\n    powderblue: '#b0e0e6',\n    purple: '#800080',\n    rebeccapurple: '#663399',\n    red: '#ff0000',\n    rosybrown: '#bc8f8f',\n    royalblue: '#4169e1',\n    saddlebrown: '#8b4513',\n    salmon: '#fa8072',\n    sandybrown: '#f4a460',\n    seagreen: '#2e8b57',\n    seashell: '#fff5ee',\n    sienna: '#a0522d',\n    silver: '#c0c0c0',\n    skyblue: '#87ceeb',\n    slateblue: '#6a5acd',\n    slategray: '#708090',\n    slategrey: '#708090',\n    snow: '#fffafa',\n    springgreen: '#00ff7f',\n    steelblue: '#4682b4',\n    tan: '#d2b48c',\n    teal: '#008080',\n    thistle: '#d8bfd8',\n    tomato: '#ff6347',\n    turquoise: '#40e0d0',\n    violet: '#ee82ee',\n    wheat: '#f5deb3',\n    white: '#ffffff',\n    whitesmoke: '#f5f5f5',\n    yellow: '#ffff00',\n    yellowgreen: '#9acd32',\n  },\n  cssKeyValue: {\n    'align-content': {\n      values: ['center', 'flex-end', 'flex-start', 'space-around', 'space-between', 'stretch'],\n    },\n    'align-items': {\n      values: ['baseline', 'center', 'flex-end', 'flex-start', 'stretch'],\n    },\n    'align-self': {\n      values: [\n        'auto',\n        'normal',\n        'self-start',\n        'self-end',\n        'baseline',\n        'center',\n        'start',\n        'end',\n        'flex-end',\n        'flex-start',\n        'safe',\n        'stretch',\n        'unsafe',\n      ],\n    },\n    all: { values: [] },\n    animation: { values: [] },\n    'animation-delay': { values: [] },\n    'animation-direction': {\n      values: ['alternate', 'alternate-reverse', 'normal', 'reverse'],\n    },\n    'animation-duration': { values: [] },\n    'animation-fill-mode': {\n      values: ['backwards', 'both', 'forwards', 'none'],\n    },\n    'animation-iteration-count': { values: ['infinite'] },\n    'animation-name': { values: ['none'] },\n    'animation-play-state': { values: ['paused', 'running'] },\n    'animation-timing-function': {\n      values: ['cubic-bezier()', 'ease', 'ease-in', 'ease-in-out', 'ease-out', 'linear', 'step-end', 'step-start', 'steps()'],\n    },\n    'backface-visibility': { values: ['hidden', 'visible'] },\n    background: { values: [], type: 'color' },\n    'background-attachment': { values: ['fixed', 'local', 'scroll'] },\n    'background-blend-mode': {\n      values: [\n        'color',\n        'color-burn',\n        'color-dodge',\n        'darken',\n        'difference',\n        'exclusion',\n        'hard-light',\n        'hue',\n        'lighten',\n        'luminosity',\n        'multiply',\n        'normal',\n        'overlay',\n        'saturation',\n        'screen',\n        'soft-light',\n      ],\n    },\n    'background-clip': { values: ['border-box', 'content-box', 'padding-box'] },\n    'background-color': { values: [], type: 'color' },\n    'background-image': {\n      values: ['image()', 'linear-gradient()', 'radial-gradient()', 'repeating-linear-gradient()', 'repeating-radial-gradient()', 'url()'],\n    },\n    'background-origin': {\n      values: ['border-box', 'content-box', 'padding-box'],\n    },\n    'background-position': {\n      values: ['left', 'center', 'right', 'bottom', 'top'],\n    },\n    'background-repeat': {\n      values: ['no-repeat', 'repeat', 'repeat-x', 'repeat-y', 'round', 'space'],\n    },\n    'background-size': { values: ['auto', 'contain', 'cover'] },\n    border: { values: [] },\n    'border-collapse': { values: ['collapse', 'separate'] },\n    'border-color': { values: [], type: 'color' },\n    'border-spacing': { values: [] },\n    'border-style': {\n      values: ['dashed', 'dotted', 'double', 'groove', 'hidden', 'inset', 'none', 'outset', 'ridge', 'solid'],\n    },\n    'border-bottom': { values: [] },\n    'border-bottom-color': { values: [], type: 'color' },\n    'border-bottom-left-radius': { values: [] },\n    'border-bottom-right-radius': { values: [] },\n    'border-bottom-style': {\n      values: ['dashed', 'dotted', 'double', 'groove', 'hidden', 'inset', 'none', 'outset', 'ridge', 'solid'],\n    },\n    'border-bottom-width': { values: ['medium', 'thin', 'thick'] },\n    'border-image': { values: ['url()'] },\n    'border-image-outset': { values: [] },\n    'border-image-slice': { values: [] },\n    'border-image-source': { values: [] },\n    'border-image-repeat': { values: ['repeat', 'round', 'space', 'stretch'] },\n    'border-image-width': { values: ['auto'] },\n    'border-left': { values: [] },\n    'border-left-color': { values: [], type: 'color' },\n    'border-left-style': {\n      values: ['dashed', 'dotted', 'double', 'groove', 'hidden', 'inset', 'none', 'outset', 'ridge', 'solid'],\n    },\n    'border-left-width': { values: ['medium', 'thin', 'thick'] },\n    'border-radius': { values: [] },\n    'border-right': { values: [] },\n    'border-right-color': { values: [], type: 'color' },\n    'border-right-style': {\n      values: ['dashed', 'dotted', 'double', 'groove', 'hidden', 'inset', 'none', 'outset', 'ridge', 'solid'],\n    },\n    'border-right-width': { values: ['medium', 'thin', 'thick'] },\n    'border-top': { values: [] },\n    'border-top-color': { values: [], type: 'color' },\n    'border-top-left-radius': { values: [] },\n    'border-top-right-radius': { values: [] },\n    'border-top-style': {\n      values: ['dashed', 'dotted', 'double', 'groove', 'hidden', 'inset', 'none', 'outset', 'ridge', 'solid'],\n    },\n    'border-top-width': { values: ['medium', 'thin', 'thick'] },\n    'border-width': { values: ['medium', 'thin', 'thick'] },\n    'box-decoration-break': { values: ['clone', 'slice'] },\n    'box-shadow': { values: ['none'] },\n    'box-sizing': { values: ['border-box', 'content-box'] },\n    bottom: { values: ['auto'] },\n    'break-after': {\n      values: ['always', 'auto', 'avoid', 'avoid-column', 'avoid-page', 'avoid-region', 'column', 'left', 'page', 'region', 'right'],\n    },\n    'break-before': {\n      values: ['always', 'auto', 'avoid', 'avoid-column', 'avoid-page', 'avoid-region', 'column', 'left', 'page', 'region', 'right'],\n    },\n    'break-inside': {\n      values: ['auto', 'avoid', 'avoid-column', 'avoid-page', 'avoid-region'],\n    },\n    'caption-side': { values: ['bottom', 'top'] },\n    'caret-color': { values: ['auto'], type: 'color' },\n    clear: { values: ['both', 'left', 'none', 'right'] },\n    clip: { values: ['auto'] },\n    color: { values: [], type: 'color' },\n    columns: { values: [] },\n    'column-count': { values: [] },\n    'column-fill': { values: ['auto', 'balance'] },\n    'column-gap': { values: ['normal'] },\n    'column-rule': { values: [] },\n    'column-rule-color': { values: [], type: 'color' },\n    'column-rule-style': {\n      values: ['dashed', 'dotted', 'double', 'groove', 'hidden', 'inset', 'none', 'outset', 'ridge', 'solid'],\n    },\n    'column-rule-width': { values: ['medium', 'thin', 'thick'] },\n    'column-span': { values: ['all', 'none'] },\n    'column-width': { values: ['auto'] },\n    content: {\n      values: ['attr()', 'close-quote', 'no-close-quote', 'no-open-quote', 'normal', 'none', 'open-quote'],\n    },\n    'counter-increment': { values: ['none'] },\n    'counter-reset': { values: ['none'] },\n    cursor: {\n      values: [\n        'alias',\n        'all-scroll',\n        'auto',\n        'cell',\n        'col-resize',\n        'context-menu',\n        'copy',\n        'crosshair',\n        'default',\n        'e-resize',\n        'ew-resize',\n        'grab',\n        'grabbing',\n        'help',\n        'move',\n        'n-resize',\n        'ne-resize',\n        'nesw-resize',\n        'no-drop',\n        'none',\n        'not-allowed',\n        'ns-resize',\n        'nw-resize',\n        'nwse-resize',\n        'pointer',\n        'progress',\n        'row-resize',\n        's-resize',\n        'se-resize',\n        'sw-resize',\n        'text',\n        'vertical-text',\n        'w-resize',\n        'wait',\n        'zoom-in',\n        'zoom-out',\n      ],\n    },\n    direction: { values: ['ltr', 'rtl'] },\n    display: {\n      values: [\n        'block',\n        'contents',\n        'flex',\n        'flow-root',\n        'grid',\n        'inline',\n        'inline-block',\n        'inline-flex',\n        'inline-grid',\n        'inline-table',\n        'list-item',\n        'none',\n        'run-in',\n        'subgrid',\n        'table',\n        'table-caption',\n        'table-cell',\n        'table-column',\n        'table-column-group',\n        'table-footer-group',\n        'table-header-group',\n        'table-row',\n        'table-row-group',\n      ],\n    },\n    'empty-cells': { values: ['hide', 'show'] },\n    fill: { values: [] },\n    filter: {\n      values: [\n        'blur()',\n        'brightness()',\n        'contrast()',\n        'custom()',\n        'drop-shadow()',\n        'grayscale()',\n        'hue-rotate()',\n        'invert()',\n        'none',\n        'opacity()',\n        'sepia()',\n        'saturate()',\n        'url()',\n      ],\n    },\n    flex: { values: ['auto', 'none'] },\n    'flex-basis': { values: ['auto'] },\n    'flex-direction': {\n      values: ['column', 'column-reverse', 'row', 'row-reverse'],\n    },\n    'flex-flow': {\n      values: ['column', 'column-reverse', 'nowrap', 'row', 'row-reverse', 'wrap', 'wrap-reverse'],\n    },\n    'flex-grow': { values: [] },\n    'flex-shrink': { values: [] },\n    'flex-wrap': { values: ['nowrap', 'wrap', 'wrap-reverse'] },\n    float: { values: ['left', 'right', 'none', 'inline-start', 'inline-end'] },\n    'flow-into': { values: ['none'], type: 'named-flow' },\n    'flow-from': { values: ['none'], type: 'named-flow' },\n    font: { values: [] },\n    'font-display': {\n      values: ['auto', 'block', 'swap', 'fallback', 'optional'],\n    },\n    'font-family': {\n      values: ['auto', 'cursive', 'fantasy', 'monospace', 'sans-serif', 'serif'],\n    },\n    'font-feature-settings': { values: ['normal'] },\n    'font-kerning': { values: ['auto', 'none', 'normal'] },\n    'font-language-override': { values: ['normal'] },\n    'font-size': {\n      values: ['xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large', 'xxx-large', 'larger', 'smaller'],\n    },\n    'font-size-adjust': { values: ['auto', 'none'] },\n    'font-stretch': {\n      values: [\n        'condensed',\n        'expanded',\n        'extra-condensed',\n        'extra-expanded',\n        'normal',\n        'semi-condensed',\n        'semi-expanded',\n        'ultra-condensed',\n        'ultra-expanded',\n      ],\n    },\n    'font-style': { values: ['italic', 'normal', 'oblique'] },\n    'font-synthesis': { values: ['none', 'style', 'weight'] },\n    'font-variant': { values: ['normal', 'small-caps'] },\n    'font-variant-alternates': { values: ['normal'] },\n    'font-variant-caps': {\n      values: ['normal', 'small-caps', 'all-small-caps', 'petite-caps', 'all-petite-caps', 'unicase', 'titling-caps'],\n    },\n    'font-variant-east-asian': { values: ['normal'] },\n    'font-variant-ligatures': { values: ['normal', 'none'] },\n    'font-variant-numeric': { values: ['normal'] },\n    'font-variant-position': { values: ['normal', 'sub', 'super'] },\n    'font-weight': {\n      values: ['bold', 'bolder', 'lighter', 'normal', '100', '200', '300', '400', '500', '600', '700', '800', '900'],\n    },\n    gap: { values: ['revert-layer'] },\n    grid: { values: [] },\n    'grid-area': { values: [] },\n    'grid-auto-columns': { values: [] },\n    'grid-auto-flow': { values: ['row', 'column', 'dense'] },\n    'grid-auto-rows': { values: [] },\n    'grid-column': { values: ['auto'] },\n    'grid-column-end': { values: [] },\n    'grid-column-gap': { values: [] },\n    'grid-column-start': { values: [] },\n    'grid-gap': { values: [] },\n    'grid-row': { values: ['auto'] },\n    'grid-row-end': { values: [] },\n    'grid-row-start': { values: [] },\n    'grid-row-gap': { values: [] },\n    'grid-template': { values: ['none'] },\n    'grid-template-areas': { values: [] },\n    'grid-template-columns': { values: ['auto'] },\n    'grid-template-rows': { values: ['auto'] },\n    'hanging-punctuation': {\n      values: ['allow-end', 'first', 'force-end', 'last', 'none'],\n    },\n    height: { values: ['auto', 'max-content', 'min-content', 'fit-content'] },\n    hyphens: { values: ['auto', 'manual', 'none'] },\n    'image-orientation': { values: [] },\n    'image-resolution': { values: ['from-image', 'snap'] },\n    isolation: { values: ['auto', 'isolate'] },\n    'justify-content': {\n      values: ['center', 'flex-end', 'flex-start', 'space-around', 'space-between'],\n    },\n    'justify-items': {\n      values: [\n        'auto',\n        'normal',\n        'stretch',\n        'center',\n        'start',\n        'end',\n        'flex-start',\n        'flex-end',\n        'self-start',\n        'self-end',\n        'left',\n        'right',\n        'baseline',\n        'first',\n        'last',\n        'safe',\n        'unsafe',\n        'legacy',\n      ],\n    },\n    'justify-self': {\n      values: [\n        'auto',\n        'normal',\n        'stretch',\n        'center',\n        'start',\n        'end',\n        'flex-start',\n        'flex-end',\n        'self-start',\n        'self-end',\n        'left',\n        'right',\n        'baseline',\n        'first',\n        'last',\n        'safe',\n        'unsafe',\n      ],\n    },\n    left: { values: ['auto'] },\n    'letter-spacing': { values: ['normal'] },\n    'line-height': { values: ['normal'] },\n    'list-style': {\n      values: [\n        'none',\n        'url()',\n        'armenian',\n        'circle',\n        'decimal',\n        'decimal-leading-zero',\n        'disc',\n        'georgian',\n        'inside',\n        'lower-alpha',\n        'lower-greek',\n        'lower-latin',\n        'lower-roman',\n        'outside',\n        'square',\n        'upper-alpha',\n        'upper-latin',\n        'upper-roman',\n      ],\n    },\n    'list-style-image': { values: ['none', 'url()'] },\n    'list-style-position': { values: ['inside', 'outside'] },\n    'list-style-type': {\n      values: [\n        'armenian',\n        'circle',\n        'decimal',\n        'decimal-leading-zero',\n        'disc',\n        'georgian',\n        'lower-alpha',\n        'lower-greek',\n        'lower-latin',\n        'lower-roman',\n        'none',\n        'square',\n        'upper-alpha',\n        'upper-latin',\n        'upper-roman',\n      ],\n    },\n    margin: { values: ['auto'] },\n    'margin-bottom': { values: ['auto'] },\n    'margin-left': { values: ['auto'] },\n    'margin-right': { values: ['auto'] },\n    'margin-top': { values: ['auto'] },\n    'max-height': { values: ['none'] },\n    'max-width': { values: ['none'] },\n    'min-height': { values: [] },\n    'min-width': { values: [] },\n    'mix-blend-mode': {\n      values: [\n        'color',\n        'color-burn',\n        'color-dodge',\n        'darken',\n        'difference',\n        'exclusion',\n        'hard-light',\n        'hue',\n        'lighten',\n        'luminosity',\n        'multiply',\n        'normal',\n        'overlay',\n        'saturation',\n        'screen',\n        'soft-light',\n      ],\n    },\n    'object-fit': {\n      values: ['contain', 'cover', 'fill', 'none', 'scale-down'],\n    },\n    'object-position': { values: ['left', 'center', 'right', 'bottom', 'top'] },\n    opacity: { values: [] },\n    order: { values: [] },\n    orphans: { values: [] },\n    outline: { values: [] },\n    'outline-color': { values: ['invert'], type: 'color' },\n    'outline-offset': { values: [] },\n    'outline-style': {\n      values: ['dashed', 'dotted', 'double', 'groove', 'hidden', 'inset', 'none', 'outset', 'ridge', 'solid'],\n    },\n    'outline-width': { values: ['medium', 'thin', 'thick'] },\n    overflow: { values: ['auto', 'hidden', 'scroll', 'visible'] },\n    'overflow-x': { values: ['auto', 'hidden', 'scroll', 'visible'] },\n    'overflow-y': { values: ['auto', 'hidden', 'scroll', 'visible'] },\n    'overflow-wrap': { values: ['normal', 'anywhere', 'break-word'] },\n    padding: { values: [] },\n    'padding-bottom': { values: [] },\n    'padding-left': { values: [] },\n    'padding-right': { values: [] },\n    'padding-top': { values: [] },\n    'page-break-after': {\n      values: ['always', 'auto', 'avoid', 'left', 'right'],\n    },\n    'page-break-before': {\n      values: ['always', 'auto', 'avoid', 'left', 'right'],\n    },\n    'page-break-inside': { values: ['auto', 'avoid'] },\n    perspective: { values: ['none'] },\n    'perspective-origin': {\n      values: ['bottom', 'center', 'left', 'right', 'top'],\n    },\n    'pointer-events': {\n      values: ['all', 'auto', 'fill', 'none', 'painted', 'stroke', 'visible', 'visibleFill', 'visiblePainted', 'visibleStroke'],\n    },\n    position: { values: ['absolute', 'fixed', 'relative', 'static', 'sticky'] },\n    quotes: { values: ['none'] },\n    'region-break-after': {\n      values: ['always', 'auto', 'avoid', 'avoid-column', 'avoid-page', 'avoid-region', 'column', 'left', 'page', 'region', 'right'],\n    },\n    'region-break-before': {\n      values: ['always', 'auto', 'avoid', 'avoid-column', 'avoid-page', 'avoid-region', 'column', 'left', 'page', 'region', 'right'],\n    },\n    'region-break-inside': {\n      values: ['auto', 'avoid', 'avoid-column', 'avoid-page', 'avoid-region'],\n    },\n    'region-fragment': { values: ['auto', 'break'] },\n    resize: { values: ['both', 'horizontal', 'none', 'vertical'] },\n    right: { values: ['auto'] },\n    'scroll-behavior': { values: ['auto', 'smooth'] },\n    'scroll-snap-type': {\n      values: ['none', 'x', 'y', 'block', 'inline', 'both', 'mandatory', 'proximity'],\n    },\n    src: { values: ['url()'] },\n    'shape-image-threshold': { values: [] },\n    'shape-inside': {\n      values: ['auto', 'circle()', 'ellipse()', 'outside-shape', 'polygon()', 'rectangle()'],\n    },\n    'shape-margin': { values: [] },\n    'shape-outside': {\n      values: [\n        'none',\n        'circle()',\n        'ellipse()',\n        'polygon()',\n        'inset()',\n        'margin-box',\n        'border-box',\n        'padding-box',\n        'content-box',\n        'url()',\n        'image()',\n        'linear-gradient()',\n        'radial-gradient()',\n        'repeating-linear-gradient()',\n        'repeating-radial-gradient()',\n      ],\n    },\n    'tab-size': { values: [] },\n    'table-layout': { values: ['auto', 'fixed'] },\n    'text-align': {\n      values: ['start', 'end', 'center', 'left', 'justify', 'right', 'match-parent', 'justify-all'],\n    },\n    'text-align-last': { values: ['center', 'left', 'justify', 'right'] },\n    'text-decoration': {\n      values: ['line-through', 'none', 'overline', 'underline'],\n    },\n    'text-decoration-color': { values: [], type: 'color' },\n    'text-decoration-line': {\n      values: ['line-through', 'none', 'overline', 'underline'],\n    },\n    'text-decoration-skip': {\n      values: ['edges', 'ink', 'none', 'objects', 'spaces'],\n    },\n    'text-decoration-style': {\n      values: ['dashed', 'dotted', 'double', 'solid', 'wavy'],\n    },\n    'text-emphasis': { values: [] },\n    'text-emphasis-color': { values: [], type: 'color' },\n    'text-emphasis-position': { values: ['above', 'below', 'left', 'right'] },\n    'text-emphasis-style': {\n      values: ['circle', 'dot', 'double-circle', 'filled', 'none', 'open', 'sesame', 'triangle'],\n    },\n    'text-indent': { values: [] },\n    'text-justify': {\n      values: ['auto', 'none', 'inter-word', 'inter-character'],\n    },\n    'text-overflow': { values: ['clip', 'ellipsis'] },\n    'text-shadow': { values: [] },\n    'text-rendering': {\n      values: ['auto', 'geometricPrecision', 'optimizeLegibility', 'optimizeSpeed'],\n    },\n    'text-transform': {\n      values: ['capitalize', 'full-width', 'lowercase', 'none', 'uppercase'],\n    },\n    'text-underline-position': {\n      values: ['alphabetic', 'auto', 'below', 'left', 'right'],\n    },\n    top: { values: ['auto'] },\n    transform: {\n      values: [\n        'matrix()',\n        'matrix3d()',\n        'none',\n        'perspective()',\n        'rotate()',\n        'rotate3d()',\n        'rotateX()',\n        'rotateY()',\n        'rotateZ()',\n        'scale()',\n        'scale3d()',\n        'scaleX()',\n        'scaleY()',\n        'scaleZ()',\n        'skewX()',\n        'skewY()',\n        'translate()',\n        'translate3d()',\n        'translateX()',\n        'translateY()',\n        'translateZ()',\n      ],\n    },\n    'transform-origin': {\n      values: ['bottom', 'center', 'left', 'right', 'top'],\n    },\n    'transform-style': { values: ['flat', 'preserve-3d'] },\n    transition: { values: [] },\n    'transition-delay': { values: [] },\n    'transition-duration': { values: [] },\n    'transition-property': { values: ['all', 'none'] },\n    'transition-timing-function': {\n      values: ['cubic-bezier()', 'ease', 'ease-in', 'ease-in-out', 'ease-out', 'linear', 'step-end', 'step-start', 'steps()'],\n    },\n    'unicode-bidi': { values: ['bidi-override', 'embed', 'normal'] },\n    'unicode-range': { values: [] },\n    'user-select': { values: ['all', 'auto', 'contain', 'none', 'text'] },\n    'vertical-align': {\n      values: ['baseline', 'bottom', 'middle', 'sub', 'super', 'text-bottom', 'text-top', 'top'],\n    },\n    visibility: { values: ['collapse', 'hidden', 'visible'] },\n    'white-space': {\n      values: ['normal', 'nowrap', 'pre', 'pre-line', 'pre-wrap'],\n    },\n    widows: { values: [] },\n    width: { values: ['auto', 'max-content', 'min-content', 'fit-content'] },\n    'will-change': {\n      values: ['auto', 'contents', 'opacity', 'scroll-position'],\n    },\n    'word-break': { values: ['normal', 'break-all', 'keep-all'] },\n    'word-spacing': { values: ['normal'] },\n    'z-index': { values: ['auto'] },\n  },\n};\n\nexport const CSSProperties = CSSSourceInfo.cssKeyValue;\n\nexport const CSSColors = CSSSourceInfo.cssColors;\n\nexport type CSSPropertiesKey = keyof typeof CSSProperties;\n\nexport const CSSPropertyList = Object.keys(CSSSourceInfo.cssKeyValue);\n"
  },
  {
    "path": "packages/engine/src/component/CSSPropertiesVariableBindEditor/index.tsx",
    "content": "import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';\nimport { ConfigProvider, message } from 'antd';\nimport { JSExpressionPropType } from '@chamn/model';\n\nimport styles from './style.module.scss';\nimport { InnerSinglePropertyEditorRef, SinglePropertyEditor } from './SingleProperty';\n\nexport type CSSPropertiesVariableBindEditorProps = {\n  initialValue?: { property: string; value: string | JSExpressionPropType }[];\n  onValueChange?: (val: { property: string; value: string }[]) => void;\n};\nexport type CSSPropertiesVariableBindEditorRef = {\n  setValue: (val: { property: string; value: string | JSExpressionPropType }[]) => void;\n};\n\nexport const CSSPropertiesVariableBindEditor = forwardRef<\n  CSSPropertiesVariableBindEditorRef,\n  CSSPropertiesVariableBindEditorProps\n>(function CSSPropertiesVariableBindEditorCore(props, ref) {\n  const [propertyList, setPropertyList] = useState<{ property: string; value: any }[]>([]);\n  useImperativeHandle(\n    ref,\n    () => {\n      return {\n        setValue: (val) => {\n          setPropertyList(val);\n        },\n      };\n    },\n    []\n  );\n\n  useEffect(() => {\n    if (props.initialValue) {\n      setPropertyList(props.initialValue);\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n\n  const [newProperty, setNewProperty] = useState<{\n    property: string;\n    value: JSExpressionPropType | string;\n  }>({\n    property: '',\n    value: {\n      type: 'EXPRESSION',\n      value: '',\n    },\n  });\n\n  const innerOnValueChange = (val: typeof propertyList) => {\n    props.onValueChange?.(val);\n  };\n\n  const createPropertyRef = useRef<InnerSinglePropertyEditorRef>(null);\n  return (\n    <ConfigProvider\n      theme={{\n        token: {\n          borderRadius: 4,\n        },\n      }}\n    >\n      <div className={styles.cssBox}>\n        {propertyList.map((el, index) => {\n          return (\n            <div key={index}>\n              <SinglePropertyEditor\n                value={el}\n                mod={'edit'}\n                onValueChange={(newVal) => {\n                  if (newVal.property === '') {\n                    propertyList.splice(index, 1);\n                    setPropertyList([...propertyList]);\n                    return;\n                  }\n                  propertyList[index] = newVal;\n                  setPropertyList([...propertyList]);\n                  innerOnValueChange(propertyList);\n                }}\n                onDelete={() => {\n                  propertyList.splice(index, 1);\n                  setPropertyList([...propertyList]);\n                  innerOnValueChange(propertyList);\n                }}\n              />\n            </div>\n          );\n        })}\n\n        <SinglePropertyEditor\n          value={newProperty}\n          ref={createPropertyRef}\n          mod=\"create\"\n          onValueChange={(newVal) => {\n            setNewProperty(newVal);\n          }}\n          onCreate={(val) => {\n            const hasExits = propertyList.find((el) => el.property === val.property);\n            if (hasExits) {\n              message.error('The attribute name already exists, please replace');\n              return {\n                errorKey: [val.property],\n              };\n            }\n            propertyList.push(val);\n            setPropertyList([...propertyList]);\n            innerOnValueChange(propertyList);\n            setNewProperty({\n              property: '',\n              value: {\n                type: 'EXPRESSION',\n                value: '',\n              },\n            });\n          }}\n        />\n      </div>\n    </ConfigProvider>\n  );\n});\n"
  },
  {
    "path": "packages/engine/src/component/CSSPropertiesVariableBindEditor/style.module.scss",
    "content": ".cssFieldBox {\n  display: flex;\n  color: $textColor !important;\n  :global {\n    .ant-select-selector input {\n      color: $textColor !important;\n    }\n  }\n  .row {\n    display: flex;\n    padding-bottom: 6px;\n    align-items: center;\n  }\n\n  .fieldLabel {\n    display: inline-block;\n    width: 45px;\n    flex-shrink: 0;\n    font-size: 12px;\n  }\n\n  .leftBox {\n    flex: 1;\n  }\n\n  .rightBox {\n    padding-left: 10px;\n  }\n  .inputBox {\n    flex: 1;\n  }\n}\n\n.cssBox {\n  :global {\n    .ant-collapse-borderless > .ant-collapse-item {\n      border-bottom: 1px solid #e4e4e4;\n    }\n\n    .ant-collapse-borderless > .ant-collapse-item:last-child {\n      border-bottom: none;\n    }\n    .ant-collapse-content-box {\n      padding-bottom: 30px !important;\n    }\n  }\n}\n\n.switchBtn {\n  margin-top: 10px;\n  color: $fontColor;\n  font-size: 12px;\n  padding: 0 5px 0 10px;\n}\n"
  },
  {
    "path": "packages/engine/src/component/CSSPropertiesVariableBindEditor/util.ts",
    "content": "export const getTextWidth = async (text: string, fontSize = 14) => {\n  return new Promise<string>((resolve) => {\n    const span = document.createElement('span');\n    span.innerHTML = text;\n    span.style.fontSize = `${fontSize}px`;\n    span.style.position = 'fixed';\n    span.style.left = '-1000px';\n    span.style.bottom = '-1000px';\n    span.style.display = 'inline-block';\n    document.body.appendChild(span);\n    setTimeout(() => {\n      const style = window.getComputedStyle(span);\n      resolve(style.width);\n      setTimeout(() => {\n        document.body.removeChild(span);\n      });\n    });\n  });\n};\n"
  },
  {
    "path": "packages/engine/src/component/CSSSizeInput/index.tsx",
    "content": "/* eslint-disable react-refresh/only-export-components */\nimport { ConfigProvider, InputProps, Select } from 'antd';\nimport { MouseEventHandler, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport styles from './style.module.scss';\nimport { addEventListenerReturnCancel } from '@chamn/layout';\nimport { InputNumberPlus } from '../InputNumberPlus';\nimport clsx from 'clsx';\n\ntype CumulativeInfoType = {\n  x: number;\n  y: number;\n  cumulativeX: number;\n  cumulativeY: number;\n};\n\nconst UNIT_LIST = [\n  { value: 'px', label: 'px' },\n  { value: '%', label: '%' },\n  { value: 'vw', label: 'vw' },\n  { value: 'vh', label: 'vh' },\n  { value: 'rem', label: 'rem' },\n];\n\nexport const useDragSize = function (options?: {\n  onStart?: (data: CumulativeInfoType) => void;\n  onChange?: (data: CumulativeInfoType) => void;\n  onEnd?: (data: CumulativeInfoType) => void;\n  limitCumulative?: {\n    x?: [number | undefined, number | undefined];\n    y?: [number | undefined, number | undefined];\n  };\n}) {\n  const cumulativeInfo = useRef<{\n    status: 'running' | '';\n    start: CumulativeInfoType;\n    processing: CumulativeInfoType;\n    mouseDown: boolean;\n  }>({\n    status: '',\n    start: {\n      x: 0,\n      y: 0,\n      cumulativeX: 0,\n      cumulativeY: 0,\n    },\n    processing: {\n      x: 0,\n      y: 0,\n      cumulativeX: 0,\n      cumulativeY: 0,\n    },\n    mouseDown: false,\n  });\n\n  const processEnd: MouseEventHandler<any> = useCallback(() => {\n    cumulativeInfo.current.mouseDown = false;\n    const { processing } = cumulativeInfo.current;\n\n    options?.onEnd?.({ ...processing });\n    cumulativeInfo.current.status = '';\n    cumulativeInfo.current.processing.cumulativeX = 0;\n    cumulativeInfo.current.processing.cumulativeY = 0;\n  }, [options]);\n\n  const onMouseDown: MouseEventHandler<any> = useCallback(() => {\n    cumulativeInfo.current.mouseDown = true;\n  }, []);\n\n  const processMove: MouseEventHandler<any> = useCallback(\n    (e) => {\n      if (!cumulativeInfo.current.mouseDown) {\n        return;\n      }\n      const { start, processing } = cumulativeInfo.current;\n      // 判断是否是拖拽\n      const tempCumulativeX = e.clientX - start.x;\n      const tempCumulativeY = e.clientY - start.y;\n\n      const shakeDIstance = 10;\n      if (\n        cumulativeInfo.current.status !== 'running' &&\n        (Math.abs(tempCumulativeX) > shakeDIstance || Math.abs(tempCumulativeY) > shakeDIstance)\n      ) {\n        cumulativeInfo.current.status = 'running';\n        const startInfo = cumulativeInfo.current.start;\n        startInfo.x = e.clientX;\n        startInfo.y = e.clientY;\n        options?.onStart?.({ ...startInfo });\n      }\n\n      if (cumulativeInfo.current.status !== 'running') {\n        return;\n      }\n      processing.x = e.clientX;\n      processing.y = e.clientY;\n\n      processing.cumulativeX = e.clientX - start.x;\n      processing.cumulativeY = e.clientY - start.y;\n\n      // 超出最大累积便宜，直接结束拖拽\n      const [minX, maxX] = options?.limitCumulative?.x || [];\n      if (minX !== undefined && processing.cumulativeX < minX) {\n        processEnd(e);\n        return;\n      }\n\n      if (maxX !== undefined && processing.cumulativeX > maxX) {\n        processEnd(e);\n        return;\n      }\n\n      options?.onChange?.({ ...processing });\n    },\n    [options, processEnd]\n  );\n\n  useEffect(() => {\n    const globalMoveDispose = addEventListenerReturnCancel(document.body, 'mousemove', (e) => {\n      processMove(e as any);\n    });\n\n    const globalUpDispose = addEventListenerReturnCancel(document.body, 'mouseup', (e) => {\n      processEnd(e as any);\n    });\n    const globalLeaveDispose = addEventListenerReturnCancel(document.body, 'mouseleave', (e) => {\n      processEnd(e as any);\n    });\n\n    return () => {\n      globalMoveDispose();\n      globalUpDispose();\n      globalLeaveDispose();\n    };\n  }, [processEnd, processMove]);\n\n  return {\n    onMouseDown,\n  };\n};\n\ntype MinMaxType = {\n  px?: number;\n  vw?: number;\n  vh?: number;\n  rem?: number;\n};\n\nexport type CSSSizeInputProps = {\n  value?: string;\n  onValueChange?: (newVal: string | undefined) => void;\n  min?: MinMaxType | number;\n  max?: MinMaxType | number;\n  size?: InputProps['size'];\n  style?: React.CSSProperties;\n\n  className?: string;\n  unit?: boolean;\n  /** 累计的偏移量，映射为具体的值，默认 1:1 */\n  cumulativeTransform?: (params: CumulativeInfoType) => CumulativeInfoType;\n  unitList?: (keyof MinMaxType)[];\n};\n\nexport const CSSSizeInput = (props: CSSSizeInputProps) => {\n  const outValue = String(props.value);\n  const [dragSizing, setDragSizing] = useState({\n    value: 0,\n    status: '',\n  });\n\n  const originalValObj = useMemo(() => {\n    const res: {\n      value: number | undefined;\n      unit: keyof MinMaxType;\n    } = {\n      value: undefined,\n      unit: 'px' as any,\n    };\n\n    if (props.value === undefined) {\n      return res;\n    }\n    const tempVal = parseFloat(props.value || '');\n    if (!isNaN(tempVal)) {\n      res.value = tempVal;\n    }\n\n    let unit: keyof MinMaxType = 'px';\n    if (res.value !== undefined) {\n      unit = outValue.replace(String(res.value), '').trim() as keyof MinMaxType;\n    }\n    if (['px', '%', 'rem', 'vw', 'vx'].includes(unit)) {\n      res.unit = unit || 'px';\n    }\n    return res;\n  }, [outValue, props.value]);\n\n  const currentUnitList = useMemo<typeof UNIT_LIST>(() => {\n    if (!props.unitList) {\n      return UNIT_LIST;\n    }\n    return props.unitList\n      .map((el) => {\n        return UNIT_LIST.find((it) => it.value === el);\n      })\n      .filter(Boolean) as typeof UNIT_LIST;\n  }, [props.unitList]);\n\n  const currentMinMix = useMemo(() => {\n    return {\n      min: typeof props.min === 'number' ? props.min : props.min?.[originalValObj.unit],\n      max: typeof props.max === 'number' ? props.max : props.max?.[originalValObj.unit],\n    };\n  }, [props, originalValObj]);\n\n  const valObj = useMemo(() => {\n    const res = { ...originalValObj };\n    if (dragSizing.status === 'dragging') {\n      res.value = dragSizing.value;\n    }\n\n    return res;\n  }, [originalValObj, dragSizing]);\n\n  const updateValue = useCallback(\n    (val: { value: number | undefined; unit: string }) => {\n      let valStr: string | undefined = `${val.value}${val.unit}`;\n      if (val.value === undefined) {\n        valStr = undefined;\n      }\n      props.onValueChange?.(valStr);\n    },\n    [props]\n  );\n\n  const processNewVal = useCallback(\n    (cumulativeData: CumulativeInfoType, value: number | undefined) => {\n      let data = { ...cumulativeData };\n      if (props.cumulativeTransform) {\n        data = props.cumulativeTransform(data);\n      }\n\n      let num = value ?? 0;\n      if (isNaN(num)) {\n        num = 0;\n      }\n      let newVal = num + data.cumulativeX;\n\n      if (currentMinMix.min !== undefined) {\n        newVal = Math.max(newVal, currentMinMix.min);\n      }\n      if (currentMinMix.max !== undefined) {\n        newVal = Math.min(newVal, currentMinMix.max);\n      }\n      return newVal;\n    },\n    [currentMinMix.max, currentMinMix.min, props]\n  );\n\n  const onDragStart = useCallback(\n    (data: CumulativeInfoType) => {\n      setDragSizing({\n        status: 'dragging',\n        value: processNewVal(data, originalValObj.value),\n      });\n    },\n    [originalValObj.value, processNewVal]\n  );\n\n  const onDragMoveChange = useCallback(\n    (data: CumulativeInfoType) => {\n      const nV = processNewVal(data, originalValObj.value);\n      setDragSizing({\n        status: 'dragging',\n        value: nV,\n      });\n    },\n    [originalValObj.value, processNewVal]\n  );\n\n  const onDragEnd = useCallback(\n    (data: CumulativeInfoType) => {\n      const newVal = processNewVal(data, originalValObj.value);\n      if (dragSizing.status !== 'dragging') {\n        return;\n      }\n\n      updateValue({\n        value: newVal,\n        unit: originalValObj.unit,\n      });\n      setDragSizing({\n        status: '',\n        value: 0,\n      });\n    },\n    [dragSizing.status, originalValObj, processNewVal, updateValue]\n  );\n  const handleRef = useRef({\n    onStart: onDragStart,\n    onChange: onDragMoveChange,\n    onEnd: onDragEnd,\n  });\n  handleRef.current = {\n    onStart: onDragStart,\n    onChange: onDragMoveChange,\n    onEnd: onDragEnd,\n  };\n\n  const dragSizeHandle = useDragSize({\n    onStart: (data) => handleRef.current.onStart(data),\n    onChange: (data) => handleRef.current.onChange(data),\n    onEnd: (data) => handleRef.current.onEnd(data),\n    limitCumulative: {\n      x: [-60, undefined],\n    },\n  });\n  const unitSelect = (\n    <span className={styles.unitSelect}>\n      <Select\n        size={props.size}\n        defaultValue=\"\"\n        value={valObj.unit}\n        onChange={(val) => {\n          updateValue({\n            value: valObj.value,\n            unit: val,\n          });\n        }}\n        style={{\n          width: '40px',\n        }}\n        popupMatchSelectWidth={false}\n        options={currentUnitList}\n      />\n    </span>\n  );\n  return (\n    <ConfigProvider\n      theme={{\n        token: {\n          borderRadius: 2,\n        },\n      }}\n    >\n      <div className={clsx([styles.cssSizeInput, props?.className])} {...dragSizeHandle} style={props.style}>\n        <InputNumberPlus\n          size={props.size}\n          value={valObj.value}\n          addonAfter={props.unit === false ? '' : unitSelect}\n          min={currentMinMix.min}\n          max={currentMinMix.max}\n          onChange={(val) => {\n            updateValue({\n              value: val,\n              unit: valObj.unit,\n            });\n          }}\n        />\n      </div>\n    </ConfigProvider>\n  );\n};\n"
  },
  {
    "path": "packages/engine/src/component/CSSSizeInput/style.module.scss",
    "content": ".cssSizeInput {\n  display: inline-block;\n  .unitSelect {\n    :global {\n      .ant-select-focused .ant-select-selector {\n        color: rgba(0, 0, 0, 0.675);\n      }\n\n      .ant-select-selector {\n        color: #585858 !important;\n        padding: 0 0 0 3px !important;\n        font-size: 12px;\n      }\n      .ant-select-selection-item {\n        padding-inline-end: 10px !important;\n      }\n      .ant-select-arrow {\n        inset-inline-end: 3px !important;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/engine/src/component/ClassNameEditor/index.tsx",
    "content": "import { forwardRef, useImperativeHandle, useRef } from 'react';\nimport { ConfigProvider } from 'antd';\nimport { CustomSchemaForm, CustomSchemaFormInstance } from '../CustomSchemaForm';\nimport { ClassNameType, CMaterialPropsType, CNode } from '@chamn/model';\nimport { CPluginCtx } from '@/core/pluginManager';\n\nexport type ClassNameEditorProps = {\n  initialValue?: ClassNameType[];\n  onValueChange?: (val: ClassNameType[]) => void;\n  pluginContext: CPluginCtx;\n  nodeModel: CNode;\n};\nexport type ClassNameEditorRef = {\n  setValue: (val: ClassNameType[]) => void;\n};\n\nconst properties: CMaterialPropsType = [\n  {\n    title: 'Class Names',\n    name: 'className',\n    valueType: 'array',\n\n    setters: [\n      {\n        componentName: 'ArraySetter',\n        props: {\n          sortLabelKey: 'name',\n          collapse: {\n            open: true,\n          },\n          item: {\n            setters: [\n              {\n                componentName: 'ShapeSetter',\n                props: {\n                  elements: [\n                    {\n                      name: 'name',\n                      title: '类名',\n                      valueType: 'string',\n                      setters: ['StringSetter'],\n                    },\n                    {\n                      name: 'status',\n                      title: '启用',\n                      valueType: 'boolean',\n                      setters: ['BooleanSetter', 'ExpressionSetter'],\n                    },\n                  ],\n                },\n                initialValue: {},\n              },\n            ],\n            initialValue: {\n              name: '',\n              status: true,\n            },\n          },\n        },\n        initialValue: [],\n      },\n    ],\n  },\n];\n\nexport const ClassNameEditor = forwardRef<ClassNameEditorRef, ClassNameEditorProps>(function CSSPropertiesEditorCore(\n  props,\n  ref\n) {\n  const formRef = useRef<CustomSchemaFormInstance>(null);\n  useImperativeHandle(ref, () => {\n    return {\n      setValue(newValue) {\n        formRef?.current?.setFields({\n          className: newValue || [],\n        });\n      },\n    };\n  });\n  return (\n    <ConfigProvider\n      theme={{\n        token: {\n          borderRadius: 4,\n        },\n      }}\n    >\n      <div>\n        <CustomSchemaForm\n          ref={formRef}\n          initialValue={[]}\n          nodeId={props.nodeModel.id}\n          pluginCtx={props.pluginContext}\n          properties={properties}\n          onSetterChange={function () {}}\n          onValueChange={(newVal) => {\n            props.onValueChange?.(newVal.className || []);\n          }}\n          defaultSetterConfig={{}}\n        ></CustomSchemaForm>\n      </div>\n    </ConfigProvider>\n  );\n});\n"
  },
  {
    "path": "packages/engine/src/component/ClassNameEditor/style.module.scss",
    "content": ""
  },
  {
    "path": "packages/engine/src/component/CustomColorPicker/index.tsx",
    "content": "import { ColorPicker, ColorPickerProps, GetProp } from 'antd';\nimport { forwardRef, useImperativeHandle, useState } from 'react';\n\nexport type CustomColorPickerRef = {\n  updateColor: (color: string) => void;\n};\n\ntype Color = GetProp<ColorPickerProps, 'value'>;\n\ntype CustomColorPickerProps = Omit<ColorPickerProps, 'onChange'> & {\n  onChange: (color: string) => void;\n};\n\nexport const CustomColorPicker = forwardRef<CustomColorPickerRef, CustomColorPickerProps>(function CustomColorPicker(\n  props,\n  ref\n) {\n  const [color, setColor] = useState<Color>();\n\n  useImperativeHandle(ref, () => {\n    return {\n      updateColor: (color: string) => {\n        setColor(color);\n      },\n    };\n  });\n\n  return (\n    <ColorPicker\n      {...props}\n      value={color}\n      onChange={(val) => {\n        setColor(val);\n      }}\n      onChangeComplete={(val) => {\n        props.onChange?.(val.toRgbString());\n      }}\n    />\n  );\n});\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/CFiledWithSwitchSetter/index.tsx",
    "content": "import { useContext, useMemo, useState } from 'react';\nimport { SetterObjType, SetterType } from '@chamn/model';\nimport { Dropdown, MenuProps } from 'antd';\nimport { SwapOutlined } from '@ant-design/icons';\nimport { CCustomSchemaFormContext } from '@/component/CustomSchemaForm/context';\nimport { getSetterList } from '@/component/CustomSchemaForm/utils';\nimport styles from './style.module.scss';\nimport { CField, CFieldProps } from '../Form/Field';\nimport { SetterSwitcherCore } from '../SetterSwitcher/core';\n\nexport const CFiledWithSwitchSetter = (\n  props: Omit<CFieldProps, 'children'> & {\n    setterList: SetterType[];\n    onSetterChange?: (setterName: string) => void;\n    defaultSetterName?: string;\n  }\n) => {\n  const { setterList: setters, defaultSetterName, ...restProps } = props;\n\n  const setterList = useMemo(() => {\n    return getSetterList(setters);\n  }, [setters]);\n\n  const { onSetterChange, defaultSetterConfig, pluginCtx, nodeId, customSetterMap } =\n    useContext(CCustomSchemaFormContext);\n  const keyPaths = [props.name];\n\n  const [currentSetter, setCurrentSetter] = useState<SetterObjType>(() => {\n    const currentSetterName = defaultSetterConfig[keyPaths.join('.')]?.setter || defaultSetterName || '';\n    return [...setterList].find((el) => el.componentName === currentSetterName) || setterList[0];\n  });\n\n  const menuItems = setterList.map((setter) => {\n    const setterName = setter?.componentName || '';\n    const setterRuntime = ({} as any)[setterName];\n    return {\n      key: setter.componentName,\n      label: setterRuntime?.setterName || setter.componentName,\n    };\n  });\n\n  const onChooseSetter: MenuProps['onClick'] = ({ key }) => {\n    const targetSetter = setterList.find((setter) => setter.componentName === key);\n    if (targetSetter) {\n      setCurrentSetter(targetSetter);\n      onSetterChange?.(keyPaths, targetSetter.componentName);\n      props.onSetterChange?.(targetSetter.componentName);\n    }\n  };\n\n  let switcher: any = (\n    <div\n      onClick={(e) => {\n        e.preventDefault();\n        e.stopPropagation();\n      }}\n      className={styles.switchBtn}\n    >\n      <Dropdown\n        trigger={['click']}\n        menu={{\n          selectable: true,\n          items: menuItems,\n          onClick: onChooseSetter,\n          defaultSelectedKeys: [currentSetter?.componentName || ''],\n        }}\n      >\n        <SwapOutlined />\n      </Dropdown>\n    </div>\n  );\n\n  if (menuItems.length === 1) {\n    switcher = null;\n  }\n\n  const setterProps = useMemo(() => {\n    let newProps = {\n      ...(currentSetter?.props || {}),\n      initialValue: currentSetter?.initialValue,\n    };\n    const target = setterList.find((el) => el.componentName === currentSetter?.componentName);\n    if (target) {\n      newProps = {\n        ...newProps,\n        ...target.props,\n      };\n    }\n    return newProps;\n  }, [setterList, currentSetter]);\n\n  return (\n    <div\n      className={styles.fieldBox}\n      style={{\n        alignItems: props.labelAlign ?? 'center',\n      }}\n    >\n      <CField {...restProps}>\n        <SetterSwitcherCore\n          {...setterProps}\n          setters={setterList}\n          keyPaths={keyPaths}\n          currentSetter={currentSetter}\n          customSetterMap={customSetterMap}\n          setCurrentSetter={setCurrentSetter}\n          setterContext={{\n            pluginCtx,\n            keyPaths: [props.name],\n            label: props.label,\n            nodeModel: pluginCtx?.pageModel.getNode(nodeId) as any,\n          }}\n        />\n      </CField>\n      {switcher}\n    </div>\n  );\n};\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/CFiledWithSwitchSetter/style.module.scss",
    "content": ".fieldBox {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.switchBtn {\n  color: $fontColor;\n  font-size: 12px;\n  padding: 0 5px 0 10px;\n}\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Form/Field/index.tsx",
    "content": "import React, { isValidElement, ReactNode, useEffect, useState } from 'react';\nimport styles from './style.module.scss';\nimport { Alert, Popover, Tooltip } from 'antd';\nimport { CFormContext } from '../context';\nimport clsx from 'clsx';\nimport { QuestionCircleOutlined } from '@ant-design/icons';\n\nexport type CFiledChildProps = {\n  onValueChange?: (val: any) => void;\n  initialValue?: any;\n  value?: any;\n};\n\nexport type CFieldProps = {\n  children: React.ReactNode;\n  label?: string;\n  labelWidth?: string;\n  labelAlign?: 'start' | 'center' | 'end';\n  tips?: ReactNode | (() => ReactNode);\n  name: string;\n  condition?: (formState: Record<string, any>) => boolean;\n  onConditionValueChange?: (val: boolean) => void;\n  /** 不做任何包裹, 不会渲染 switchSetter */\n  noStyle?: boolean;\n  /** 隐藏 label, 会渲染 switchSetter */\n  hiddenLabel?: boolean;\n  valueChangeEventName?: string;\n  formatEventValue?: (val: any) => any;\n  rules?: { validator?: (value: any) => Promise<boolean | undefined>; required?: boolean; msg?: string }[];\n};\n\nexport const CField = (props: CFieldProps) => {\n  const { children, label, tips, name, hiddenLabel } = props;\n  const [errorInfo, setErrorInfo] = useState({\n    isPass: true,\n    errorMsg: '',\n  });\n  let labelView: ReactNode = label;\n  const { formState, updateContext, updateConditionConfig } = React.useContext(CFormContext);\n  useEffect(() => {\n    if (props.condition) {\n      updateConditionConfig(name, props.condition);\n    }\n  }, [name, props.condition, updateConditionConfig]);\n\n  useEffect(() => {\n    const condition = props.condition ?? (() => true);\n    const canRender = condition(formState);\n    props.onConditionValueChange?.(canRender);\n  }, [formState, props]);\n\n  if (tips) {\n    let newTip: any = tips;\n    if (typeof tips === 'function') {\n      newTip = tips();\n    }\n    labelView = (\n      <Tooltip\n        title={<span style={{ color: 'rgba(255,255,255,0.9', fontSize: '12px' }}>{newTip}</span>}\n        color=\"rgba(50,50,50,0.8)\"\n      >\n        <span className={styles.tipsLabel}>{label}</span>\n      </Tooltip>\n    );\n  }\n  let newChildren = children;\n\n  if (isValidElement(children)) {\n    const eventName = props.valueChangeEventName ?? 'onValueChange';\n    const extraProps: any = {\n      [eventName]: async (val: any) => {\n        let tempVal = val;\n        if (props.formatEventValue) {\n          tempVal = props.formatEventValue(val);\n        }\n\n        // 先检查数据是否合法\n        const rules = props.rules || [];\n        const checkResult = await checkValue(props.name, tempVal, rules);\n        if (!checkResult.isPass) {\n          setErrorInfo(checkResult);\n        } else {\n          setErrorInfo({\n            isPass: true,\n            errorMsg: '',\n          });\n        }\n        updateContext(\n          {\n            ...formState,\n            [name]: tempVal,\n          },\n          [name]\n        );\n      },\n    };\n\n    extraProps.value = (formState as any)[name];\n    newChildren = React.cloneElement(children, extraProps);\n  }\n  const condition = props.condition ?? (() => true);\n  const canRender = condition(formState);\n  if (!canRender) {\n    return null;\n  }\n  if (props.noStyle) {\n    return <>{newChildren}</>;\n  }\n\n  return (\n    <div\n      className={clsx([styles.fieldBox, !errorInfo.isPass && styles.error])}\n      style={{\n        alignItems: props.labelAlign ?? 'center',\n      }}\n    >\n      {hiddenLabel !== true && (\n        <div\n          className={styles.label}\n          style={{\n            width: props.labelWidth ?? '60px',\n          }}\n        >\n          {labelView}\n        </div>\n      )}\n      <div className={styles.content}>{newChildren}</div>\n      {!errorInfo.isPass && (\n        <Popover content={<Alert message={errorInfo.errorMsg} type=\"error\" />}>\n          <QuestionCircleOutlined className={styles.errorTipIcon} />\n        </Popover>\n      )}\n    </div>\n  );\n};\n\nconst checkValue = async (key: string, value: any, rules: CFieldProps['rules'] = []) => {\n  let pass = true;\n  let errorMsg = '';\n\n  for (let i = 0; i < rules.length; i++) {\n    if (rules[i].required && !value) {\n      pass = false;\n      errorMsg = `${key} is required.`;\n      break;\n    }\n    if (rules[i].validator) {\n      try {\n        const isPass = await rules[i].validator?.(value);\n        if (!isPass) {\n          pass = false;\n          errorMsg = rules[i]?.msg || `${key} is illegal `;\n          break;\n        }\n      } catch (e) {\n        pass = false;\n        errorMsg = `${String(e)}`;\n        break;\n      }\n    }\n  }\n\n  return {\n    isPass: pass,\n    errorMsg,\n  };\n};\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Form/Field/style.module.scss",
    "content": ".fieldBox {\n  display: flex;\n  flex-wrap: nowrap;\n  flex-shrink: 0;\n  flex-grow: 1;\n  min-width: 250px;\n  align-items: center;\n  .label {\n    font-size: 12px;\n    color: $textColor;\n    padding-right: 10px;\n    user-select: none;\n\n    max-height: 40px;\n    word-break: break-all;\n    text-overflow: ellipsis;\n    overflow: hidden;\n    display: -webkit-box;\n    -webkit-box-orient: vertical;\n    -webkit-line-clamp: 2; /* 这里是超出几行省略 */\n  }\n  .tipsLabel {\n    text-decoration-line: underline;\n    text-decoration-style: dashed;\n    text-underline-offset: 2px;\n    cursor: help;\n    word-break: break-all;\n  }\n  .content {\n    flex: 1;\n    position: relative;\n  }\n}\n\n$errorColor: #ff002dc4;\n\n.error {\n  outline: 1px solid $errorColor;\n  outline-offset: 3px;\n  border-radius: 4px;\n}\n\n.errorTipIcon {\n  color: $errorColor;\n  margin: 0 5px;\n}\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Form/context.ts",
    "content": "import React from 'react';\nimport { CSetter } from '../Setters/type';\n\nexport type ContextState = Record<string, any>;\n\nexport type CFormContextData = {\n  formName: string;\n  formState: ContextState;\n  conditionConfig: Record<string, (state: ContextState) => boolean>;\n  // 自定义 setter 的具体实现，可以覆盖默认 setter\n  customSetterMap?: Record<string, CSetter>;\n  updateContext: (newState: ContextState, changeKeys?: string[]) => void;\n  updateConditionConfig: (name: string, cb: (state: ContextState) => boolean) => void;\n};\n\nexport const CFormContext = React.createContext<CFormContextData>({\n  formName: '',\n  formState: {},\n  conditionConfig: {},\n  updateContext: () => {},\n  updateConditionConfig: () => {},\n  // 自定义 setter 的具体实现，可以覆盖默认 setter\n  customSetterMap: {},\n});\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Form/index.tsx",
    "content": "import React from 'react';\nimport { ReactNode } from 'react';\nimport { CFormContext, CFormContextData, ContextState } from './context';\n\nexport type CFormProps = {\n  name: string;\n  children?: ReactNode | ReactNode[];\n  initialValue?: Record<string, any>;\n  customSetterMap: CFormContextData['customSetterMap'];\n  onValueChange?: (formData: Record<string, any>, changeKeys?: string[]) => void;\n} & Partial<ContextState>;\n\nconst CUSTOM_SETTER_MAP = {};\n\nlet updateState = () => {};\n\nexport const registerCustomSetter = (customSetterMap: CFormContextData['customSetterMap']) => {\n  Object.assign(CUSTOM_SETTER_MAP, customSetterMap);\n  updateState?.();\n};\nexport class CForm extends React.Component<CFormProps, CFormContextData> {\n  updateContext: (newState: ContextState) => void;\n  isMount = false;\n  constructor(props: CFormProps) {\n    super(props);\n    this.updateContext = (newState: ContextState, changeKeys?: string[]) => {\n      this.setState({\n        formState: newState,\n        customSetterMap: CUSTOM_SETTER_MAP,\n      });\n      this.props.onValueChange?.(this.formatValue(newState), changeKeys);\n    };\n    updateState = () => {\n      if (!this.isMount) {\n        return;\n      }\n      this.setState({\n        customSetterMap: { ...CUSTOM_SETTER_MAP, ...this.props.customSetterMap },\n      });\n    };\n    registerCustomSetter(props.customSetterMap || {});\n    this.state = {\n      formName: props.name,\n      formState: props.initialValue ?? {},\n      conditionConfig: {},\n      customSetterMap: props.customSetterMap || {},\n\n      updateContext: this.updateContext,\n      updateConditionConfig: (name: string, cb: (state: Record<string, any>) => boolean) => {\n        this.setState({\n          conditionConfig: {\n            ...this.state.conditionConfig,\n            [name]: cb,\n          },\n        });\n      },\n    };\n  }\n\n  componentDidMount(): void {\n    this.isMount = true;\n  }\n\n  componentWillUnmount(): void {\n    this.isMount = false;\n  }\n\n  getFieldsValue = () => {\n    return this.formatValue(this.state.formState);\n  };\n\n  setFields = (state: Record<string, any>) => {\n    this.setState({\n      formState: state,\n    });\n  };\n\n  formatValue = (data: Record<string, any>) => {\n    const res: Record<string, any> = {};\n    const conditionConfig = this.state.conditionConfig;\n    Object.keys(data).forEach((key) => {\n      const isValid = conditionConfig[key]?.(data) ?? true;\n      if (isValid) {\n        res[key] = data[key];\n      }\n    });\n    return res;\n  };\n\n  render(): ReactNode {\n    const { state } = this;\n    const { children } = this.props;\n\n    return <CFormContext.Provider value={state}>{children}</CFormContext.Provider>;\n  }\n}\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/SetterSwitcher/core.tsx",
    "content": "import React, { useContext, useEffect } from 'react';\nimport { SetterObjType } from '@chamn/model';\nimport InnerSetters from '../Setters/index';\nimport { CFieldProps } from '../Form/Field';\nimport { CCustomSchemaFormContext } from '../../context';\nimport { CFormContext } from '../Form/context';\nimport { CSetter, CSetterProps } from '../Setters/type';\nimport { getDefaultSetterByValue } from '../../utils';\n\nexport type SetterSwitcherProps = {\n  // 支持的 setter 列表\n  setters: SetterObjType[];\n  // 自定义 setter 的具体实现，可以覆盖默认 setter\n  customSetterMap?: Record<string, CSetter>;\n  keyPaths: string[];\n  prefix?: React.ReactNode;\n  suffix?: React.ReactNode;\n  style?: React.CSSProperties;\n  /** 是否实用 CFile 包裹 */\n  useField?: boolean;\n} & Omit<CFieldProps, 'children'>;\n\n/** 用于渲染切换设置器 */\nexport const SetterSwitcherCore = ({\n  setters,\n  keyPaths,\n  currentSetter,\n  setCurrentSetter,\n  customSetterMap: customSetterMapProp,\n  ...props\n}: Pick<SetterSwitcherProps, 'setters' | 'keyPaths'> & {\n  value?: any;\n  setterContext?: Partial<CSetterProps['setterContext']>;\n  currentSetter: SetterObjType;\n  customSetterMap?: any;\n  setCurrentSetter: (setter: SetterObjType) => void;\n}) => {\n  const { customSetterMap } = useContext(CFormContext);\n  const { defaultSetterConfig } = useContext(CCustomSchemaFormContext);\n  const allSetterMap = {\n    ...InnerSetters,\n    ...(customSetterMapProp || {}),\n    ...customSetterMap,\n  };\n\n  useEffect(() => {\n    const currentSetterName = defaultSetterConfig[keyPaths.join('.')]?.setter || '';\n    const devConfigSetter = setters.find((el) => el.componentName === currentSetterName);\n    const st = devConfigSetter || getDefaultSetterByValue(props.value, setters) || setters[0];\n    setCurrentSetter(st);\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n\n  let CurrentSetterComp = null;\n  if (currentSetter?.componentName) {\n    CurrentSetterComp = allSetterMap[currentSetter?.componentName] || currentSetter.component;\n  }\n  if (!CurrentSetterComp) {\n    CurrentSetterComp = function EmptySetter() {\n      return (\n        <div\n          style={{\n            backgroundColor: 'whitesmoke',\n            margin: '5px 0',\n            padding: '5px',\n            borderRadius: '2px',\n            color: 'gray',\n          }}\n        >{`${currentSetter?.componentName} is not found.`}</div>\n      );\n    };\n  }\n\n  return <CurrentSetterComp {...props} />;\n};\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/SetterSwitcher/helper.tsx",
    "content": "/* eslint-disable react-refresh/only-export-components */\nimport styles from './style.module.scss';\n\nexport function EmptySetter(props: { setterName: string }) {\n  return (\n    <div\n      style={{\n        backgroundColor: 'whitesmoke',\n        margin: '5px 0',\n        padding: '5px',\n        borderRadius: '2px',\n        color: 'gray',\n      }}\n    >{`${props?.setterName} is not found.`}</div>\n  );\n}\n\nexport const getEmptySetter = (setterName: string) => {\n  return () => <EmptySetter setterName={setterName} />;\n};\n\n// 新增 CollapseHeader 组件\nexport const CollapseHeader: React.FC<{\n  label: React.ReactNode;\n  headerExt?: React.ReactNode;\n  switcher?: React.ReactNode;\n}> = ({ label, headerExt, switcher }) => (\n  <div className={styles.collapseHeader}>\n    <span style={{ flex: 1 }}>{label}</span>\n    {headerExt}\n    {switcher}\n  </div>\n);\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/SetterSwitcher/index.tsx",
    "content": "import React, { useContext, useMemo, useState } from 'react';\nimport { SetterObjType, SetterTypeEnum } from '@chamn/model';\nimport InnerSetters from '../Setters/index';\nimport { CField, CFieldProps } from '../Form/Field';\nimport { Collapse, Dropdown, MenuProps } from 'antd';\nimport { SwapOutlined } from '@ant-design/icons';\nimport styles from './style.module.scss';\nimport { CCustomSchemaFormContext } from '../../context';\nimport { CFormContext } from '../Form/context';\nimport { CSetter } from '../Setters/type';\nimport { SetterSwitcherCore } from './core';\n\nexport type SetterSwitcherProps = {\n  // 支持的 setter 列表\n  setters: SetterObjType[];\n  // 自定义 setter 的具体实现，可以覆盖默认 setter\n  customSetterMap?: Record<string, CSetter>;\n  keyPaths: string[];\n  prefix?: React.ReactNode;\n  suffix?: React.ReactNode;\n  style?: React.CSSProperties;\n  /** 是否实用 CFile 包裹 */\n  useField?: boolean;\n} & Omit<CFieldProps, 'children'>;\n\nexport const SetterSwitcher = ({\n  setters: outerSetter,\n  keyPaths,\n  condition,\n  useField = true,\n  ...props\n}: SetterSwitcherProps) => {\n  const [visible, setVisible] = useState(true);\n  const { customSetterMap } = useContext(CFormContext);\n  const { onSetterChange, defaultSetterConfig, formRef, pluginCtx, nodeId } = useContext(CCustomSchemaFormContext);\n  const allSetterMap = {\n    ...InnerSetters,\n    ...customSetterMap,\n  };\n  // 统一添加一些内置的 setter\n  const setters = useMemo(() => {\n    return [\n      ...outerSetter,\n      {\n        componentName: SetterTypeEnum.EMPTY_VALUE_SETTER,\n      },\n    ];\n  }, [outerSetter]);\n  const [currentSetter, setCurrentSetter] = useState<SetterObjType>(() => {\n    const currentSetterName = defaultSetterConfig[keyPaths.join('.')]?.setter || '';\n    const devConfigSetter = setters.find((el) => el.componentName === currentSetterName);\n    return devConfigSetter || setters[0];\n  });\n\n  const menuItems = setters.map((setter) => {\n    const setterName = setter?.componentName || '';\n    const setterRuntime = allSetterMap[setterName];\n    return {\n      key: setter.componentName,\n      label: setterRuntime?.setterName || setter.componentName,\n    };\n  });\n\n  const switcher = useMemo(() => {\n    const onChooseSetter: MenuProps['onClick'] = ({ key }) => {\n      const targetSetter = setters.find((setter) => setter.componentName === key);\n      if (targetSetter) {\n        setCurrentSetter(targetSetter);\n        onSetterChange?.(keyPaths, targetSetter.componentName);\n      }\n    };\n    if (menuItems.length === 1) {\n      return null;\n    }\n\n    return (\n      <div\n        className={styles.switchBtn}\n        onClick={(e) => {\n          e.preventDefault();\n          e.stopPropagation();\n        }}\n      >\n        <Dropdown\n          trigger={['click']}\n          menu={{\n            selectable: true,\n            items: menuItems,\n            onClick: onChooseSetter,\n            defaultSelectedKeys: [currentSetter?.componentName || ''],\n          }}\n        >\n          <SwapOutlined />\n        </Dropdown>\n      </div>\n    );\n  }, [menuItems, currentSetter?.componentName, setters, onSetterChange, keyPaths]);\n\n  const setterProps = useMemo(() => {\n    let newProps = {\n      ...(currentSetter?.props || {}),\n      initialValue: currentSetter?.initialValue,\n    };\n    const target = setters.find((el) => el.componentName === currentSetter?.componentName);\n    if (target) {\n      newProps = {\n        ...newProps,\n        ...target.props,\n      };\n    }\n    return newProps;\n  }, [setters, currentSetter]);\n\n  const [collapseHeaderExt, setCollapseHeaderExt] = useState<any>([]);\n\n  let bodyView: any = null;\n  const hiddenLabel = currentSetter?.hiddenLabel === true;\n\n  const labelWidth = currentSetter?.labelWidth;\n  const labelAlign = currentSetter?.labelAlign || 'center';\n  const collapse = (currentSetter?.props as any)?.collapse;\n  const specialSetter = ['ArraySetter', 'ShapeSetter'].includes(currentSetter?.componentName);\n  const setterContext = useMemo(\n    () => ({\n      formRef,\n      pluginCtx,\n      keyPaths: [...keyPaths],\n      label: props.label,\n      setCollapseHeaderExt: specialSetter ? setCollapseHeaderExt : undefined,\n      nodeModel: pluginCtx?.pageModel.getNode(nodeId) as any,\n    }),\n    [formRef, keyPaths, nodeId, pluginCtx, props.label, specialSetter]\n  );\n  const filedView = useMemo(() => {\n    const customSetterMap = {\n      ...(props.customSetterMap || {}),\n    };\n\n    const cFiledProps = {\n      labelWidth,\n      labelAlign,\n      hiddenLabel,\n      condition,\n      noStyle: specialSetter ? true : false,\n      onConditionValueChange: (val: boolean) => {\n        setVisible(val);\n      },\n    };\n\n    if (useField === false) {\n      return (\n        <SetterSwitcherCore\n          setters={setters}\n          keyPaths={keyPaths}\n          currentSetter={currentSetter}\n          setCurrentSetter={setCurrentSetter}\n          {...props}\n          {...setterProps}\n          customSetterMap={customSetterMap}\n          setterContext={setterContext}\n        />\n      );\n    }\n    return (\n      <CField {...cFiledProps} {...props}>\n        <SetterSwitcherCore\n          setters={setters}\n          keyPaths={keyPaths}\n          currentSetter={currentSetter}\n          setCurrentSetter={setCurrentSetter}\n          {...setterProps}\n          customSetterMap={customSetterMap}\n          setterContext={setterContext}\n        />\n      </CField>\n    );\n  }, [\n    condition,\n    currentSetter,\n    hiddenLabel,\n    keyPaths,\n    labelAlign,\n    labelWidth,\n    props,\n    setterContext,\n    setterProps,\n    setters,\n    specialSetter,\n    useField,\n  ]);\n\n  const renderCollapse = useMemo(\n    function renderCollapse() {\n      const collapseObj = typeof collapse === 'object' ? collapse : {};\n\n      const CollapseComponent = (extraHeaderContent?: React.ReactNode) => (\n        <Collapse\n          bordered={false}\n          {...collapseObj}\n          style={{\n            marginBottom: '10px',\n            flex: 1,\n          }}\n          defaultActiveKey={[collapseObj.open ? props.name : '']}\n          items={[\n            {\n              key: props.name,\n              label: (\n                <div className={styles.collapseHeader}>\n                  <span style={{ flex: 1 }}>{props.label}</span>\n                  {extraHeaderContent}\n                  {switcher}\n                </div>\n              ),\n              children: filedView,\n            },\n          ]}\n        />\n      );\n\n      return CollapseComponent;\n    },\n    [collapse, props.name, props.label, switcher, filedView]\n  );\n\n  if (['ArraySetter'].includes(currentSetter?.componentName || '')) {\n    bodyView = renderCollapse(collapseHeaderExt);\n  } else if (['ShapeSetter'].includes(currentSetter?.componentName || '')) {\n    bodyView = (\n      <div className={styles.shapeFieldBox}>\n        {props.prefix ?? null}\n        {(currentSetter?.props as any)?.collapse === false && <div style={{ width: '100%' }}>{filedView}</div>}\n        {collapse !== false && renderCollapse()}\n        {props.suffix ?? null}\n      </div>\n    );\n  } else {\n    bodyView = (\n      <div style={{ display: 'flex', alignItems: labelAlign, paddingBottom: '8px' }}>\n        {props.prefix ?? null}\n        {filedView}\n        {switcher}\n        {props.suffix ?? null}\n      </div>\n    );\n  }\n\n  return <div style={{ display: visible ? 'block' : 'none' }}>{bodyView}</div>;\n};\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/SetterSwitcher/style.module.scss",
    "content": ".switchBtn {\n  color: $fontColor;\n  font-size: 12px;\n  padding: 0 5px 0 10px;\n}\n\n.shapeFieldBox,\n.collapseHeader {\n  color: $fontColor;\n  display: flex;\n  font-size: 12px;\n}\n\n.collapseHeader {\n  align-items: center;\n}\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ActionFlowSetter/component/CreateNewNodePopup/index.tsx",
    "content": "import { Popover } from 'antd';\nimport { useState } from 'react';\nimport { DEFAULT_NODE_LIST } from './initData';\nimport { TLogicItemHandlerFlow } from '@chamn/model';\n\nexport const CreateNewNodePopup = (props: {\n  title?: string;\n  children: React.ReactNode;\n  disabled?: boolean;\n  style?: React.CSSProperties;\n  onNewNodeAdd: (data: TLogicItemHandlerFlow[number]) => void;\n}) => {\n  const { title = 'Next Step' } = props;\n\n  const [open, setOpen] = useState(false);\n\n  const handleOpenChange = (newOpen: boolean) => {\n    setOpen(newOpen);\n  };\n\n  const nodeList = DEFAULT_NODE_LIST.map((el) => {\n    return (\n      <div\n        onClick={() => {\n          setOpen(false);\n          props.onNewNodeAdd?.(el.getInitData());\n        }}\n        key={el.key}\n        style={{\n          padding: '10px 20px',\n          border: '1px solid #c3c3c3b8',\n          borderRadius: '4px',\n          textAlign: 'center',\n          fontSize: '12px',\n          cursor: 'pointer',\n        }}\n      >\n        {el.name}\n      </div>\n    );\n  });\n\n  // 禁用弹窗\n  if (props.disabled) {\n    return props.children;\n  }\n\n  return (\n    <Popover\n      onOpenChange={handleOpenChange}\n      open={open}\n      trigger={'hover'}\n      content={\n        <div\n          style={{\n            display: 'flex',\n            flexWrap: 'wrap',\n            width: '300px',\n            gap: '8px',\n            justifyContent: 'space-between',\n          }}\n        >\n          {nodeList.map((el) => (\n            <div\n              key={el.key}\n              style={{\n                flex: '0 0 calc(50% - 4px)',\n              }}\n            >\n              {el}\n            </div>\n          ))}\n        </div>\n      }\n      title={title}\n    >\n      {props.children}\n    </Popover>\n  );\n};\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ActionFlowSetter/component/CreateNewNodePopup/initData.ts",
    "content": "import {\n  AssignValueType,\n  getRandomStr,\n  LogicType,\n  TLogicAssignValueItem,\n  TLogicCallNodeMethodItem,\n  TLogicJumpLinkItem,\n  TLogicRequestAPIItem,\n  TLogicRunCodeItem,\n} from '@chamn/model';\n\nexport const DEFAULT_NODE_LIST = [\n  {\n    key: LogicType.ASSIGN_VALUE,\n    name: '赋值/创建变量',\n    getInitData: () => {\n      const id = getRandomStr();\n      const assignValueItem: TLogicAssignValueItem = {\n        id: id,\n        type: LogicType.ASSIGN_VALUE,\n        valueType: AssignValueType.STATE,\n        currentValue: '',\n        targetValueName: '',\n      };\n\n      return assignValueItem;\n    },\n  },\n  {\n    key: LogicType.JUMP_LINK,\n    name: '页面跳转',\n    getInitData: () => {\n      const id = getRandomStr();\n      const assignValueItem: TLogicJumpLinkItem = {\n        id: id,\n        type: LogicType.JUMP_LINK,\n        link: '',\n      };\n\n      return assignValueItem;\n    },\n  },\n  {\n    key: LogicType.REQUEST_API,\n    name: '请求数据',\n    getInitData: () => {\n      const id = getRandomStr();\n      const requestAPIItem: TLogicRequestAPIItem = {\n        id: id,\n        type: LogicType.REQUEST_API,\n        apiPath: '',\n        method: 'GET',\n        header: {},\n        query: {},\n        responseVarName: `responseData_${id}`,\n        afterSuccessResponse: [],\n        afterFailedResponse: [\n          {\n            id: getRandomStr(),\n            type: LogicType.RUN_CODE,\n            value: 'console.error(\"API request failed:\", $$response)',\n          },\n        ],\n      };\n\n      return requestAPIItem;\n    },\n  },\n  {\n    key: LogicType.CALL_NODE_METHOD,\n    name: '调用组件方法',\n    getInitData: () => {\n      const id = getRandomStr();\n\n      const callNodeMethodItem: TLogicCallNodeMethodItem = {\n        id: id,\n        type: LogicType.CALL_NODE_METHOD,\n        nodeId: '',\n        methodName: '',\n        args: [],\n        returnVarName: '',\n      };\n\n      return callNodeMethodItem;\n    },\n  },\n  {\n    key: LogicType.RUN_CODE,\n    name: '运行代码',\n    getInitData: () => {\n      const id = getRandomStr();\n\n      const runCodeItem: TLogicRunCodeItem = {\n        id: id,\n        type: LogicType.RUN_CODE,\n        name: `run_code_${id}`,\n        value: `console.log('hello world');`,\n      };\n\n      return runCodeItem;\n    },\n  },\n];\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ActionFlowSetter/component/InputHandle/index.tsx",
    "content": "import { Handle, HandleProps, Position } from '@xyflow/react';\nimport { INPUT_HANDLE_ID } from '../../config';\n\nexport const InputHandle = (props: Partial<HandleProps>) => {\n  return <Handle type=\"target\" id={INPUT_HANDLE_ID} position={Position.Top} {...props} />;\n};\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ActionFlowSetter/component/NodeCard/index.tsx",
    "content": "import { Card, CardProps } from 'antd';\nimport { OUTPUT_HANDLE_ID, REACT_FLOW_DRAG_CLASS_NAME } from '../../config';\nimport { HolderOutlined } from '@ant-design/icons';\nimport styles from './style.module.scss';\nimport { CreateNewNodePopup } from '../CreateNewNodePopup';\nimport { InputHandle } from '../InputHandle';\nimport { OutputHandle } from '../OutputHandle';\nimport { NodeProps, useReactFlow } from '@xyflow/react';\nimport { getNewNodePosInfo, UseNodeHasConnected } from '../../util';\nimport clsx from 'clsx';\n\nexport const NodeCard = ({\n  nodeProps,\n  customHandle,\n  handleNewNodeAdd: outHandleNewNodeAdd,\n  useCardStyle,\n  outputHandle,\n  inputHandle,\n  ...props\n}: CardProps & {\n  inputHandle?: boolean;\n  outputHandle?: boolean;\n  useCardStyle?: boolean;\n  nodeProps: NodeProps;\n  customHandle?: React.ReactNode;\n  handleNewNodeAdd?: (newNodeData: any) => void;\n}) => {\n  const { data, isConnectable, selected } = nodeProps;\n  const reactFlowInstance = useReactFlow();\n  const outputNodeHasConnected = UseNodeHasConnected(data, OUTPUT_HANDLE_ID);\n\n  const handleNewNodeAdd = (newNodeData: any) => {\n    const currentNode = reactFlowInstance.getNode(String(data.id));\n    if (!currentNode) {\n      console.error(`Not found node by id ${data.id}`);\n      return;\n    }\n    const { newEdge, newNode } = getNewNodePosInfo(currentNode, newNodeData);\n    // 更新流程图\n    reactFlowInstance.addNodes(newNode);\n    reactFlowInstance.addEdges(newEdge);\n  };\n\n  let customHandleView: any = (\n    <>\n      {inputHandle !== false && <InputHandle isConnectable={isConnectable} />}\n      {outputHandle !== false && (\n        <CreateNewNodePopup\n          style={{\n            position: 'absolute',\n            left: '50%',\n            bottom: '0',\n          }}\n          onNewNodeAdd={(data) => {\n            /** 外部有自定义 handle 时，取消事件触发 */\n            if (customHandle) {\n              return;\n            }\n            if (outHandleNewNodeAdd) {\n              outHandleNewNodeAdd(data);\n            } else {\n              handleNewNodeAdd(data);\n            }\n          }}\n          disabled={outputNodeHasConnected}\n        >\n          <OutputHandle\n            isConnectable={isConnectable}\n            style={{\n              width: '10px',\n              height: '10px',\n            }}\n          />\n        </CreateNewNodePopup>\n      )}\n    </>\n  );\n\n  if (customHandle) {\n    customHandleView = customHandle;\n  }\n\n  if (useCardStyle === false) {\n    return (\n      <div className={clsx([selected ? styles.selectStatus : ''])}>\n        <div className={REACT_FLOW_DRAG_CLASS_NAME}>{props.children}</div>\n        {customHandleView}\n      </div>\n    );\n  }\n\n  return (\n    <Card\n      {...props}\n      classNames={{\n        header: REACT_FLOW_DRAG_CLASS_NAME,\n      }}\n      className={clsx([selected ? styles.selectStatus : ''])}\n      styles={{\n        body: {\n          cursor: 'default',\n        },\n      }}\n      extra={\n        <div\n          style={{\n            display: 'flex',\n          }}\n        >\n          {props.extra}\n          <div className={styles.dragBox}>\n            <HolderOutlined />\n          </div>\n        </div>\n      }\n    >\n      {props.children}\n      <div className={clsx([REACT_FLOW_DRAG_CLASS_NAME, styles.l])}></div>\n      <div className={clsx([REACT_FLOW_DRAG_CLASS_NAME, styles.r])}></div>\n      <div className={clsx([REACT_FLOW_DRAG_CLASS_NAME, styles.b])}></div>\n      {customHandleView}\n    </Card>\n  );\n};\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ActionFlowSetter/component/NodeCard/style.module.scss",
    "content": ".l {\n  position: absolute;\n  left: 0;\n  top: 0;\n  height: 100%;\n  width: 20px;\n  cursor: grab;\n}\n\n.r {\n  position: absolute;\n  right: 0;\n  top: 0;\n  height: 100%;\n  width: 20px;\n  cursor: grab;\n}\n\n.b {\n  position: absolute;\n  right: 0;\n  bottom: 0;\n  width: 100%;\n  height: 20px;\n  cursor: grab;\n}\n\n.dragBox {\n  background-color: rgb(203 203 203 / 16%);\n  padding: 0 4px;\n  border-radius: 4px;\n  color: rgb(126 126 126 / 54%);\n}\n\n.selectStatus {\n  outline: 1px solid #f57dbd;\n  border-radius: 4px;\n  box-shadow: 0px 3.54px 4.55px 0px #00000005, 0px 3.54px 4.55px 0px #0000000d, 0px 0.51px 1.01px 0px #0000001a;\n}\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ActionFlowSetter/component/OutputHandle/index.tsx",
    "content": "import { Handle, HandleProps, Position } from '@xyflow/react';\nimport { OUTPUT_HANDLE_ID } from '../../config';\n\nexport const OutputHandle = (props: Partial<HandleProps>) => {\n  return <Handle type=\"source\" position={Position.Bottom} id={OUTPUT_HANDLE_ID} {...props} />;\n};\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ActionFlowSetter/component/SelectNodeByTree/index.tsx",
    "content": "import { Button } from 'antd';\nimport { useMemo, useState } from 'react';\nimport { CPage } from '@chamn/model';\nimport { SelectNodeModal } from './modal';\n\nexport const SelectNodeByTree = (props: {\n  pageModel: CPage;\n  onChange?: (data: { nodeId: string; title: string }) => void;\n  value?: any;\n}) => {\n  const [modalOpen, setModalOpen] = useState(false);\n  const [innerValue, setInnerValue] = useState<{ nodeId: string; title: string }>(props.value);\n\n  const nodeTitle = useMemo(() => {\n    const nodeInfo = props.pageModel.getNode(innerValue?.nodeId || props.value);\n\n    if (nodeInfo) {\n      return nodeInfo.value.title || nodeInfo.material?.value.title || innerValue?.title || '';\n    }\n    return '';\n  }, [props.pageModel, props?.value, innerValue?.nodeId, innerValue?.title]);\n\n  return (\n    <>\n      <Button\n        style={{\n          width: '200px',\n        }}\n        onClick={() => setModalOpen(true)}\n      >\n        {nodeTitle ?? '选择节点'}\n      </Button>\n      <SelectNodeModal\n        open={modalOpen}\n        onCancel={() => setModalOpen(false)}\n        onOk={(data) => {\n          props.onChange?.(data);\n          setInnerValue(data);\n          setModalOpen(false);\n        }}\n        pageModel={props.pageModel}\n        value={innerValue?.nodeId || props.value}\n      />\n    </>\n  );\n};\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ActionFlowSetter/component/SelectNodeByTree/modal.tsx",
    "content": "import { Modal, TreeSelect } from 'antd';\nimport { CPage } from '@chamn/model';\nimport { useRef, useMemo } from 'react';\nimport { transformPageSchemaToTreeData, traverseTree } from '@/plugins/OutlineTree/util';\nimport { getNodeInfo } from '../SelectNodeByTree/util';\n\ninterface SelectNodeModalProps {\n  open: boolean;\n  onCancel: () => void;\n  onOk: (data: { nodeId: string; title: string }) => void;\n  pageModel: CPage;\n  value?: string;\n}\n\nexport const SelectNodeModal = (props: SelectNodeModalProps) => {\n  const { open, onCancel, onOk, pageModel, value } = props;\n  const boxDomRef = useRef<HTMLDivElement>(null);\n\n  const treeData = useMemo(() => {\n    if (!pageModel) return;\n    const treeData = transformPageSchemaToTreeData(pageModel?.export(), pageModel);\n    traverseTree(treeData, (el: any) => {\n      el.sourceData = {\n        title: el.title,\n        value: el.value,\n      };\n      el.value = el.key;\n      el.title = <div style={{ flexWrap: 'nowrap', display: 'flex', width: '150px' }}>{el.title}</div>;\n\n      return false;\n    });\n    return treeData;\n  }, [pageModel]);\n\n  return (\n    <Modal open={open} onCancel={onCancel} title=\"选择节点\" onOk={() => onOk({ nodeId: value || '', title: '' })}>\n      <div ref={boxDomRef} style={{ width: '100%' }}>\n        <TreeSelect\n          virtual={false}\n          showSearch\n          style={{ width: '100%' }}\n          value={value}\n          allowClear\n          onClear={() => {\n            onOk({\n              nodeId: '',\n              title: '',\n            });\n          }}\n          filterTreeNode={(inputValue, treeNode: any) => {\n            return treeNode.sourceData?.title.toLowerCase().indexOf(inputValue.toLowerCase()) > -1;\n          }}\n          getPopupContainer={() => boxDomRef.current!}\n          treeDefaultExpandAll\n          onChange={(newVal) => {\n            const nodeInfo = getNodeInfo(newVal, (treeData as any) ?? []);\n            onOk({\n              nodeId: newVal,\n              title: (nodeInfo as any)?.sourceData?.title,\n            });\n          }}\n          treeData={treeData}\n        />\n      </div>\n    </Modal>\n  );\n};\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ActionFlowSetter/component/SelectNodeByTree/util.ts",
    "content": "import { TreeDataNode } from 'antd';\n\nexport const getParentKey = (key: React.Key, tree: TreeDataNode[]): React.Key => {\n  let parentKey: React.Key;\n  for (let i = 0; i < tree.length; i++) {\n    const node = tree[i];\n    if (node.children) {\n      if (node.children.some((item) => item.key === key)) {\n        parentKey = node.key;\n      } else if (getParentKey(key, node.children)) {\n        parentKey = getParentKey(key, node.children);\n      }\n    }\n  }\n  return parentKey!;\n};\n\nexport const getNodeInfo = (key: string, tree: TreeDataNode[]): TreeDataNode | null => {\n  const traverse = (nodes: TreeDataNode[]): TreeDataNode | null => {\n    for (const node of nodes) {\n      if (node.key === key) {\n        return node;\n      }\n      if (node.children?.length) {\n        const result = traverse(node.children);\n        if (result) {\n          return result;\n        }\n      }\n    }\n    return null;\n  };\n\n  return traverse(tree);\n};\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ActionFlowSetter/component/SelectNodeState/index.tsx",
    "content": "import { CPage } from '@chamn/model';\nimport { SelectNodeByTree } from '../SelectNodeByTree';\nimport { Input, Space } from 'antd';\n\nexport const SelectNodeState = (props: {\n  value?: {\n    nodeId: string;\n    keyPath: string;\n  };\n  pageModel: CPage;\n  onChange?: (data: { nodeId: string; keyPath: string }) => void;\n}) => {\n  return (\n    <div>\n      <Space>\n        <SelectNodeByTree\n          pageModel={props.pageModel}\n          value={props.value}\n          onChange={(data) => {\n            props.onChange?.({\n              nodeId: data.nodeId,\n              keyPath: props.value?.keyPath || '',\n            });\n          }}\n        />\n        <Input\n          style={{\n            width: 250,\n          }}\n          value={props.value?.keyPath}\n          onChange={(e) => {\n            const { value: inputValue } = e.target;\n            props.onChange?.({\n              nodeId: props.value?.nodeId || '',\n              keyPath: String(inputValue),\n            });\n          }}\n          placeholder=\"variable name. support '.'\"\n        />\n      </Space>\n    </div>\n  );\n};\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ActionFlowSetter/component/SelectNodeState/util.ts",
    "content": "import { TreeDataNode } from 'antd';\nimport { Key } from 'react';\n\nexport const getParentKey = (key: React.Key, tree: TreeDataNode[]): React.Key => {\n  let parentKey: React.Key;\n  for (let i = 0; i < tree.length; i++) {\n    const node = tree[i];\n    if (node.children) {\n      if (node.children.some((item) => item.key === key)) {\n        parentKey = node.key;\n      } else if (getParentKey(key, node.children)) {\n        parentKey = getParentKey(key, node.children);\n      }\n    }\n  }\n  return parentKey!;\n};\n\nexport const generateKeyList = (data: TreeDataNode[]) => {\n  let dataList: { title: string; key: Key }[] = [];\n  for (let i = 0; i < data.length; i++) {\n    const node = data[i];\n    const { key } = node;\n    dataList.push({ key, title: node.title as string });\n    if (node.children) {\n      const res = generateKeyList(node.children);\n      dataList = [...dataList, ...res];\n    }\n  }\n\n  return dataList;\n};\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ActionFlowSetter/config.ts",
    "content": "export const REACT_FLOW_DRAG_CLASS_NAME = 'chamn-action-drag-handler';\n\nexport const INPUT_HANDLE_ID = 'INPUT_HANDLE_ID';\n\nexport const OUTPUT_HANDLE_ID = 'OUTPUT_HANDLE_ID';\n\nexport const REQUEST_API_FAILED_HANDLE_ID = 'REQUEST_API_FAILED_HANDLE_ID';\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ActionFlowSetter/context.ts",
    "content": "import { createContext, useContext } from 'react';\nimport { CNode, CPage } from '@chamn/model';\nimport { CPluginCtx } from '@/core/pluginManager';\n\ninterface ActionFlowContextType {\n  pluginCtx: CPluginCtx;\n  pageModel: CPage;\n  /** 数据有改变时，包含节点内部的数据 */\n  onDataChange: () => void;\n  /** 当前节点 */\n  nodeModel: CNode | null;\n}\n\nexport const ActionFlowContext = createContext<ActionFlowContextType>({\n  pluginCtx: null as any,\n  pageModel: null as any,\n  onDataChange: () => {},\n  nodeModel: null,\n});\n\nexport const useActionFlow = () => {\n  const context = useContext(ActionFlowContext);\n  if (!context) {\n    throw new Error('useActionFlow must be used within ActionFlowProvider');\n  }\n  return context;\n};\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ActionFlowSetter/index.tsx",
    "content": "import {\n  addEdge,\n  Background,\n  Controls,\n  MiniMap,\n  OnConnect,\n  ReactFlow,\n  ReactFlowProvider,\n  useEdgesState,\n  useNodesState,\n  useReactFlow,\n  Node,\n} from '@xyflow/react';\nimport '@xyflow/react/dist/style.css';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { TActionLogicItem } from '@chamn/model';\nimport { calculateElementLayout, parseActionLogicToNodeList, revertNodeToActionLogic } from './util';\nimport { NODE_MAP, NODE_TYPE } from './node';\nimport { CSetterProps } from '../type';\nimport { REACT_FLOW_DRAG_CLASS_NAME } from './config';\nimport { ActionFlowContext } from './context';\nimport { Button } from 'antd';\nimport { MoveableModal } from '@/component/MoveableModal';\nimport { HotKeysPluginInstance } from '@/plugins/Hotkeys/type';\n\nexport type TActionFlowSetterCore = CSetterProps<{\n  value?: TActionLogicItem;\n  children?: React.ReactNode;\n}>;\n\nexport const ActionFlowSetterCore = (props: TActionFlowSetterCore) => {\n  const { fitView } = useReactFlow();\n  const [flowMount, setFlowMount] = useState(false);\n  const [nodes, setNodes, onNodesChange] = useNodesState<any>([\n    {\n      id: NODE_TYPE.START_NODE,\n      data: { id: NODE_TYPE.START_NODE },\n      position: { x: 0, y: 0 },\n      type: NODE_TYPE.START_NODE,\n      dragHandle: `.${REACT_FLOW_DRAG_CLASS_NAME}`,\n      selectable: false,\n    },\n  ]);\n  const [edges, setEdges, onEdgesChange] = useEdgesState<any>([]);\n\n  const latesEdgesRef = useRef<any[]>([]);\n  latesEdgesRef.current = edges;\n\n  const onConnect = useCallback<OnConnect>((params) => setEdges((eds) => addEdge({ ...params }, eds)), [setEdges]);\n\n  const latestNodeRef = useRef<Node[]>([]);\n  latestNodeRef.current = nodes;\n\n  const [dataReady, setDataReady] = useState(false);\n\n  /** 重新布局 */\n  const layoutGraph = useCallback(\n    (options?: { fitView: boolean }) => {\n      const layoutInfo = calculateElementLayout(latestNodeRef.current, edges, { direction: 'TB' });\n      setNodes([...layoutInfo.nodes]);\n      setEdges([...layoutInfo.edges]);\n      setTimeout(() => {\n        setFlowMount(true);\n        if (options?.fitView !== false) {\n          fitView({});\n        }\n      });\n    },\n    [edges, fitView, setEdges, setNodes]\n  );\n\n  useEffect(() => {\n    // 将 value 转换为 nodes 以及 edges\n    const { nodes, edges } = parseActionLogicToNodeList(props.value);\n    setNodes(nodes);\n    setEdges(edges);\n    setTimeout(() => {\n      setDataReady(true);\n    }, 300);\n  }, [props.value, setEdges, setNodes]);\n\n  const saveData = useCallback(() => {\n    setTimeout(() => {\n      const newSchemaValue = revertNodeToActionLogic({ nodes: latestNodeRef.current, edges: latesEdgesRef.current });\n      props.onValueChange?.(newSchemaValue);\n    });\n  }, [props]);\n\n  return (\n    <ActionFlowContext.Provider\n      value={{\n        pluginCtx: props.setterContext?.pluginCtx,\n        pageModel: props.setterContext?.pluginCtx?.pageModel,\n        onDataChange: saveData,\n        nodeModel: props.setterContext.nodeModel,\n      }}\n    >\n      <div\n        style={{\n          width: '100%',\n          height: '100%',\n          display: 'flex',\n          position: 'relative',\n        }}\n      >\n        <div\n          style={{\n            flex: 1,\n            position: 'relative',\n          }}\n        >\n          <Button\n            style={{\n              position: 'absolute',\n              top: '10px',\n              right: '20px',\n              zIndex: 99,\n            }}\n            onClick={() => layoutGraph({ fitView: false })}\n          >\n            Reset Layout\n          </Button>\n\n          {!flowMount && (\n            <div\n              style={{\n                position: 'absolute',\n                left: 0,\n                top: 0,\n                width: '100%',\n                height: '100%',\n                backgroundColor: 'white',\n                zIndex: 999,\n              }}\n            ></div>\n          )}\n          {dataReady && (\n            <ReactFlow\n              nodes={nodes}\n              edges={edges}\n              onNodesChange={(changes) => {\n                onNodesChange(changes);\n                saveData();\n              }}\n              onEdgesChange={(changes) => {\n                onEdgesChange(changes);\n                saveData();\n              }}\n              onConnect={(connection) => {\n                onConnect(connection);\n                saveData();\n              }}\n              defaultEdgeOptions={{\n                type: 'smoothstep',\n              }}\n              minZoom={0.2}\n              maxZoom={1}\n              onInit={() => {\n                layoutGraph();\n              }}\n              fitView\n              nodeTypes={NODE_MAP}\n            >\n              <Background />\n              <Controls />\n              <MiniMap />\n            </ReactFlow>\n          )}\n        </div>\n      </div>\n    </ActionFlowContext.Provider>\n  );\n};\n\nexport const ActionFlowSetter = (props: TActionFlowSetterCore) => {\n  const [open, setOpen] = useState(false);\n\n  const newValueRef = useRef(props.value);\n\n  const disableLowcodeHotKey = async (status: boolean) => {\n    // 启用 lowcode 编辑器热键\n    const hotkey = await props.setterContext?.pluginCtx?.pluginManager?.get<HotKeysPluginInstance>('Hotkeys');\n    hotkey?.export.disable(status);\n  };\n\n  const triggerView = props.children || (\n    <Button\n      size=\"small\"\n      style={{\n        marginTop: '5px',\n        width: '100%',\n        color: '#676767',\n        fontSize: '12px',\n      }}\n    >\n      Edit Flow\n    </Button>\n  );\n\n  return (\n    <>\n      <div\n        onClick={async () => {\n          // 禁用 lowcode 编辑器热键\n          await disableLowcodeHotKey(true);\n          setOpen(true);\n        }}\n      >\n        {triggerView}\n      </div>\n\n      <MoveableModal\n        destroyOnClose\n        open={open}\n        centered\n        title=\"Edit Flow\"\n        width=\"calc(100vw - 200px)\"\n        onCancel={() => {\n          setOpen(false);\n          disableLowcodeHotKey(false);\n        }}\n        onOk={async () => {\n          props.onValueChange?.(newValueRef.current);\n          await disableLowcodeHotKey(false);\n          setOpen(false);\n        }}\n      >\n        <div\n          style={{\n            width: '100%',\n            height: 'calc(100vh - 200px)',\n          }}\n        >\n          <ReactFlowProvider>\n            <ActionFlowSetterCore\n              {...props}\n              onValueChange={(newVal) => {\n                newValueRef.current = newVal;\n              }}\n            ></ActionFlowSetterCore>\n          </ReactFlowProvider>\n        </div>\n      </MoveableModal>\n    </>\n  );\n};\n\nActionFlowSetter.setterName = '逻辑流设置器';\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ActionFlowSetter/node/AssignValueNode/index.tsx",
    "content": "import { BUILD_IN_SETTER_MAP, CustomSchemaFormInstance } from '@/component/CustomSchemaForm';\nimport { AssignValueType, DEV_CONFIG_KEY, TLogicAssignValueItem } from '@chamn/model';\nimport { NodeProps, Node } from '@xyflow/react';\nimport { Radio } from 'antd';\nimport { useEffect, useRef, useState } from 'react';\nimport { CForm } from '../../../../Form';\nimport { CField } from '../../../../Form/Field';\nimport { SelectNodeState } from '../../component/SelectNodeState/index';\nimport styles from './style.module.scss';\nimport { TTextAreaSetterProps } from '../../../TextAreaSetter';\nimport { isValidJSVariableName } from './util';\nimport { CCustomSchemaFormContext } from '@/component/CustomSchemaForm/context';\nimport { ensureKeyExist } from '@/utils';\nimport { NodeCard } from '../../component/NodeCard';\nimport { CommonDynamicValueSetter } from '../../util';\nimport { useActionFlow } from '../../context';\nimport { CFiledWithSwitchSetter } from '@/component/CustomSchemaForm/components/CFiledWithSwitchSetter';\n\nexport type TAssignValueNode = Node<TLogicAssignValueItem, 'AssignValueNode'>;\n\nexport const AssignValueNode = (props: NodeProps<TAssignValueNode>) => {\n  const { data } = props;\n  const { onDataChange, pageModel, pluginCtx, nodeModel } = useActionFlow();\n  ensureKeyExist(data, DEV_CONFIG_KEY, {});\n  const devConfigObj = data[DEV_CONFIG_KEY]!;\n  const [isReady, setIsReady] = useState(false);\n  const formRef = useRef<CustomSchemaFormInstance>(null);\n  const [formValue, setFormValue] = useState<TLogicAssignValueItem>();\n  useEffect(() => {\n    const newVal = {\n      id: data.id,\n      type: data.type,\n      valueType: data.valueType,\n      currentValue: data.currentValue,\n      targetValueName: data.targetValueName || '',\n    };\n    formRef.current?.setFields(newVal);\n    setFormValue(newVal);\n    setIsReady(true);\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n\n  return (\n    <CCustomSchemaFormContext.Provider\n      value={{\n        defaultSetterConfig: devConfigObj.defaultSetterMap || {},\n        formRef: formRef,\n        onSetterChange: (keyPaths, setterName) => {\n          if (!devConfigObj.defaultSetterMap) {\n            devConfigObj.defaultSetterMap = {};\n          }\n          devConfigObj.defaultSetterMap[keyPaths.join('.')] = {\n            name: keyPaths.join('.'),\n            setter: setterName,\n          };\n        },\n        pluginCtx: pluginCtx,\n        nodeId: nodeModel?.id,\n        customSetterMap: {},\n      }}\n    >\n      <div\n        style={{\n          minHeight: '80px',\n          minWidth: '100px',\n        }}\n      >\n        <NodeCard title=\"Assign Value\" nodeProps={props}>\n          <CForm\n            ref={formRef}\n            name=\"jump Link\"\n            customSetterMap={BUILD_IN_SETTER_MAP}\n            onValueChange={(newVal) => {\n              Object.assign(data, newVal);\n              setFormValue(newVal as any);\n              onDataChange();\n            }}\n          >\n            {isReady && (\n              <>\n                <div className={styles.line}>\n                  <CField\n                    name={'valueType'}\n                    label=\"Var Type\"\n                    valueChangeEventName=\"onChange\"\n                    formatEventValue={({ target: { value } }: any) => {\n                      return value;\n                    }}\n                  >\n                    <Radio.Group\n                      optionType=\"button\"\n                      buttonStyle=\"solid\"\n                      options={[\n                        { label: 'Memory', value: AssignValueType.MEMORY },\n                        { label: 'Node', value: AssignValueType.STATE },\n                      ]}\n                    />\n                  </CField>\n                </div>\n                <div className={styles.line}>\n                  {formValue?.valueType === 'STATE' && (\n                    <CField name={'targetValueName'} label=\"Var Name\" valueChangeEventName=\"onChange\">\n                      <SelectNodeState pageModel={pageModel} />\n                    </CField>\n                  )}\n\n                  {formValue?.valueType === 'MEMORY' && (\n                    <CFiledWithSwitchSetter\n                      name={'targetValueName'}\n                      label=\"Var Name\"\n                      labelWidth=\"auto\"\n                      labelAlign={'start'}\n                      tips={\n                        '变量名必须以字母（a-z、A-Z）、下划线（_）或美元符号（$）开头。后续字符可以是字母、数字（0-9）、下划线或美元符号。变量名不能是保留关键字（例如 if、while 等）'\n                      }\n                      setterList={[\n                        {\n                          componentName: 'TextAreaSetter',\n                          props: {\n                            valueValidator: isValidJSVariableName,\n                          } as TTextAreaSetterProps,\n                        },\n                      ]}\n                    ></CFiledWithSwitchSetter>\n                  )}\n                </div>\n                <div\n                  className={styles.line}\n                  style={{\n                    minWidth: '450px',\n                  }}\n                >\n                  <CFiledWithSwitchSetter\n                    name={'currentValue'}\n                    label=\"Value\"\n                    labelWidth=\"60px\"\n                    labelAlign={'start'}\n                    setterList={CommonDynamicValueSetter}\n                  ></CFiledWithSwitchSetter>\n                </div>\n              </>\n            )}\n          </CForm>\n        </NodeCard>\n      </div>\n    </CCustomSchemaFormContext.Provider>\n  );\n};\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ActionFlowSetter/node/AssignValueNode/style.module.scss",
    "content": ".line {\n  padding-bottom: 10px;\n}\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ActionFlowSetter/node/AssignValueNode/util.ts",
    "content": "export function isValidJSVariableName(name: string) {\n  // 1. 使用正则表达式验证变量名规则\n  const identifierRegex = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;\n  if (!identifierRegex.test(name)) {\n    return false;\n  }\n\n  // 2. 检测是否为保留关键字\n  try {\n    new Function(`let ${name};`);\n    return true;\n  } catch {\n    return false;\n  }\n}\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ActionFlowSetter/node/CallNodeMethodNode/index.tsx",
    "content": "import { BUILD_IN_SETTER_MAP, CustomSchemaForm, CustomSchemaFormInstance } from '@/component/CustomSchemaForm';\nimport { DEV_CONFIG_KEY, TLogicCallNodeMethodItem } from '@chamn/model';\nimport { NodeProps, Node } from '@xyflow/react';\nimport { Input, Select } from 'antd';\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { CForm } from '../../../../Form';\nimport styles from './style.module.scss';\nimport { CCustomSchemaFormContext } from '@/component/CustomSchemaForm/context';\nimport { SelectNodeByTree } from '../../component/SelectNodeByTree';\nimport { CField } from '@/component/CustomSchemaForm/components/Form/Field';\nimport { formatArgsObjToArray, formatArgsToObject, getArgsObjFormSchema, isValidJSVariableName } from './util';\nimport { ensureKeyExist } from '@/utils';\nimport { NodeCard } from '../../component/NodeCard';\nimport { useActionFlow } from '../../context';\n\nexport type TCallNodeMethodNode = Node<TLogicCallNodeMethodItem, 'CallNodeMethodNode'>;\n\nexport const CallNodeMethodNode = (props: NodeProps<TCallNodeMethodNode>) => {\n  const { data } = props;\n  const { pageModel, onDataChange, pluginCtx, nodeModel } = useActionFlow();\n  ensureKeyExist(data, DEV_CONFIG_KEY, {});\n  const devConfigObj = data[DEV_CONFIG_KEY]!;\n\n  const formRef = useRef<CustomSchemaFormInstance>(null);\n  const [formValue, setFormValue] = useState<TLogicCallNodeMethodItem>();\n  useEffect(() => {\n    const newVal = {\n      id: data.id || '',\n      type: data.type,\n      nodeId: data.nodeId,\n      methodName: data.methodName,\n      args: data.args,\n      returnVarName: data.returnVarName,\n    };\n    formRef.current?.setFields(newVal);\n\n    setFormValue(newVal);\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n\n  const methodList = useMemo(() => {\n    const targetNode = pageModel.getNode(data.nodeId);\n    const list = targetNode?.material?.value.methods || [];\n    return list;\n  }, [data.nodeId, pageModel]);\n\n  const methodListOptions = useMemo(() => {\n    return methodList?.map((el) => {\n      return {\n        value: el.name,\n        label: el.title,\n      };\n    });\n  }, [methodList]);\n\n  const argsFormSchema = useMemo(() => {\n    return getArgsObjFormSchema(pageModel.getNode(formValue?.nodeId)!, formValue?.methodName || '');\n  }, [pageModel, formValue?.nodeId, formValue?.methodName]);\n\n  const updateKeySetterConfig = (keyPaths: string[], setterName: string) => {\n    if (!devConfigObj.defaultSetterMap) {\n      devConfigObj.defaultSetterMap = {};\n    }\n    devConfigObj.defaultSetterMap[keyPaths.join('.')] = {\n      name: keyPaths.join('.'),\n      setter: setterName,\n    };\n  };\n\n  return (\n    <CCustomSchemaFormContext.Provider\n      value={{\n        defaultSetterConfig: devConfigObj.defaultSetterMap || {},\n        formRef: formRef,\n        onSetterChange: updateKeySetterConfig,\n        customSetterMap: { ...BUILD_IN_SETTER_MAP },\n      }}\n    >\n      <div\n        style={{\n          minHeight: '80px',\n          minWidth: '100px',\n        }}\n      >\n        <NodeCard title=\"Call Node Method\" nodeProps={props}>\n          <CForm\n            ref={formRef}\n            name=\"Call Node Method\"\n            customSetterMap={BUILD_IN_SETTER_MAP}\n            onValueChange={(newVal, changeKeys) => {\n              if (changeKeys?.includes('nodeId')) {\n                newVal = {\n                  ...newVal,\n                  methodName: '',\n                  args: [],\n                };\n                setTimeout(() => {\n                  formRef.current?.setFields({\n                    ...formValue,\n                    ...newVal,\n                  });\n                });\n              }\n              Object.assign(data, newVal);\n              setFormValue(newVal as any);\n              onDataChange();\n            }}\n          >\n            <div className={styles.line}>\n              <CField\n                label={'组件'}\n                name=\"nodeId\"\n                valueChangeEventName=\"onChange\"\n                formatEventValue={(el) => {\n                  return el.nodeId;\n                }}\n              >\n                <SelectNodeByTree pageModel={pageModel} />\n              </CField>\n            </div>\n\n            <div className={styles.line}>\n              <CField name=\"methodName\" label=\"方法\" valueChangeEventName=\"onChange\">\n                <Select style={{ width: 250 }} allowClear options={methodListOptions}></Select>\n              </CField>\n            </div>\n            <div className={styles.line}>\n              <CField\n                name={'args'}\n                label=\"参数\"\n                labelWidth=\"60px\"\n                labelAlign={'start'}\n                condition={() => Boolean(argsFormSchema.length)}\n                noStyle\n                formatEventValue={(val) => {\n                  const newVal = formatArgsObjToArray(val);\n                  return newVal;\n                }}\n              >\n                <CustomSchemaForm\n                  initialValue={formatArgsToObject(data.args || [])}\n                  pluginCtx={pluginCtx}\n                  nodeId={nodeModel?.id}\n                  properties={argsFormSchema}\n                  onSetterChange={updateKeySetterConfig}\n                  defaultSetterConfig={devConfigObj.defaultSetterMap || {}}\n                ></CustomSchemaForm>\n              </CField>\n            </div>\n\n            <div className={styles.line}>\n              <CField\n                name={'returnVarName'}\n                label=\"返回值变量名\"\n                labelWidth=\"80px\"\n                tips={\n                  '变量名必须以字母（a-z、A-Z）、下划线（_）或美元符号（$）开头。后续字符可以是字母、数字（0-9）、下划线或美元符号。变量名不能是保留关键字（例如 if、while 等）'\n                }\n                rules={[\n                  {\n                    validator: async (val) => {\n                      if (val === '') {\n                        return true;\n                      }\n                      return isValidJSVariableName(val);\n                    },\n                  },\n                ]}\n                valueChangeEventName=\"onChange\"\n                formatEventValue={(e) => e.target.value}\n              >\n                <Input allowClear />\n              </CField>\n            </div>\n          </CForm>\n        </NodeCard>\n      </div>\n    </CCustomSchemaFormContext.Provider>\n  );\n};\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ActionFlowSetter/node/CallNodeMethodNode/style.module.scss",
    "content": ".line {\n  padding-bottom: 10px;\n}\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ActionFlowSetter/node/CallNodeMethodNode/util.ts",
    "content": "import { CMaterialPropsType, CPageNode, isNodeModel } from '@chamn/model';\nimport { CommonDynamicValueSetter } from '../../util';\n\nexport function isValidJSVariableName(name: string) {\n  // 1. 使用正则表达式验证变量名规则\n  const identifierRegex = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;\n  if (!identifierRegex.test(name)) {\n    return false;\n  }\n\n  // 2. 检测是否为保留关键字\n  try {\n    new Function(`let ${name};`);\n    return true;\n  } catch {\n    return false;\n  }\n}\n\nconst ARGS_PREFIX = 'args.';\n/**\n *\n * @param node 结合 method 描述，合并为一个 FormSchema, 加 key 前缀是为了避免 key 冲突\n * @param args\n * @returns\n */\nexport const getArgsObjFormSchema = (node: CPageNode, methodName: string) => {\n  if (isNodeModel(node)) {\n    const methodList = node?.material?.value.methods || [];\n\n    const formSchema = methodList\n      .find((el) => el.name === methodName)\n      ?.params?.map((el, index) => {\n        return {\n          name: `${ARGS_PREFIX}${index}`,\n          title: {\n            label: el.name || `arg[${index}]`,\n            tip: el.description,\n          },\n          valueType: 'string',\n          setters: CommonDynamicValueSetter,\n        } as CMaterialPropsType[number];\n      });\n\n    return formSchema || [];\n  }\n  return [];\n};\n\nexport const formatArgsObjToArray = (val: Record<string, any>) => {\n  const res = Object.keys(val)\n    .map((key) => parseInt(key.replace(ARGS_PREFIX, '')))\n    .sort()\n    .map((index) => val[`${ARGS_PREFIX}${index}`]);\n  return res;\n};\n\nexport const formatArgsToObject = (valArr: any[]) => {\n  const res: any = {};\n  valArr.forEach((item, index) => {\n    res[`${ARGS_PREFIX}${index}`] = item;\n  });\n  return res;\n};\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ActionFlowSetter/node/JumpLinkNode.tsx",
    "content": "import { BUILD_IN_SETTER_MAP, CustomSchemaFormInstance } from '@/component/CustomSchemaForm';\nimport { isExpression, isFunction, TLogicJumpLinkItem } from '@chamn/model';\nimport { NodeProps, Node } from '@xyflow/react';\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { CForm } from '../../../Form';\nimport { NodeCard } from '../component/NodeCard';\nimport { CommonDynamicValueSetter } from '../util';\nimport { useActionFlow } from '../context';\nimport { CFiledWithSwitchSetter } from '../../../CFiledWithSwitchSetter';\n\nexport type TJumpLinkNode = Node<TLogicJumpLinkItem, 'JumpLinkNode'>;\n\nexport const JumpLinkNode = (props: NodeProps<TJumpLinkNode>) => {\n  const { data } = props;\n  const { onDataChange } = useActionFlow();\n\n  const formRef = useRef<CustomSchemaFormInstance>(null);\n  const [isReady, setIsReady] = useState(false);\n\n  useEffect(() => {\n    formRef.current?.setFields({\n      link: data.link,\n    });\n    setIsReady(true);\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n\n  const defaultLinkSetter = useMemo(() => {\n    if (isFunction(data.link)) {\n      return 'FunctionSetter';\n    } else if (isExpression(data.link)) {\n      return 'ExpressionSetter';\n    } else {\n      return 'TextAreaSetter';\n    }\n  }, [data.link]);\n\n  return (\n    <div\n      style={{\n        minHeight: '80px',\n        minWidth: '100px',\n      }}\n    >\n      <NodeCard title=\"Jump Link\" nodeProps={props}>\n        <CForm\n          ref={formRef}\n          name=\"jump Link\"\n          customSetterMap={BUILD_IN_SETTER_MAP}\n          onValueChange={(newVal) => {\n            Object.assign(data, newVal);\n            onDataChange();\n          }}\n        >\n          {isReady && (\n            <>\n              <CFiledWithSwitchSetter\n                name={'link'}\n                label=\"link\"\n                labelWidth=\"auto\"\n                labelAlign={'start'}\n                defaultSetterName={defaultLinkSetter}\n                setterList={CommonDynamicValueSetter}\n              ></CFiledWithSwitchSetter>\n            </>\n          )}\n        </CForm>\n      </NodeCard>\n    </div>\n  );\n};\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ActionFlowSetter/node/RequestAPINode/helper.ts",
    "content": "import { SetterType } from '@chamn/model';\n\nexport const requestParamsSchemaSetterList: SetterType[] = [\n  {\n    componentName: 'JSONSetter',\n    labelAlign: 'start',\n    props: {\n      mode: 'inline',\n      lineNumbers: 'off',\n      editorOptions: {\n        lineNumbers: 'off',\n        lineDecorationsWidth: 0,\n        lineNumbersMinChars: 0,\n        glyphMargin: false,\n      },\n    },\n  },\n  {\n    componentName: 'FunctionSetter',\n    labelAlign: 'start',\n    props: {\n      mode: 'inline',\n      minimap: false,\n      containerStyle: {\n        paddingTop: '10px',\n        width: '470px',\n        height: '150px',\n      },\n      lineNumber: false,\n    },\n  },\n];\n\nexport const methodOptions = [\n  { value: 'GET', label: 'GET' },\n  { value: 'POST', label: 'POST' },\n  { value: 'PUT', label: 'PUT' },\n  { value: 'PATCH', label: 'PATCH' },\n  { value: 'DELETE', label: 'DELETE' },\n];\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ActionFlowSetter/node/RequestAPINode/index.tsx",
    "content": "import { DEV_CONFIG_KEY, TLogicRequestAPIItem } from '@chamn/model';\nimport { useReactFlow, NodeProps, Node } from '@xyflow/react';\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { Input, Select, Tabs, TabsProps } from 'antd';\nimport { BUILD_IN_SETTER_MAP, CustomSchemaFormInstance } from '@/component/CustomSchemaForm';\n\nimport { ensureKeyExist } from '@/utils';\nimport { NodeCard } from '../../component/NodeCard';\nimport { CForm } from '@/component/CustomSchemaForm/components/Form';\nimport { CCustomSchemaFormContext } from '@/component/CustomSchemaForm/context';\nimport { CField } from '@/component/CustomSchemaForm/components/Form/Field';\nimport { methodOptions, requestParamsSchemaSetterList } from './helper';\nimport { CreateNewNodePopup } from '../../component/CreateNewNodePopup';\nimport { InputHandle } from '../../component/InputHandle';\nimport { OutputHandle } from '../../component/OutputHandle';\nimport {\n  INPUT_HANDLE_ID,\n  OUTPUT_HANDLE_ID,\n  REACT_FLOW_DRAG_CLASS_NAME,\n  REQUEST_API_FAILED_HANDLE_ID,\n} from '../../config';\nimport styles from './style.module.scss';\nimport { useActionFlow } from '../../context';\nimport { CFiledWithSwitchSetter } from '@/component/CustomSchemaForm/components/CFiledWithSwitchSetter';\nimport { RightPanelConfig } from '@/plugins/RightPanel/type';\n\nexport type TRequestAPINode = Node<TLogicRequestAPIItem, 'RequestAPINode'>;\n\nexport const RequestAPINode = (props: NodeProps<TRequestAPINode>) => {\n  const { data, isConnectable } = props;\n  const { onDataChange, pluginCtx } = useActionFlow();\n  const pluginConfig: RightPanelConfig = pluginCtx.config || {};\n\n  const CustomAPISelectInput: any = useMemo(() => {\n    if (pluginConfig.requestAPINode?.customAPIInput) {\n      return pluginConfig.requestAPINode?.customAPIInput;\n    }\n    return Input;\n  }, [pluginConfig.requestAPINode?.customAPIInput]);\n\n  const reactFlowInstance = useReactFlow();\n  ensureKeyExist(data, DEV_CONFIG_KEY, {});\n  const devConfigObj = data[DEV_CONFIG_KEY]!;\n  const formRef = useRef<CustomSchemaFormInstance>(null);\n  const [formValue, setFormValue] = useState<TLogicRequestAPIItem>();\n  const checkHandleConnection = (handleId: string) => {\n    const edges = reactFlowInstance.getEdges();\n    return edges.some(\n      (edge) => edge.source === String(data.id) && (edge.sourceHandle || OUTPUT_HANDLE_ID) === handleId\n    );\n  };\n\n  const isOutputHandleConnected = checkHandleConnection(OUTPUT_HANDLE_ID);\n\n  const isAfterFailedResponseHandleConnected = checkHandleConnection(REQUEST_API_FAILED_HANDLE_ID);\n\n  useEffect(() => {\n    const newVal = {\n      id: data.id,\n      type: data.type,\n      apiPath: data.apiPath,\n      body: data.body,\n      query: data.query,\n      header: data.header,\n      method: data.method || 'GET',\n      responseVarName: data.responseVarName || '',\n      afterSuccessResponse: data.afterSuccessResponse || [],\n      afterFailedResponse: data.afterFailedResponse || [],\n    } as TLogicRequestAPIItem;\n    formRef.current?.setFields(newVal);\n    setFormValue(newVal);\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n\n  const formHandler = useMemo(() => {\n    return {\n      updateFields: (newValue: any) => {\n        const newValueObj = {\n          ...formValue,\n          ...newValue,\n        };\n        formRef.current?.setFields({\n          ...newValueObj,\n        });\n        setFormValue(newValueObj);\n      },\n      getFields: () => {\n        return formRef.current?.getFieldsValue();\n      },\n    };\n  }, [formValue]);\n\n  const updateKeySetterConfig = (keyPaths: string[], setterName: string) => {\n    if (!devConfigObj.defaultSetterMap) {\n      devConfigObj.defaultSetterMap = {};\n    }\n    devConfigObj.defaultSetterMap[keyPaths.join('.')] = {\n      name: keyPaths.join('.'),\n      setter: setterName,\n    };\n  };\n\n  const tabItems = useMemo(() => {\n    const tabTagList = [\n      { key: 'header', label: 'Header' },\n      { key: 'query', label: 'Query' },\n      { key: 'body', label: 'Body' },\n    ];\n    const items: TabsProps['items'] = tabTagList.map((el) => {\n      return {\n        ...el,\n        disabled: el.key === 'body' && formValue?.method === 'GET',\n        children: (\n          <div className={styles.line}>\n            <CFiledWithSwitchSetter\n              name={el.key}\n              hiddenLabel={true}\n              labelAlign={'start'}\n              setterList={requestParamsSchemaSetterList}\n            ></CFiledWithSwitchSetter>\n          </div>\n        ),\n      };\n    });\n\n    return items;\n  }, [formValue]);\n\n  const handleNewNodeAdd = (newNodeData: any, handleType: string) => {\n    const currentNode = reactFlowInstance.getNode(String(data.id));\n    if (!currentNode) return;\n\n    let offsetX = 0;\n    if (handleType !== OUTPUT_HANDLE_ID) {\n      offsetX = (currentNode.measured?.width ?? 0) + 150;\n    }\n    // 计算新节点位置\n    const newNodePosition = {\n      x: currentNode.position.x + offsetX,\n      y: currentNode.position.y + (currentNode.measured?.height ?? 0) + 150,\n    };\n\n    // 创建新节点\n    const newNode = {\n      id: newNodeData.id,\n      type: newNodeData.type,\n      position: newNodePosition,\n      /** 必须 */\n      dragHandle: `.${REACT_FLOW_DRAG_CLASS_NAME}`,\n      data: {\n        ...newNodeData,\n      },\n    };\n\n    // 创建连线\n    const newEdge = {\n      id: `${data.id}_${newNode.id}`,\n      source: String(data.id),\n      sourceHandle: handleType,\n      target: newNode.id,\n      targetHandle: INPUT_HANDLE_ID,\n    };\n\n    // 更新流程图\n    reactFlowInstance.addNodes(newNode);\n    reactFlowInstance.addEdges(newEdge);\n    if (handleType === OUTPUT_HANDLE_ID) {\n      // 更新当前节点的 afterSuccessResponse\n      if (!data.afterSuccessResponse) {\n        data.afterSuccessResponse = [];\n      }\n      data.afterSuccessResponse.push(newNodeData);\n    } else {\n      // 更新当前节点的 afterFailedResponse\n      if (!data.afterFailedResponse) {\n        data.afterFailedResponse = [];\n      }\n      data.afterFailedResponse.push(newNodeData);\n    }\n  };\n\n  return (\n    <CCustomSchemaFormContext.Provider\n      value={{\n        defaultSetterConfig: devConfigObj.defaultSetterMap || {},\n        formRef: formRef,\n        onSetterChange: updateKeySetterConfig,\n        customSetterMap: { ...BUILD_IN_SETTER_MAP },\n      }}\n    >\n      <div\n        style={{\n          minHeight: '80px',\n          minWidth: '100px',\n        }}\n      >\n        <NodeCard\n          title=\"Request Data\"\n          customHandle={\n            <>\n              <InputHandle type=\"target\" isConnectable={isConnectable} />\n              <CreateNewNodePopup\n                style={{\n                  position: 'absolute',\n                  left: '50%',\n                  bottom: '0',\n                }}\n                title=\"请求成功时\"\n                onNewNodeAdd={(data) => handleNewNodeAdd(data, OUTPUT_HANDLE_ID)}\n                disabled={isOutputHandleConnected}\n              >\n                <OutputHandle\n                  isConnectable={isConnectable}\n                  style={{\n                    width: '10px',\n                    height: '10px',\n                    background: '#8BC34A',\n                  }}\n                />\n              </CreateNewNodePopup>\n              <CreateNewNodePopup\n                title=\"请求异常时\"\n                onNewNodeAdd={(data) => handleNewNodeAdd(data, REQUEST_API_FAILED_HANDLE_ID)}\n                disabled={isAfterFailedResponseHandleConnected}\n              >\n                <OutputHandle\n                  style={{\n                    left: '75%',\n                    bottom: '0',\n                    width: '10px',\n                    height: '10px',\n                    background: '#ff4d4f',\n                  }}\n                  id={REQUEST_API_FAILED_HANDLE_ID}\n                  isConnectable={isConnectable}\n                />\n              </CreateNewNodePopup>\n            </>\n          }\n          nodeProps={props}\n          handleNewNodeAdd={() => {}}\n        >\n          <div\n            style={{\n              width: '500px',\n            }}\n          >\n            <CForm\n              name={'requestAPI'}\n              ref={formRef}\n              customSetterMap={BUILD_IN_SETTER_MAP}\n              onValueChange={(newFormData) => {\n                setFormValue(newFormData as any);\n                Object.assign(data, newFormData);\n                onDataChange();\n              }}\n            >\n              <div className={styles.line}>\n                <CField\n                  label={'API'}\n                  name=\"apiPath\"\n                  valueChangeEventName=\"onChange\"\n                  formatEventValue={(el) => el.target.value}\n                >\n                  <CustomAPISelectInput form={formHandler} />\n                </CField>\n              </div>\n              <div className={styles.line}>\n                <CField label={'请求方法'} name=\"method\" valueChangeEventName=\"onChange\">\n                  <Select defaultValue=\"GET\" style={{ width: 230 }} options={methodOptions} />\n                </CField>\n              </div>\n              <div className={styles.line}>\n                <CField\n                  label={'返回值变量'}\n                  name=\"responseVarName\"\n                  tips={\n                    '变量名必须以字母（a-z、A-Z）、下划线（_）或美元符号（$）开头。后续字符可以是字母、数字（0-9）、下划线或美元符号。变量名不能是保留关键字（例如 if、while 等）'\n                  }\n                  valueChangeEventName=\"onChange\"\n                  formatEventValue={(el) => el.target.value}\n                >\n                  <Input />\n                </CField>\n              </div>\n              <Tabs defaultActiveKey={formValue?.method === 'POST' ? 'body' : 'query'} items={tabItems} />\n            </CForm>\n          </div>\n        </NodeCard>\n      </div>\n    </CCustomSchemaFormContext.Provider>\n  );\n};\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ActionFlowSetter/node/RequestAPINode/style.module.scss",
    "content": ".line {\n  padding-bottom: 10px;\n}\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ActionFlowSetter/node/RequestAPINode/util.ts",
    "content": "export function isValidJSVariableName(name: string) {\n  // 1. 使用正则表达式验证变量名规则\n  const identifierRegex = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;\n  if (!identifierRegex.test(name)) {\n    return false;\n  }\n\n  // 2. 检测是否为保留关键字\n  try {\n    new Function(`let ${name};`);\n    return true;\n  } catch {\n    return false;\n  }\n}\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ActionFlowSetter/node/RunCodeNode/index.tsx",
    "content": "import { DEV_CONFIG_KEY, TLogicRunCodeItem } from '@chamn/model';\nimport { NodeProps, Node } from '@xyflow/react';\n\nimport { ensureKeyExist } from '@/utils';\nimport { FunctionSetter } from '../../../FunctionSetter';\nimport { NodeCard } from '../../component/NodeCard';\nimport { useActionFlow } from '../../context';\n\nexport type TRunCodeNode = Node<TLogicRunCodeItem, 'RunCodeNode'>;\n\nexport const RunCodeNode = (props: NodeProps<TRunCodeNode>) => {\n  const { data } = props;\n  ensureKeyExist(data, DEV_CONFIG_KEY, {});\n\n  const { onDataChange, pluginCtx, nodeModel } = useActionFlow();\n\n  return (\n    <div\n      style={{\n        minHeight: '80px',\n        minWidth: '100px',\n      }}\n    >\n      <NodeCard title=\"Run Code\" nodeProps={props}>\n        <FunctionSetter\n          mode=\"inline\"\n          initialValue={data.value}\n          containerStyle={{\n            paddingTop: '10px',\n            width: '600px',\n            height: '300px',\n          }}\n          onValueChange={(newVal: any) => {\n            data.value = newVal.value;\n            onDataChange();\n          }}\n          setterContext={{\n            pluginCtx: pluginCtx,\n            setCollapseHeaderExt: undefined,\n            onSetterChange: function () {},\n            keyPaths: [],\n            label: '',\n            nodeModel: nodeModel as any,\n          }}\n        />\n      </NodeCard>\n    </div>\n  );\n};\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ActionFlowSetter/node/RunCodeNode/style.module.scss",
    "content": ".line {\n  padding-bottom: 10px;\n}\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ActionFlowSetter/node/RunCodeNode/util.ts",
    "content": "export function isValidJSVariableName(name: string) {\n  // 1. 使用正则表达式验证变量名规则\n  const identifierRegex = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;\n  if (!identifierRegex.test(name)) {\n    return false;\n  }\n\n  // 2. 检测是否为保留关键字\n  try {\n    new Function(`let ${name};`);\n    return true;\n  } catch {\n    return false;\n  }\n}\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ActionFlowSetter/node/StartNode.tsx",
    "content": "import { NodeProps, Node } from '@xyflow/react';\nimport { NodeCard } from '../component/NodeCard';\n\nexport type CounterNode = Node<any, 'StartNode'>;\n\nexport const StartNode = (props: NodeProps<CounterNode>) => {\n  return (\n    <NodeCard nodeProps={props} useCardStyle={false} inputHandle={false}>\n      <div\n        style={{\n          minWidth: '100px',\n          padding: '10px 20px',\n          border: '1px solid #1a192b',\n          borderRadius: '4px',\n          textAlign: 'center',\n        }}\n      >\n        Start\n      </div>\n    </NodeCard>\n  );\n};\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ActionFlowSetter/node/index.ts",
    "content": "import { LogicType } from '@chamn/model';\nimport { AssignValueNode } from './AssignValueNode';\nimport { StartNode } from './StartNode';\nimport { CallNodeMethodNode } from './CallNodeMethodNode';\nimport { JumpLinkNode } from './JumpLinkNode';\nimport { RequestAPINode } from './RequestAPINode';\nimport { RunCodeNode } from './RunCodeNode';\n\nexport enum NODE_TYPE {\n  START_NODE = 'START_NODE',\n  JUMP_LINK = LogicType.JUMP_LINK,\n  ASSIGN_VALUE = LogicType.ASSIGN_VALUE,\n  CALL_NODE_METHOD = LogicType.CALL_NODE_METHOD,\n  RUN_CODE = LogicType.RUN_CODE,\n  REQUEST_API = LogicType.REQUEST_API,\n}\n\nexport const NODE_MAP = {\n  [NODE_TYPE.START_NODE]: StartNode,\n  [NODE_TYPE.JUMP_LINK]: JumpLinkNode,\n  [NODE_TYPE.ASSIGN_VALUE]: AssignValueNode,\n  [NODE_TYPE.CALL_NODE_METHOD]: CallNodeMethodNode,\n  [NODE_TYPE.RUN_CODE]: RunCodeNode,\n  [NODE_TYPE.REQUEST_API]: RequestAPINode,\n};\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ActionFlowSetter/util.ts",
    "content": "import { Edge, Node, useReactFlow } from '@xyflow/react';\nimport Dagre from '@dagrejs/dagre';\nimport { getRandomStr, SetterType, TActionLogicItem, TLogicItemHandlerFlow } from '@chamn/model';\nimport { INPUT_HANDLE_ID, OUTPUT_HANDLE_ID, REACT_FLOW_DRAG_CLASS_NAME, REQUEST_API_FAILED_HANDLE_ID } from './config';\nimport { NODE_TYPE } from './node';\nimport { message } from 'antd';\n\n/** 自动布局 flow node */\nexport const calculateElementLayout = (\n  nodes: Node[],\n  edges: Edge[],\n  options: {\n    direction: 'TB' | 'LR';\n  }\n): { nodes: Node[]; edges: Edge[] } => {\n  const g = new Dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({}));\n  g.setGraph({ rankdir: options.direction });\n\n  edges.forEach((edge) => g.setEdge(edge.source, edge.target));\n  nodes.forEach((node) =>\n    g.setNode(node.id, {\n      ...node,\n      width: node.measured?.width ?? 0,\n      height: node.measured?.height ?? 0,\n    })\n  );\n\n  Dagre.layout(g);\n\n  return {\n    nodes: nodes.map((node) => {\n      const position = g.node(node.id);\n      // We are shifting the dagre node position (anchor=center center) to the top left\n      // so it matches the React Flow node anchor point (top left).\n      const x = position.x - (node.measured?.width ?? 0) / 2;\n      const y = position.y - (node.measured?.height ?? 0) / 2;\n\n      return { ...node, position: { x, y } };\n    }),\n    edges,\n  };\n};\n\nconst createFlowNode = (nodeData: Partial<Node>): Node => {\n  const nodeId = nodeData.id || getRandomStr();\n  return {\n    id: nodeId,\n    type: nodeData.type,\n    position: { x: 0, y: 0 },\n    dragHandle: `.${REACT_FLOW_DRAG_CLASS_NAME}`,\n    selectable: nodeData.type === NODE_TYPE.START_NODE ? false : true,\n    data: {\n      ...nodeData,\n      id: nodeId,\n    },\n  };\n};\n\n/** 创建边 */\nconst createFlowEdge = (source: string, target: string, sourceHandle = OUTPUT_HANDLE_ID) => {\n  return {\n    id: `${source}_${target}`,\n    source,\n    sourceHandle,\n    target,\n    targetHandle: INPUT_HANDLE_ID,\n  };\n};\n\n/** 将 json schema 转换为 react-flow 节点和边信息 */\nexport const parseActionLogicToNodeList = (value: TActionLogicItem) => {\n  const nodes: Node[] = [\n    createFlowNode({\n      id: NODE_TYPE.START_NODE,\n      type: NODE_TYPE.START_NODE,\n    }),\n  ];\n  const edges: Edge[] = [];\n\n  if (!value?.handler?.length) {\n    return { nodes: nodes, edges: edges };\n  }\n\n  const processNodes = (\n    items: TLogicItemHandlerFlow,\n    options?: {\n      /**\n       * 用于标记是从那种类型的 output flag  id 导出的 flow\n       */\n      sourceHandler: string;\n    }\n  ) => {\n    items.forEach((item) => {\n      const currentNode = createFlowNode(item);\n      nodes.push(currentNode);\n      if (item.next) {\n        edges.push(createFlowEdge(item.id, String(item.next), options?.sourceHandler));\n      }\n\n      if (item.type === 'REQUEST_API') {\n        // 处理成功分支节点\n        if (item.afterSuccessResponse?.length) {\n          edges.push(createFlowEdge(item.id, String(item.afterSuccessResponse[0].id)));\n          processNodes(item.afterSuccessResponse);\n        }\n\n        // 处理失败分支节点\n        if (item.afterFailedResponse?.length) {\n          edges.push(createFlowEdge(item.id, String(item.afterFailedResponse[0].id), REQUEST_API_FAILED_HANDLE_ID));\n\n          processNodes(item.afterFailedResponse, {\n            sourceHandler: REQUEST_API_FAILED_HANDLE_ID,\n          });\n        }\n      }\n    });\n  };\n\n  const nextItem = value.handler[0];\n  edges.push(createFlowEdge(NODE_TYPE.START_NODE, nextItem.id));\n\n  processNodes(value.handler);\n  return { nodes, edges };\n};\n\n/** 将节点格式化json schema */\nexport const revertNodeToActionLogic = (params: { nodes: Node[]; edges: Edge[] }): TActionLogicItem => {\n  const { nodes, edges } = params;\n  const result: TActionLogicItem = {\n    type: 'ACTION',\n    handler: [],\n  };\n\n  // 找到起始节点\n  const startNode = nodes.find((node) => node.type === NODE_TYPE.START_NODE);\n  if (!startNode) return result;\n  const nodeMap = new Map(nodes.map((node) => [node.id, node]));\n  const visited = new Set<string>();\n\n  const traverseNodes = (currentNodeId: string) => {\n    if (visited.has(currentNodeId)) return [];\n    visited.add(currentNodeId);\n\n    const currentNode = nodeMap.get(currentNodeId);\n    if (!currentNode) return [];\n\n    const handlers = [];\n    const nodeData: any = { ...currentNode.data, id: currentNodeId };\n\n    if (currentNode.type === 'REQUEST_API') {\n      // 获取成功分支节点\n      const successTargets = edges\n        .filter((e) => e.source === currentNodeId && e.sourceHandle === OUTPUT_HANDLE_ID)\n        .map((e) => traverseNodes(e.target))\n        .flat();\n\n      // 获取失败分支节点\n      const failedTargets = edges\n        .filter((e) => e.source === currentNodeId && e.sourceHandle === REQUEST_API_FAILED_HANDLE_ID)\n        .map((e) => traverseNodes(e.target))\n        .flat();\n\n      nodeData.afterSuccessResponse = successTargets;\n      nodeData.afterFailedResponse = failedTargets;\n      handlers.push(nodeData);\n\n      return handlers;\n    }\n\n    handlers.push(nodeData);\n\n    // 获取下一个主流程节点, 理论上只有能一个， 分叉逻辑需要使用特殊节点处理\n    const nextEdges = edges.filter((e) => e.source === currentNodeId && e.sourceHandle === OUTPUT_HANDLE_ID);\n    if (nextEdges.length > 1) {\n      message.error('不允许存在一个节点连接多个节点');\n      throw new Error('不允许存在一个节点连接多个节点');\n    }\n    if (nextEdges.length !== 0) {\n      /** 理论上只会有一个元素 */\n      nextEdges.forEach((edge) => {\n        const nextHandlers = traverseNodes(edge.target);\n        handlers.push(...nextHandlers);\n        nodeData.next = nextHandlers[0]?.id ?? null;\n      });\n    }\n\n    return handlers;\n  };\n  const handler = traverseNodes(startNode.id);\n  // 移除 start node\n  handler.shift();\n  result.handler = handler;\n  return result;\n};\n\n/** 通用的 flow action 画布中的 setter 配置 */\nexport const CommonDynamicValueSetter: SetterType[] = [\n  'StringSetter',\n  'NumberSetter',\n  'ExpressionSetter',\n  'JSONSetter',\n  {\n    componentName: 'FunctionSetter',\n    props: {\n      mode: 'inline',\n      minimap: false,\n      lineNumber: false,\n      containerStyle: {\n        width: '500px',\n        height: '250px',\n      },\n    },\n  },\n];\n\nexport const UseNodeHasConnected = function (data: any, handleId: string) {\n  const reactFlowInstance = useReactFlow();\n  const edges = reactFlowInstance.getEdges();\n  return edges.some((edge) => edge.source === String(data.id) && (edge.sourceHandle || OUTPUT_HANDLE_ID) === handleId);\n};\n\nexport const getNewNodePosInfo = function (currentNode: Node, newNodeData: any, sourceHandle?: string) {\n  // 计算新节点位置\n  const newNodePosition = {\n    x: currentNode.position.x,\n    y: currentNode.position.y + (currentNode.measured?.height ?? 0) + 150,\n  };\n\n  // 创建新节点\n  const newNode = {\n    id: newNodeData.id,\n    type: newNodeData.type,\n    position: newNodePosition,\n    /** 必须 */\n    dragHandle: `.${REACT_FLOW_DRAG_CLASS_NAME}`,\n    data: {\n      ...newNodeData,\n    },\n  };\n\n  // 创建连线\n  const newEdge = {\n    id: `${currentNode.data.id}_${newNode.id}`,\n    source: String(currentNode.data.id),\n    sourceHandle: sourceHandle ?? OUTPUT_HANDLE_ID,\n    target: newNode.id,\n    targetHandle: INPUT_HANDLE_ID,\n  };\n\n  return {\n    newEdge,\n    newNode,\n  };\n};\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/AdvanceSetterList.ts",
    "content": "import { ActionFlowSetter } from './ActionFlowSetter';\nimport { CSetter } from './type';\n\n/** 需要单独导出避免循环以来，因为 ActionFlowSetter 使用了大量内置 setter */\nexport const BUILD_IN_ADVANCE_SETTER_MAP = {\n  ActionFlowSetter,\n} as Record<string, CSetter>;\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/AntDColorSetter/index.tsx",
    "content": "import { ColorPicker, ConfigProvider } from 'antd';\nimport { CSetter, CSetterProps } from '../type';\nimport { DEFAULT_PRESET_COLORS } from '@/config/colorPickerColorList';\n\ntype ColorSetterProps = {\n  initialValue: string;\n};\n\nexport const AntDColorSetter: CSetter = ({\n  onValueChange,\n  initialValue,\n  value,\n  setterContext,\n  ...restProps\n}: CSetterProps<ColorSetterProps>) => {\n  return (\n    <ConfigProvider\n      theme={{\n        token: {\n          borderRadius: 4,\n        },\n      }}\n    >\n      <ColorPicker\n        showText={true}\n        allowClear\n        value={value ?? initialValue}\n        onChangeComplete={(color) => {\n          onValueChange?.(color.toRgbString());\n        }}\n        presets={DEFAULT_PRESET_COLORS}\n        {...restProps}\n      />\n    </ConfigProvider>\n  );\n};\n\nAntDColorSetter.setterName = '颜色设置器';\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ArraySetter/ArrayItem.tsx",
    "content": "import React, { useEffect, useRef } from 'react';\nimport { CForm } from '../../Form';\nimport { SetterSwitcher } from '../../SetterSwitcher';\nimport { DeleteOutlined } from '@ant-design/icons';\nimport { SetterObjType } from '@chamn/model';\n\nexport function ArrayItem(props: {\n  index: number;\n  labelPrefix?: string;\n  keyPaths: string[];\n  value: Record<string, any>;\n  setters: SetterObjType[];\n  style: React.CSSProperties;\n  onValueChange: (val: Record<string, any>) => void;\n  onDelete: () => void;\n}) {\n  const { index, keyPaths, setters } = props;\n\n  const style = {\n    ...props.style,\n  };\n  const objectValue = {\n    [index]: props.value,\n  };\n  const formRef = useRef<CForm>(null);\n  useEffect(() => {\n    formRef.current?.setFields({\n      [index]: props.value,\n    });\n  }, [index, props.value]);\n\n  return (\n    <div style={style}>\n      <CForm\n        ref={formRef}\n        name={String(index)}\n        initialValue={objectValue || {}}\n        onValueChange={props.onValueChange}\n        customSetterMap={{}}\n      >\n        <SetterSwitcher\n          suffix={\n            <div\n              style={{\n                marginLeft: '8px',\n                cursor: 'pointer',\n                fontSize: '12px',\n              }}\n            >\n              <DeleteOutlined onClick={props.onDelete} />\n            </div>\n          }\n          name={String(index)}\n          label={`${props.labelPrefix ?? `元素-${index}`}`}\n          keyPaths={[...keyPaths, String(index)]}\n          setters={setters}\n        ></SetterSwitcher>\n      </CForm>\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ArraySetter/SortItemOrderModal.tsx",
    "content": "import React, { useEffect, useRef, useState } from 'react';\nimport { CSS, Transform } from '@dnd-kit/utilities';\nimport { Modal, ModalProps } from 'antd';\nimport {\n  DndContext,\n  DragEndEvent,\n  KeyboardSensor,\n  PointerSensor,\n  useDraggable,\n  useSensor,\n  useSensors,\n} from '@dnd-kit/core';\nimport { arrayMove, SortableContext, sortableKeyboardCoordinates, useSortable } from '@dnd-kit/sortable';\nimport { restrictToVerticalAxis } from '@dnd-kit/modifiers';\nimport { getRandomStr } from '@chamn/model';\nimport styles from './style.module.scss';\n\nexport type SortItemOrderProps = {\n  list: any[];\n  keyPaths: string[];\n  label: string;\n  sortLabelKey?: string;\n  onValueChange?: (newList: any[]) => void;\n} & ModalProps;\n\nexport const SortItemOrderModal = ({\n  list,\n  onValueChange,\n  keyPaths,\n  label,\n  sortLabelKey,\n  ...modalProps\n}: SortItemOrderProps) => {\n  const [listValue, setListValue] = useState<{ val: any; id: string }[]>([]);\n  useEffect(() => {\n    const innerList = list.map((el, index) => ({\n      val: el,\n      oldIndex: index,\n      id: getRandomStr(),\n    }));\n    setListValue(innerList);\n  }, [list, modalProps.open]);\n\n  const sensors = useSensors(\n    useSensor(PointerSensor, {\n      activationConstraint: {\n        distance: 15,\n      },\n    }),\n    useSensor(KeyboardSensor, {\n      coordinateGetter: sortableKeyboardCoordinates,\n    })\n  );\n\n  function handleDragEnd(event: DragEndEvent) {\n    const { active, over } = event;\n    if (active.id !== over?.id) {\n      const oldIndex = listValue.findIndex((el) => el.id === active?.id);\n      const newIndex = listValue.findIndex((el) => el.id === over?.id);\n      const newInnerListVal = arrayMove(listValue, oldIndex, newIndex);\n      const newVal = newInnerListVal.map((el) => {\n        return el.val;\n      });\n      setListValue(newInnerListVal);\n      onValueChange?.(newVal);\n    }\n  }\n\n  const [modalTransform, setModalTransform] = useState<Transform>({\n    x: 0,\n    y: 0,\n    scaleX: 1,\n    scaleY: 1,\n  });\n\n  return (\n    <Modal\n      {...modalProps}\n      title={`Sort:  ${label} [${keyPaths.join('.')}]`}\n      styles={{\n        mask: {\n          backgroundColor: 'rgba(0, 0, 0, 0.1)',\n        },\n      }}\n      modalRender={(modal) => {\n        return (\n          <DndContext\n            sensors={sensors}\n            onDragEnd={({ delta }) => {\n              const res = {\n                ...modalTransform,\n                ...delta,\n                x: modalTransform.x + (delta?.x || 0),\n                y: modalTransform.y + (delta?.y || 0),\n              };\n              setModalTransform(res);\n            }}\n          >\n            <ModalDragView modal={modal} transform={modalTransform} />\n          </DndContext>\n        );\n      }}\n    >\n      <div className={styles.sortModalBox}>\n        <DndContext sensors={sensors} onDragEnd={handleDragEnd} modifiers={[restrictToVerticalAxis]}>\n          <SortableContext items={listValue}>\n            {listValue.map(({ id, val }, index) => {\n              return <SortableItem key={id} id={id} index={index} label={val[sortLabelKey || '']} />;\n            })}\n          </SortableContext>\n        </DndContext>\n      </div>\n    </Modal>\n  );\n};\n\nconst ModalDragView = ({ modal, transform }: { modal: React.ReactNode; transform: Transform }) => {\n  const id = useRef(getRandomStr());\n  const {\n    setNodeRef,\n    attributes,\n    listeners,\n    transform: tempTransform,\n  } = useDraggable({\n    id: id.current,\n  });\n\n  const finalTransform = {\n    ...transform,\n    ...tempTransform,\n    x: transform.x + (tempTransform?.x || 0),\n    y: transform.y + (tempTransform?.y || 0),\n  };\n\n  return (\n    <div\n      ref={setNodeRef}\n      style={{\n        pointerEvents: 'auto',\n        transform: CSS.Transform.toString(finalTransform),\n      }}\n      {...attributes}\n      {...listeners}\n    >\n      {modal}\n    </div>\n  );\n};\n\nconst SortableItem = (props: { id: string; index: string | number; label?: string }) => {\n  const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id: props.id });\n  const style = {\n    transform: CSS.Transform.toString(transform),\n    transition,\n  };\n  const labelText = props.label || `Ele ${props.index}`;\n  return (\n    <div className={styles.dragItem} ref={setNodeRef} style={style} {...attributes} {...listeners}>\n      {labelText}\n    </div>\n  );\n};\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ArraySetter/index.tsx",
    "content": "import { useEffect, useMemo, useState } from 'react';\nimport { Button, ConfigProvider } from 'antd';\nimport { CSetterProps } from '../type';\nimport { getSetterList } from '../../../utils';\nimport { SetterType } from '@chamn/model';\n\nimport { ArrayItem } from './ArrayItem';\nimport { SortItemOrderModal } from './SortItemOrderModal';\nimport styles from './style.module.scss';\n\nexport type CArraySetterProps = {\n  item: {\n    setters: SetterType[];\n    initialValue?: any;\n  };\n  itemLabelPrefix?: string;\n  sortLabelKey?: string;\n  itemLabelPrefixKey?: string;\n};\n\nfunction formatValue(value: unknown) {\n  if (Array.isArray(value)) {\n    return value;\n  } else {\n    return [];\n  }\n}\n\nexport const ArraySetter = ({\n  onValueChange,\n  setterContext,\n  item: { setters, initialValue: itemInitialValue },\n  sortLabelKey,\n  initialValue,\n  itemLabelPrefix,\n  itemLabelPrefixKey,\n  ...props\n}: CSetterProps<CArraySetterProps>) => {\n  const { keyPaths, label } = setterContext;\n  const listValue: any[] = useMemo(() => {\n    return formatValue(props.value ?? initialValue);\n  }, [initialValue, props.value]);\n\n  const [sortVisible, setSortVisible] = useState(false);\n  const innerSetters = getSetterList(\n    setters || [\n      {\n        component: 'StringSetter',\n      },\n    ]\n  );\n\n  useEffect(() => {\n    if (setterContext.setCollapseHeaderExt) {\n      setterContext.setCollapseHeaderExt?.(\n        <Button\n          type=\"text\"\n          size=\"small\"\n          style={{\n            color: 'gray',\n          }}\n          onClick={(e) => {\n            e.preventDefault();\n            e.stopPropagation();\n            setSortVisible(true);\n          }}\n        >\n          sort\n        </Button>\n      );\n    }\n  }, [setterContext]);\n\n  return (\n    <ConfigProvider\n      theme={{\n        token: {\n          borderRadius: 4,\n        },\n      }}\n    >\n      {listValue.map((val, index) => {\n        return (\n          <ArrayItem\n            labelPrefix={val[itemLabelPrefixKey || sortLabelKey || ''] || itemLabelPrefix}\n            key={index}\n            style={{ paddingBottom: '10px' }}\n            index={index}\n            keyPaths={keyPaths}\n            value={val}\n            onValueChange={(val) => {\n              listValue[index] = val[index];\n              onValueChange?.([...listValue]);\n            }}\n            setters={innerSetters}\n            onDelete={() => {\n              const newVal = [...listValue];\n              newVal.splice(index, 1);\n              onValueChange?.(newVal);\n            }}\n          />\n        );\n      })}\n\n      <Button\n        className={styles.addOneBtn}\n        size=\"small\"\n        onClick={() => {\n          const newVal = [...listValue];\n          onValueChange?.([...newVal, itemInitialValue ?? '']);\n        }}\n      >\n        Add One\n      </Button>\n      <SortItemOrderModal\n        label={label}\n        sortLabelKey={sortLabelKey}\n        onValueChange={(newVal) => {\n          onValueChange?.([...newVal]);\n        }}\n        open={sortVisible}\n        list={listValue}\n        keyPaths={keyPaths}\n        onCancel={() => {\n          setSortVisible(false);\n        }}\n        onOk={() => {\n          setSortVisible(false);\n        }}\n      />\n    </ConfigProvider>\n  );\n};\n\nArraySetter.setterName = '数组设置器';\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ArraySetter/style.module.scss",
    "content": ".dragItem {\n  cursor: grab;\n  border: 1px solid $borderColor;\n  background-color: white;\n  padding: 10px;\n  text-align: center;\n  margin-bottom: 10px;\n  font-size: $fontSizeSmall;\n  border-radius: $borderRadius;\n}\n\n.sortModalBox {\n  padding: 20px 10px 10px;\n  overflow: auto;\n  max-height: 500px;\n}\n\n.addOneBtn {\n  width: 100%;\n  font-size: $fontSizeSmall !important;\n  color: $fontColor;\n}\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/BooleanSetter/index.tsx",
    "content": "import React from 'react';\nimport { ConfigProvider, Switch, SwitchProps } from 'antd';\nimport { CSetter, CSetterProps } from '../type';\n\ntype BooleanSetterProps = SwitchProps;\n\nexport const BooleanSetter: CSetter<BooleanSetterProps & { initialValue?: boolean }> = ({\n  onValueChange,\n  initialValue,\n  setterContext,\n  ...props\n}: CSetterProps<BooleanSetterProps>) => {\n  return (\n    <ConfigProvider\n      theme={{\n        token: {\n          borderRadius: 4,\n        },\n      }}\n    >\n      <Switch\n        {...props}\n        checked={(props.value ?? initialValue) as boolean}\n        onChange={(open, e) => {\n          props.onChange?.(open, e);\n          onValueChange?.(open);\n        }}\n      />\n    </ConfigProvider>\n  );\n};\n\nBooleanSetter.setterName = 'Bool 设置器';\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/CSSSizeSetter/index.tsx",
    "content": "import { ConfigProvider } from 'antd';\nimport { CSetter, CSetterProps } from '../type';\nimport { CSSSizeInput, CSSSizeInputProps } from '@/component/CSSSizeInput';\n\nexport const CSSSizeSetter: CSetter<CSSSizeInputProps> = ({\n  onValueChange,\n  setterContext,\n  initialValue,\n  ...props\n}: CSetterProps<CSSSizeInputProps & { initialValue?: string }>) => {\n  return (\n    <ConfigProvider\n      theme={{\n        token: {\n          borderRadius: 4,\n        },\n      }}\n    >\n      <CSSSizeInput\n        min={0}\n        {...props}\n        value={props.value ?? initialValue}\n        onValueChange={(newVal) => {\n          onValueChange?.(newVal);\n        }}\n      />\n    </ConfigProvider>\n  );\n};\n\nCSSSizeSetter.setterName = 'CSS size 设置器';\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/CSSValueSetter/index.tsx",
    "content": "import React, { useEffect, useMemo, useRef, useState } from 'react';\nimport { AutoComplete, ConfigProvider } from 'antd';\nimport { CSetter, CSetterProps } from '../type';\nimport { BaseSelectRef } from 'rc-select';\nimport clsx from 'clsx';\nimport styles from './style.module.scss';\nimport { CSSProperties, CSSPropertiesKey } from '../../../../CSSPropertiesEditor/cssProperties';\n\ntype CSSValueSetterProps = {\n  propertyKey: string;\n};\n\nexport const CSSValueSetter: CSetter<CSSValueSetterProps> = ({\n  onValueChange,\n  propertyKey = '',\n  value,\n  initialValue,\n}: CSetterProps<CSSValueSetterProps>) => {\n  // const { keyPaths, onSetterChange } = setterContext;\n  const propertyValueRef = useRef<BaseSelectRef | null>(null);\n  const [innerValue, setInnerVal] = useState<any>(value ?? (initialValue || ''));\n  const [focusState, setFocusState] = useState(false);\n  const updateOuterValue = () => {\n    onValueChange?.(innerValue);\n  };\n  useEffect(() => {\n    setInnerVal(value);\n  }, [value]);\n\n  const optionsValue = useMemo(() => {\n    const list = CSSProperties[propertyKey as unknown as CSSPropertiesKey]?.values || [];\n    return list.map((el) => {\n      return {\n        value: el,\n      };\n    });\n  }, [propertyKey]);\n\n  return (\n    <ConfigProvider\n      theme={{\n        token: {\n          borderRadius: 4,\n        },\n      }}\n    >\n      <AutoComplete\n        ref={propertyValueRef}\n        popupMatchSelectWidth={200}\n        variant={'borderless'}\n        value={innerValue}\n        onChange={(val) => {\n          setInnerVal(val);\n          updateOuterValue();\n        }}\n        style={{\n          flex: 1,\n        }}\n        onFocus={() => {\n          setFocusState(true);\n        }}\n        onBlur={() => {\n          setFocusState(false);\n        }}\n        className={clsx([styles.inputAuto, focusState && styles.active])}\n        placeholder=\"value\"\n        options={optionsValue}\n      ></AutoComplete>\n    </ConfigProvider>\n  );\n};\n\nCSSValueSetter.setterName = 'CSS值设置器';\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/CSSValueSetter/style.module.scss",
    "content": ".inputAuto {\n  border-bottom: 1px solid rgba(128, 128, 128, 0.23);\n}\n\n.active {\n  border-bottom: 1px solid rgb(128, 177, 255);\n}\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ColorSetter/index.tsx",
    "content": "import { ConfigProvider, Input, Popover } from 'antd';\nimport { CSetter, CSetterProps } from '../type';\nimport { SketchPicker } from 'react-color';\n\ntype ColorSetterProps = {\n  initialValue: string;\n};\n\nexport const ColorSetter: CSetter = ({ onValueChange, initialValue, value }: CSetterProps<ColorSetterProps>) => {\n  return (\n    <ConfigProvider\n      theme={{\n        token: {\n          borderRadius: 4,\n        },\n      }}\n    >\n      <Popover\n        trigger=\"click\"\n        content={\n          <SketchPicker\n            color={value || initialValue}\n            onChange={(newColor) => {\n              const newColorStr = `rgba(${newColor.rgb.r},${newColor.rgb.g},${newColor.rgb.b}, ${newColor.rgb.a || 1})`;\n              onValueChange?.(newColorStr);\n            }}\n          />\n        }\n        placement={'bottomLeft'}\n        styles={{\n          body: {\n            padding: 0,\n          },\n        }}\n        arrow={{\n          pointAtCenter: false,\n        }}\n      >\n        <Input\n          placeholder=\"color\"\n          value={value}\n          onChange={(e) => {\n            onValueChange?.(e.target.value);\n          }}\n          prefix={\n            <div\n              style={{\n                backgroundColor: value || 'transparent',\n                width: '10px',\n                height: '10px',\n                borderRadius: '4px',\n                overflow: 'hidden',\n              }}\n            ></div>\n          }\n        />\n      </Popover>\n    </ConfigProvider>\n  );\n};\n\nColorSetter.setterName = '颜色设置器';\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/EmptyValueSetter/index.tsx",
    "content": "import { ConfigProvider, Radio } from 'antd';\nimport { CSetter, CSetterProps } from '../type';\nimport { CheckboxGroupProps } from 'antd/es/checkbox';\nimport { useEffect, useMemo } from 'react';\n\nconst emptyValMap = {\n  _null_: null,\n  _undefined_: undefined,\n};\n\nexport const EmptyValueSetter: CSetter<{ emptyValue?: any }> = ({\n  onValueChange,\n  setterContext,\n  initialValue,\n  emptyValue,\n  hiddenDefaultOption,\n  ...props\n}: CSetterProps<{ emptyValue?: string; hiddenDefaultOption?: boolean }>) => {\n  const options = useMemo<CheckboxGroupProps<string>['options']>(() => {\n    const tempValue = [\n      { label: 'Undefined', value: '_undefined_' },\n      { label: 'Null', value: '_null_' },\n    ];\n    if (emptyValue) {\n      if (hiddenDefaultOption) {\n        return [{ label: emptyValue, value: emptyValue }];\n      } else {\n        tempValue.push({ label: emptyValue, value: emptyValue });\n      }\n    }\n\n    return tempValue;\n  }, [emptyValue, hiddenDefaultOption]);\n\n  const innerValue = useMemo(() => {\n    const tempValue = initialValue || props.value;\n    if (tempValue === undefined) {\n      return '_undefined_';\n    }\n\n    if (tempValue === null) {\n      return '_null_';\n    }\n\n    return tempValue || '_undefined_';\n  }, [initialValue, props.value]);\n\n  // 切换初始化时，直接强制重置为 undefined\n  useEffect(() => {\n    onValueChange?.(emptyValue ?? undefined);\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n\n  return (\n    <ConfigProvider\n      theme={{\n        token: {\n          borderRadius: 6,\n        },\n      }}\n    >\n      <Radio.Group\n        block\n        options={options}\n        defaultValue={initialValue}\n        optionType=\"button\"\n        size=\"small\"\n        {...props}\n        value={innerValue}\n        onChange={(e) => {\n          let newVal = e.target.value;\n          if (['_null_', '_undefined_'].includes(newVal)) {\n            newVal = emptyValMap[newVal as keyof typeof emptyValMap];\n          }\n          onValueChange?.(newVal);\n        }}\n      />\n    </ConfigProvider>\n  );\n};\n\nEmptyValueSetter.setterName = '空值设置器';\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ExpressionSetter/index.tsx",
    "content": "import React, { useRef, useState } from 'react';\nimport { Button, ConfigProvider, Modal } from 'antd';\nimport { CSetter, CSetterProps } from '../type';\nimport { EditorType, MonacoEditor, MonacoEditorInstance } from '../../../../MonacoEditor';\nimport DefaultTslibSource from '../FunctionSetter//defaultDts?raw';\nimport { CNodePropsTypeEnum } from '@chamn/model';\nimport styles from './style.module.scss';\nimport { getPageTypeDefined } from '../FunctionSetter/helper';\nexport type ExpressionSetterProps = CSetterProps<{\n  value: {\n    type: string;\n    value: string;\n  };\n  mode: 'modal' | 'inline';\n  containerStyle?: React.CSSProperties;\n  minimap?: boolean;\n  lineNumber?: boolean;\n  editorOptions?: EditorType['options'];\n}>;\n\nexport const ExpressionSetter: CSetter<ExpressionSetterProps> = ({\n  onValueChange,\n  initialValue,\n  setterContext,\n  editorOptions,\n\n  mode = 'modal',\n  ...props\n}) => {\n  const editorRef = useRef<MonacoEditorInstance | null>(null);\n  const [open, setOpen] = useState(false);\n  const onInnerValueChange = () => {\n    const newValStr = editorRef.current?.getValue() || '';\n    onValueChange?.({\n      type: CNodePropsTypeEnum.EXPRESSION,\n      value: newValStr,\n    });\n  };\n\n  let lineNumberOptions = {};\n  if (props.lineNumber === false || mode === 'inline') {\n    lineNumberOptions = {\n      lineNumbers: 'off',\n      lineDecorationsWidth: 0,\n      lineNumbersMinChars: 0,\n      glyphMargin: false,\n    };\n  }\n\n  const editorView = (\n    <MonacoEditor\n      beforeMount={async (monaco) => {\n        monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({\n          noSemanticValidation: true,\n          noSyntaxValidation: false,\n        });\n\n        // compiler options\n        monaco.languages.typescript.javascriptDefaults.setCompilerOptions({\n          target: monaco.languages.typescript.ScriptTarget.ES5,\n          allowNonTsExtensions: true,\n        });\n\n        const realtimeDts = await getPageTypeDefined(setterContext.pluginCtx.pageModel, setterContext.nodeModel);\n\n        const libUri = 'ts:filename/chameleon.default.variable.d.ts';\n        monaco.languages.typescript.javascriptDefaults.addExtraLib(realtimeDts, libUri);\n        // When resolving definitions and references, the editor will try to use created models.\n        // Creating a model for the library allows \"peek definition/references\" commands to work with the library.\n        const model = monaco.editor.getModel(monaco.Uri.parse(libUri));\n        if (!model) {\n          monaco.editor.createModel(DefaultTslibSource, 'typescript', monaco.Uri.parse(libUri));\n        }\n      }}\n      onDidMount={(editor) => {\n        editorRef.current = editor;\n      }}\n      initialValue={props.value?.value ?? initialValue}\n      language={'javascript'}\n      options={{\n        automaticLayout: true,\n        tabSize: 2,\n        minimap: { enabled: false },\n        folding: false,\n        hover: {\n          // enabled: false, // ✅ 禁用 hoverWidget\n        },\n        ...lineNumberOptions,\n        ...(editorOptions || {}),\n      }}\n      onChange={() => {\n        if (mode === 'inline') {\n          // TODO: 需要节流每 1 秒触发一次\n          onInnerValueChange();\n        }\n      }}\n    />\n  );\n\n  if (mode === 'inline') {\n    return (\n      <div\n        className={styles.expressionCodeEditor}\n        style={{\n          border: '1px solid rgba(0,0,0, 0.2)',\n          ...(props.containerStyle || {}),\n          height: '100px',\n        }}\n      >\n        {editorView}\n      </div>\n    );\n  }\n  return (\n    <ConfigProvider\n      theme={{\n        token: {\n          borderRadius: 4,\n        },\n      }}\n    >\n      <Button\n        size=\"small\"\n        style={{\n          marginTop: '5px',\n          width: '100%',\n          color: '#676767',\n          fontSize: '12px',\n        }}\n        onClick={() => {\n          setOpen(true);\n        }}\n      >\n        {props.value?.value || 'Edit Expression'}\n      </Button>\n      <Modal\n        destroyOnClose\n        open={open}\n        title=\"Expression Editor\"\n        onCancel={() => setOpen(false)}\n        width=\"calc(100vw - 100px)\"\n        onOk={() => {\n          onInnerValueChange();\n          setOpen(false);\n        }}\n        style={{\n          maxWidth: '800px',\n        }}\n        styles={{\n          body: {\n            height: '300px',\n          },\n        }}\n      >\n        <div style={{ height: '100%' }}>{open && editorView}</div>\n      </Modal>\n    </ConfigProvider>\n  );\n};\n\nExpressionSetter.setterName = '表达式';\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ExpressionSetter/style.module.scss",
    "content": ".expressionCodeEditor {\n  :global {\n    .monaco-editor .suggest-widget {\n      width: 300px !important;\n      left: 0 !important;\n      right: auto !important;\n      transform: none !important; // 防止默认偏移\n    }\n  }\n}\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/FastLayoutSetter/index.tsx",
    "content": "import { CSSSizeInputProps } from '@/component/CSSSizeInput';\nimport { CSetter, CSetterProps } from '../type';\nimport { StyleUIPanel, StyleUIPanelRef } from '@/component/StylePanel';\nimport { useCallback, useEffect, useMemo, useRef } from 'react';\nimport { formatStyleProperty, styleArr2Obj, styleObjToArr } from '@/utils';\nimport { CNode, CRootNode } from '@chamn/model';\nimport { isEqual } from 'lodash-es';\n\nexport const FastLayoutSetter: CSetter<CSSSizeInputProps> = ({\n  value,\n  setterContext,\n  initialValue,\n  ...resetProps\n}: CSetterProps<CSSSizeInputProps & { initialValue?: string }>) => {\n  const cssUIRef = useRef<StyleUIPanelRef>(null);\n  const node = setterContext.nodeModel;\n  const lastNode = useRef<CNode | CRootNode>();\n\n  const initialValueInner = useMemo(() => {\n    const newStyle = node.value.style || [];\n    const { normalProperty } = formatStyleProperty(newStyle);\n    return styleArr2Obj(normalProperty);\n  }, [node.value.style]);\n\n  const updatePanelValue = useCallback(() => {\n    lastNode.current = node;\n    const newStyle = node.value.style || [];\n    const { normalProperty } = formatStyleProperty(newStyle);\n    cssUIRef.current?.setValue(styleArr2Obj(normalProperty) || {});\n  }, [node]);\n\n  useEffect(() => {\n    updatePanelValue();\n    node.emitter.on('onNodeChange', updatePanelValue);\n    node.emitter.on('onReloadPage', updatePanelValue);\n    return () => {\n      node.emitter.off('onNodeChange', updatePanelValue);\n      node.emitter.off('onReloadPage', updatePanelValue);\n    };\n  }, [node.emitter, node.id, updatePanelValue]);\n\n  return (\n    <StyleUIPanel\n      {...resetProps}\n      initialVal={initialValueInner}\n      ref={cssUIRef}\n      onValueChange={(newNormaCss) => {\n        const newStyle = styleObjToArr(newNormaCss);\n        const { expressionProperty } = formatStyleProperty(node.value.style || []);\n        const newStyleList = [...newStyle, ...expressionProperty];\n        if (isEqual(node.value.style, newStyleList)) {\n          return;\n        }\n        node.value.style = newStyleList;\n        node.updateValue();\n      }}\n    />\n  );\n};\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/FunctionSetter/defaultDts.ts",
    "content": "type SetStateInternal<T> = {\n  _(\n    partial:\n      | T\n      | Partial<T>\n      | {\n          _(state: T): T | Partial<T>;\n        }['_'],\n    replace?: boolean | undefined\n  ): void;\n}['_'];\nexport interface StoreApi<T> {\n  setState: SetStateInternal<T>;\n  getState: () => T;\n  subscribe: (listener: (state: T, prevState: T) => void) => () => void;\n  /**\n   * @deprecated Use `unsubscribe` returned by `subscribe`\n   */\n  destroy: () => void;\n}\n\ntype PageState = any;\n\ntype NodeId = keyof PageState;\n\ntype MethodsManager = any;\n\ntype GlobalState = any;\n\ntype CurrentNodeState = any;\n\ntype PageStateManager<K extends keyof PageState> = {\n  state: PageState[K];\n  updateState: (newState: Partial<PageState[K]>) => void;\n};\n\ntype PageStateManagerMap = {\n  [K in keyof PageState]: PageStateManager<K>;\n};\n\ntype ContextType = {\n  /** 渲染函数的入口参数 */\n  params?: Record<any, any>;\n  /** 全局状态 */\n  globalState?: GlobalState;\n  /** 更新全局状态 */\n  updateGlobalState?: (newState: Partial<GlobalState>) => void;\n  /** 存储当前节点的数据，不具有响应性, 可能不是最新的值, 可以直接赋值 **/\n  staticVar?: Record<string | number, any>;\n  methods?: Record<string, (...arg: any) => any>;\n  /** 当前节点状态 **/\n  state?: CurrentNodeState;\n  /** 更新当前节点状态 */\n  updateState?: (newState: Partial<CurrentNodeState>) => void;\n  /** 获取当前节点状态最新 */\n  /**  用于访访问和管理页面被注册为全局的局部 state 快照，在闭包中使用会存在值不是最新的情况 */\n  stateManager: PageStateManagerMap;\n  /** 循环数据 */\n  loopData?: {\n    item: any;\n    index: number;\n  };\n  /** 组件节点的 Ref, 可以通过 current 直接调用节点提供的方法，需要判断是否在存在 */\n  nodeRefs?: { get: (/** 节点 id */ nodeId: NodeId) => { current?: any } };\n  /** 运行时全局的 store 管理 */\n  storeManager?: StoreManager<NodeId>;\n  /** 第三方辅助库 */\n  thirdLibs?: Record<string, any>;\n};\n\ndeclare class StoreManager<T extends NodeId> {\n  storeMap: Map<T, StoreApi<PageState[T]>>;\n  getStore(storeName: T): StoreApi<PageState[T]> | undefined;\n  getState(nodeId: T): any;\n  getStateObj(nodeId: T): {\n    state: PageState[T];\n    updateState: (newState: Partial<PageState[T]>) => void;\n  };\n  setState(nodeId: NodeId, newState: Partial<PageState[T]>): void | undefined;\n  connect<T extends NodeId>(name: string, cb: (newState: PageState[T]) => void): () => void;\n  getStateSnapshot(): PageState;\n  destroy(): void;\n}\n\ntype UpdaterMap = {\n  [K in NodeId /** 更新对应 nodeId 的数据 **/]: (newState: Partial<PageState[K]>) => void;\n};\n\ndeclare global {\n  /** 运行时上下文 */\n  const $CTX: ContextType;\n\n  /** 当前节点的 state */\n  const $STATE: CurrentNodeState;\n\n  /** global state */\n  const $G_STATE: GlobalState;\n\n  /** 当前页面所有节点的 state */\n  const $ALL_STATE: PageState;\n\n  /** 状态更新函数 */\n  const $U_STATE: UpdaterMap;\n\n  /** 节点方法调用 */\n  const $M: MethodsManager;\n\n  /** 当前节点 ID */\n  const $N_ID: string;\n\n  /** 存储所有节点的 id  */\n  const $IDS: Record<NodeId, NodeId>;\n\n  /** 上一个 API 返回的数据，可能不存在 */\n  const $RESPONSE: any;\n\n  /** 循环数据，如果存在 */\n  const $LOOP_DATA: ContextType['loopData'];\n\n  /** 函数在配置面板传入的参数 */\n  const $PARAMS: ContextType['params'];\n\n  /** 事件传入的第一个参数，如果需要获取第二个参数，请使用 $ARGS 数组获取*/\n  const $EVENT_PARAMS: any;\n\n  /** 事件对象 */\n  const $EVENT: MouseEvent;\n\n  /** 函数执行时传入的入参 */\n  const $ARGS: any[];\n\n  /** action flow 中的局部变量 */\n  const $ACTION_VAR_SPACE: any;\n}\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/FunctionSetter/helper.ts",
    "content": "import { CNode, CPage, InnerComponentNameEnum, traversePageNode } from '@chamn/model';\nimport DefaultTslibSource from './defaultDts?raw';\n\nimport { quicktype, InputData, jsonInputForTargetLanguage } from 'quicktype-core';\n\nexport async function quicktypeJSON(typeName: string, jsonString: string) {\n  const jsonInput = jsonInputForTargetLanguage('typescript');\n\n  // We could add multiple samples for the same desired\n  // type, or many sources for other types. Here we're\n  // just making one type from one piece of sample JSON.\n  await jsonInput.addSource({\n    name: typeName,\n    samples: [jsonString],\n  });\n\n  const inputData = new InputData();\n  inputData.addInput(jsonInput);\n\n  return await quicktype({\n    inputData,\n    lang: 'typescript',\n    rendererOptions: {\n      'just-types': 'true',\n      'nice-property-names': 'false',\n      'acronym-style': 'original',\n    },\n  });\n}\n\nexport const getPageTypeDefined = async (pageModel: CPage, currentNode: CNode) => {\n  const stateTypeMap: Record<\n    string,\n    {\n      stateTypeDefined: string;\n      methodsTypeDefined: string;\n    }\n  > = {} as any;\n  const pList: any[] = [];\n  traversePageNode(pageModel, (node) => {\n    stateTypeMap[node.id] = {\n      stateTypeDefined: '',\n      methodsTypeDefined: '',\n    };\n    if (node.value.state) {\n      const cb = async () => {\n        let typeName = `CNode${node.id.toUpperCase()}`;\n        let id = node.id;\n        if (node.value.componentName === InnerComponentNameEnum.ROOT_CONTAINER) {\n          typeName = 'GlobalState';\n          id = 'GlobalState';\n        }\n        const tempRes = await quicktypeJSON(typeName, JSON.stringify(node.value.state));\n        stateTypeMap[id] = {\n          stateTypeDefined: tempRes.lines.join('\\n'),\n          methodsTypeDefined: '',\n        };\n      };\n\n      pList.push(cb());\n    }\n  });\n\n  await Promise.all(pList);\n\n  const globalStateDts = stateTypeMap['GlobalState'];\n  delete stateTypeMap.GlobalState;\n\n  // 拼接 整体的 page state 类型定义\n  let pageStateDts = '';\n\n  pageStateDts += `type PageState = {\\n`;\n  const nodeIdList: string[] = [];\n\n  Object.keys(stateTypeMap).forEach((k) => {\n    if (stateTypeMap[k].stateTypeDefined) {\n      const body = getBodyDefined(`CNode${k.toUpperCase()}`, stateTypeMap[k].stateTypeDefined);\n      pageStateDts += `  '${k}': ${body},\\n`;\n      nodeIdList.push(`'${k}'`);\n    }\n  });\n\n  pageStateDts += `};\\n\\n`;\n\n  let dtsContent = DefaultTslibSource.replace('type PageState = any;', pageStateDts);\n  dtsContent = dtsContent.replace('type NodeId = any;', `type NodeId = ${nodeIdList.join(' | ')};`);\n  if (globalStateDts?.stateTypeDefined) {\n    dtsContent = dtsContent.replace('type GlobalState = any;', globalStateDts.stateTypeDefined);\n    dtsContent = dtsContent.replace(\n      'Partial<GlobalState>',\n      getBodyDefined('GlobalState', globalStateDts.stateTypeDefined)\n    );\n  }\n\n  // 处理当前 node 的 types\n  const currentNodeDts = await quicktypeJSON('CurrentNodeState', JSON.stringify(currentNode.value.state || {}));\n  const currentNodeDtsText = currentNodeDts.lines.join('\\n');\n  dtsContent = dtsContent.replace('type CurrentNodeState = any;', currentNodeDtsText);\n  dtsContent = dtsContent.replace('Partial<CurrentNodeState>', getBodyDefined('CurrentNodeState', currentNodeDtsText));\n\n  // 处理 methods 调用\n  return dtsContent;\n};\n\nconst getBodyDefined = (\n  typeName: string,\n  dtsStr: string,\n  options?: {\n    isPartial: true;\n  }\n) => {\n  const body = dtsStr.replace(`export interface ${typeName}`, '').trim();\n  if (!options?.isPartial) {\n    return body;\n  }\n  return `Partial<${body}>`;\n};\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/FunctionSetter/index.tsx",
    "content": "import React, { useRef, useState } from 'react';\nimport { Button, ConfigProvider, Modal } from 'antd';\nimport { CSetter } from '../type';\nimport { EditorType, MonacoEditor, MonacoEditorInstance } from '../../../../MonacoEditor';\nimport DefaultTslibSource from './defaultDts?raw';\nimport { CNodePropsTypeEnum } from '@chamn/model';\nimport { getPageTypeDefined } from './helper';\n\nexport const FunctionSetter: CSetter<{\n  mode: 'modal' | 'inline';\n  containerStyle?: React.CSSProperties;\n  minimap?: boolean;\n  lineNumber?: boolean;\n  editorOptions?: EditorType['options'];\n}> = ({ onValueChange, initialValue, setterContext, editorOptions, ...props }) => {\n  getPageTypeDefined(setterContext.pluginCtx.pageModel, setterContext.nodeModel);\n  const editorRef = useRef<MonacoEditorInstance | null>(null);\n  const [open, setOpen] = useState(false);\n  const onInnerValueChange = () => {\n    const newValStr = editorRef.current?.getValue() || '';\n    onValueChange?.({\n      type: CNodePropsTypeEnum.FUNCTION,\n      value: newValStr,\n    });\n  };\n\n  let lineNumberOptions = {};\n  if (props.lineNumber === false) {\n    lineNumberOptions = {\n      lineNumbers: 'off',\n      lineDecorationsWidth: 0,\n      lineNumbersMinChars: 0,\n      glyphMargin: false,\n    };\n  }\n\n  const editorView = (\n    <MonacoEditor\n      beforeMount={async (monaco) => {\n        monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({\n          noSemanticValidation: true,\n          noSyntaxValidation: false,\n        });\n\n        // compiler options\n        monaco.languages.typescript.javascriptDefaults.setCompilerOptions({\n          target: monaco.languages.typescript.ScriptTarget.ES5,\n          allowNonTsExtensions: true,\n        });\n\n        const realtimeDts = await getPageTypeDefined(setterContext.pluginCtx.pageModel, setterContext.nodeModel);\n\n        const libUri = 'ts:filename/chameleon.default.variable.d.ts';\n        monaco.languages.typescript.javascriptDefaults.addExtraLib(realtimeDts, libUri);\n        // When resolving definitions and references, the editor will try to use created models.\n        // Creating a model for the library allows \"peek definition/references\" commands to work with the library.\n        const model = monaco.editor.getModel(monaco.Uri.parse(libUri));\n        if (!model) {\n          monaco.editor.createModel(DefaultTslibSource, 'typescript', monaco.Uri.parse(libUri));\n        }\n      }}\n      onDidMount={(editor) => {\n        editorRef.current = editor;\n      }}\n      initialValue={props.value?.value ?? (initialValue || 'function run() {\\n}')}\n      language={'javascript'}\n      options={{\n        automaticLayout: true,\n        tabSize: 2,\n\n        minimap: {\n          enabled: props.minimap ?? true,\n        },\n        ...lineNumberOptions,\n        ...(editorOptions || {}),\n      }}\n      onChange={() => {\n        if (props.mode === 'inline') {\n          // TODO: 需要节流每 1 秒触发一次\n          onInnerValueChange();\n        }\n      }}\n    />\n  );\n\n  if (props.mode === 'inline') {\n    return (\n      <div\n        style={{\n          border: '1px solid rgba(0,0,0, 0.2)',\n          ...(props.containerStyle || {}),\n        }}\n      >\n        {editorView}\n      </div>\n    );\n  }\n  return (\n    <ConfigProvider\n      theme={{\n        token: {\n          borderRadius: 4,\n        },\n      }}\n    >\n      <Button\n        size=\"small\"\n        style={{\n          marginTop: '5px',\n          width: '100%',\n          color: '#676767',\n          fontSize: '12px',\n        }}\n        onClick={() => {\n          setOpen(true);\n        }}\n      >\n        Edit Function\n      </Button>\n      <Modal\n        centered\n        destroyOnClose\n        open={open}\n        title=\"Function Editor\"\n        onCancel={() => setOpen(false)}\n        width=\"calc(100vw - 100px)\"\n        onOk={() => {\n          onInnerValueChange();\n          setOpen(false);\n        }}\n        style={{\n          maxWidth: '1300px',\n        }}\n        styles={{\n          body: {\n            minHeight: '500px',\n            height: 'calc(100vh - 280px)',\n          },\n        }}\n      >\n        <div style={{ height: '100%' }}>{open && editorView}</div>\n      </Modal>\n    </ConfigProvider>\n  );\n};\n\nFunctionSetter.setterName = '函数设置器';\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/JSONSetter/index.tsx",
    "content": "import React, { useEffect, useRef, useState } from 'react';\nimport { Button, ConfigProvider } from 'antd';\nimport { CSetter, CSetterProps } from '../type';\nimport { EditorType, MonacoEditor, MonacoEditorInstance } from '../../../../MonacoEditor';\nimport { MoveableModal } from '@/component/MoveableModal';\nimport { sageJSONParse } from '@/utils';\n\nconst EditorConfig = {\n  options: {\n    automaticLayout: true,\n    tabSize: 2,\n    suggestOnTriggerCharacters: false,\n    minimap: { enabled: false },\n  },\n  style: {\n    height: '200px',\n    border: '1px solid #d9d9d9',\n    borderRadius: '4px',\n  },\n};\n\nconst JsonEditor = ({ editor, value, initialValue, onChange, height, editorOptions }: any) => {\n  const editorRef = useRef<MonacoEditorInstance>();\n  const oldValueRef = useRef();\n  oldValueRef.current = value;\n  useEffect(() => {\n    const oldVal = sageJSONParse(editorRef.current?.getValue() || '', {});\n    if (JSON.stringify(value) === JSON.stringify(oldVal)) {\n      return;\n    }\n    editorRef.current?.setValue(JSON.stringify(value, null, 2));\n  }, [value]);\n  return (\n    <div style={{ height: height || EditorConfig.style.height }}>\n      <MonacoEditor\n        onDidMount={(editorInstance) => {\n          editor.current = editorInstance;\n          editorRef.current = editorInstance;\n        }}\n        onChange={onChange}\n        initialValue={JSON.stringify(value ?? (initialValue || {}), null, 2)}\n        language=\"json\"\n        options={{\n          ...EditorConfig.options,\n          ...editorOptions,\n        }}\n      />\n    </div>\n  );\n};\nexport const JSONSetter: CSetter<any> = ({\n  onValueChange,\n  initialValue,\n  mode = 'modal',\n  editorOptions,\n  setterContext,\n  ...props\n}: CSetterProps<{\n  mode: 'modal' | 'inline';\n  containerStyle?: React.CSSProperties;\n  minimap?: boolean;\n  lineNumber?: 'on' | 'off';\n  editorOptions?: EditorType['options'];\n}>) => {\n  const editorRef = useRef<MonacoEditorInstance | null>(null);\n  const [open, setOpen] = useState(false);\n\n  const onInnerValueChange = () => {\n    const newValStr = editorRef.current?.getValue() || '';\n    try {\n      const newVal = JSON.parse(newValStr);\n      onValueChange?.(newVal);\n    } catch (e) {\n      console.warn(e);\n    }\n  };\n\n  return (\n    <ConfigProvider theme={{ token: { borderRadius: 4 } }}>\n      {mode === 'inline' ? (\n        <div style={EditorConfig.style}>\n          <JsonEditor\n            editor={editorRef}\n            value={props.value}\n            initialValue={initialValue}\n            onChange={onInnerValueChange}\n            editorOptions={editorOptions || {}}\n          />\n        </div>\n      ) : (\n        <>\n          <Button\n            size=\"small\"\n            style={{\n              marginTop: '5px',\n              width: '100%',\n              color: '#676767',\n              fontSize: '12px',\n            }}\n            onClick={() => setOpen(true)}\n          >\n            Edit\n          </Button>\n          <MoveableModal\n            destroyOnClose\n            open={open}\n            title=\"JSON Editor\"\n            width=\"800px\"\n            onCancel={() => setOpen(false)}\n            onOk={() => {\n              onInnerValueChange();\n              setOpen(false);\n            }}\n          >\n            {open && (\n              <JsonEditor\n                editor={editorRef}\n                value={props.value}\n                initialValue={initialValue}\n                height=\"500px\"\n                editorOptions={editorOptions || {}}\n              />\n            )}\n          </MoveableModal>\n        </>\n      )}\n    </ConfigProvider>\n  );\n};\n\nJSONSetter.setterName = 'JSON 设置器';\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/NumberSetter/index.tsx",
    "content": "import React from 'react';\nimport { ConfigProvider, InputNumber, InputNumberProps } from 'antd';\nimport { CSetter, CSetterProps } from '../type';\n\nexport const NumberSetter: CSetter<InputNumberProps> = ({\n  onValueChange,\n  setterContext,\n  initialValue,\n  ...props\n}: CSetterProps<InputNumberProps>) => {\n  return (\n    <ConfigProvider\n      theme={{\n        token: {\n          borderRadius: 4,\n        },\n      }}\n    >\n      <InputNumber\n        style={{\n          width: '100%',\n        }}\n        {...props}\n        value={props.value ?? initialValue}\n        onChange={(value) => {\n          props.onChange?.(value);\n          onValueChange?.(value);\n        }}\n      />\n    </ConfigProvider>\n  );\n};\n\nNumberSetter.setterName = '数字设置器';\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/RadioGroupSetter/index.tsx",
    "content": "import { ConfigProvider, Radio } from 'antd';\nimport { CSetter, CSetterProps } from '../type';\n\ntype ColorSetterProps = {\n  initialValue: string;\n};\n\nexport const RadioGroupSetter: CSetter = ({\n  onValueChange,\n  value,\n  initialValue,\n  setterContext,\n  ...restProps\n}: CSetterProps<ColorSetterProps>) => {\n  return (\n    <ConfigProvider\n      theme={{\n        token: {\n          borderRadius: 4,\n        },\n      }}\n    >\n      <Radio.Group\n        onChange={(e) => {\n          onValueChange?.(e.target.value);\n        }}\n        defaultValue={initialValue}\n        value={value}\n        {...restProps}\n      />\n    </ConfigProvider>\n  );\n};\n\nRadioGroupSetter.setterName = '单选设置器';\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/SelectSetter/index.tsx",
    "content": "import React from 'react';\nimport { ConfigProvider, Select, SelectProps } from 'antd';\nimport { CSetter, CSetterProps } from '../type';\n\nexport const SelectSetter: CSetter<SelectProps> = ({\n  onValueChange,\n  setterContext,\n  initialValue,\n  ...props\n}: CSetterProps<SelectProps>) => {\n  return (\n    <ConfigProvider\n      theme={{\n        token: {\n          borderRadius: 4,\n        },\n      }}\n    >\n      <Select\n        showSearch\n        style={{\n          width: '100%',\n        }}\n        {...props}\n        value={props.value ?? initialValue}\n        onChange={(val, option) => {\n          props.onChange?.(val, option);\n          onValueChange?.(val);\n        }}\n      />\n    </ConfigProvider>\n  );\n};\n\nSelectSetter.setterName = '选择设置器';\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/ShapeSetter/index.tsx",
    "content": "import { useEffect, useRef } from 'react';\nimport { ConfigProvider } from 'antd';\nimport { CSetter, CSetterProps } from '../type';\nimport { CForm } from '../../Form';\nimport { SetterSwitcher } from '../../SetterSwitcher';\nimport { getSetterList } from '../../../utils';\nimport { getMTitle, getMTitleTip, MaterialPropType } from '@chamn/model';\n\nexport type CShapeSetterProps = {\n  elements: MaterialPropType[];\n  initialValue?: Record<string, any>;\n  value: Record<string, any>;\n};\n\nexport const ShapeSetter: CSetter<CShapeSetterProps> = ({\n  onValueChange,\n  elements,\n  value,\n  setterContext,\n  initialValue,\n}: CSetterProps<CShapeSetterProps>) => {\n  const { keyPaths } = setterContext;\n  const formRef = useRef<CForm>(null);\n  useEffect(() => {\n    formRef.current?.setFields(value || {});\n  }, [value]);\n\n  return (\n    <ConfigProvider\n      theme={{\n        token: {\n          borderRadius: 4,\n        },\n      }}\n    >\n      <CForm\n        ref={formRef}\n        name={keyPaths.join('.')}\n        initialValue={value || initialValue || {}}\n        onValueChange={(val) => {\n          onValueChange?.(val);\n        }}\n        customSetterMap={{}}\n      >\n        {elements.map((el, index) => {\n          const setters = getSetterList(el.setters);\n          const title = getMTitle(el.title);\n          const tip = getMTitleTip(el.title) || el?.description;\n          return (\n            <div key={index}>\n              <SetterSwitcher\n                name={el.name}\n                label={title}\n                tips={tip}\n                condition={el.condition}\n                keyPaths={[...keyPaths, el.name]}\n                setters={setters}\n              />\n            </div>\n          );\n        })}\n      </CForm>\n    </ConfigProvider>\n  );\n};\n\nShapeSetter.setterName = '对象设置器';\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/SliderSetter/index.tsx",
    "content": "import { Slider, SliderSingleProps } from 'antd';\nimport { CSetter, CSetterProps } from '../type';\n\nexport const SliderSetter: CSetter<SliderSingleProps> = ({\n  onValueChange,\n  setterContext,\n  initialValue,\n  ...props\n}: CSetterProps<SliderSingleProps & { initialValue?: string }>) => {\n  return (\n    <Slider\n      {...props}\n      value={props.value ?? initialValue}\n      onChange={(newValue) => {\n        props.onChange?.(newValue);\n        onValueChange?.(newValue);\n      }}\n    />\n  );\n};\n\nSliderSetter.setterName = '滑动条设置器';\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/StringSetter/index.tsx",
    "content": "import { ConfigProvider, Input, InputProps } from 'antd';\nimport { CSetter, CSetterProps } from '../type';\nimport { useState, useRef, useEffect } from 'react';\n\nexport const StringSetter: CSetter<InputProps> = ({\n  onValueChange,\n  setterContext,\n  initialValue,\n  ...props\n}: CSetterProps<InputProps & { initialValue?: string }>) => {\n  const [internalValue, setInternalValue] = useState(props.value ?? initialValue ?? '');\n  const lastExternalValueRef = useRef(props.value ?? initialValue);\n\n  // 监听外部值变化，同步到内部状态（避免重复更新）\n  useEffect(() => {\n    const externalValue = props.value ?? initialValue;\n    if (externalValue !== lastExternalValueRef.current) {\n      lastExternalValueRef.current = externalValue;\n      setInternalValue(externalValue ?? '');\n    }\n  }, [props.value, initialValue]);\n\n  return (\n    <ConfigProvider\n      theme={{\n        token: {\n          borderRadius: 4,\n        },\n      }}\n    >\n      <Input\n        {...props}\n        value={internalValue}\n        onChange={(e) => {\n          const newValue = e.target.value;\n          setInternalValue(newValue);\n        }}\n        onBlur={(e) => {\n          // blur 时才触发 onChange 和更新值到外部\n          const finalValue = e.target.value;\n          lastExternalValueRef.current = finalValue;\n          props.onChange?.(e);\n          onValueChange?.(finalValue);\n          props.onBlur?.(e);\n        }}\n      />\n    </ConfigProvider>\n  );\n};\n\nStringSetter.setterName = '字符设置器';\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/TextAreaSetter/index.tsx",
    "content": "import { ConfigProvider, Input } from 'antd';\nimport { CSetter, CSetterProps } from '../type';\nimport { TextAreaProps } from 'antd/es/input';\nimport { useState, useRef, useEffect } from 'react';\n\nexport type TTextAreaSetterProps = TextAreaProps & {\n  valueValidator?: (value: string) => boolean;\n};\n\nexport const TextAreaSetter: CSetter<TTextAreaSetterProps> = ({\n  onValueChange,\n  setterContext,\n  initialValue,\n  valueValidator,\n  ...props\n}: CSetterProps<TTextAreaSetterProps>) => {\n  const [checkValueStatus, setCheckValueStatus] = useState<TextAreaProps['status']>('');\n  const [internalValue, setInternalValue] = useState(props.value ?? initialValue ?? '');\n  const lastExternalValueRef = useRef(props.value ?? initialValue);\n\n  // 监听外部值变化，同步到内部状态（避免重复更新）\n  useEffect(() => {\n    const externalValue = props.value ?? initialValue;\n    if (externalValue !== lastExternalValueRef.current) {\n      lastExternalValueRef.current = externalValue;\n      setInternalValue(externalValue ?? '');\n    }\n  }, [props.value, initialValue]);\n\n  const validateValue = (value: string) => {\n    if (valueValidator !== undefined) {\n      const res = valueValidator(value);\n      if (!res) {\n        setCheckValueStatus('error');\n      } else {\n        setCheckValueStatus('');\n      }\n    }\n  };\n\n  return (\n    <ConfigProvider\n      theme={{\n        token: {\n          borderRadius: 4,\n        },\n      }}\n    >\n      <Input.TextArea\n        status={checkValueStatus}\n        {...props}\n        value={internalValue}\n        onChange={(e) => {\n          const newValue = e.target.value;\n          setInternalValue(newValue);\n          validateValue(newValue);\n        }}\n        onBlur={(e) => {\n          // blur 时才触发 onChange 和更新值到外部\n          const finalValue = e.target.value;\n          lastExternalValueRef.current = finalValue;\n          props.onChange?.(e as any);\n          onValueChange?.(finalValue);\n          props.onBlur?.(e);\n        }}\n      />\n    </ConfigProvider>\n  );\n};\n\nTextAreaSetter.setterName = '长文本设置器';\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/index.ts",
    "content": "import { CSetter } from './type';\nimport { StringSetter } from './StringSetter';\nimport { NumberSetter } from './NumberSetter';\nimport { ArraySetter } from './ArraySetter';\nimport { ShapeSetter } from './ShapeSetter';\nimport { ExpressionSetter } from './ExpressionSetter';\nimport { BooleanSetter } from './BooleanSetter';\nimport { SelectSetter } from './SelectSetter';\nimport { JSONSetter } from './JSONSetter';\nimport { FunctionSetter } from './FunctionSetter';\nimport { TextAreaSetter } from './TextAreaSetter';\nimport { CSSValueSetter } from './CSSValueSetter';\nimport { ColorSetter } from './ColorSetter';\nimport { AntDColorSetter } from './AntDColorSetter';\nimport { SliderSetter } from './SliderSetter';\nimport { RadioGroupSetter } from './RadioGroupSetter';\nimport { CSSSizeSetter } from './CSSSizeSetter';\nimport { FastLayoutSetter } from './FastLayoutSetter';\nimport { EmptyValueSetter } from './EmptyValueSetter';\n\nexport const BUILD_IN_SETTER_MAP = {\n  FastLayoutSetter,\n  StringSetter,\n  ArraySetter,\n  ShapeSetter,\n  NumberSetter,\n  ExpressionSetter,\n  BooleanSetter,\n  SelectSetter,\n  JSONSetter,\n  FunctionSetter,\n  TextAreaSetter,\n  CSSValueSetter,\n  ColorSetter,\n  SliderSetter,\n  AntDColorSetter,\n  RadioGroupSetter,\n  CSSSizeSetter,\n  EmptyValueSetter,\n} as Record<string, CSetter>;\n\nexport default BUILD_IN_SETTER_MAP;\n\nexport * from './type';\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/components/Setters/type.ts",
    "content": "import { CNode } from '@chamn/model';\nimport { CPluginCtx } from '../../../../core/pluginManager';\n\nexport type CSetter<T = any> = {\n  (props: CSetterProps<T>): JSX.Element;\n  setterName?: string;\n};\n\nexport type CSetterProps<T = { _: any }> = {\n  onValueChange?: (val: any) => void;\n  initialValue?: any;\n  value?: any;\n  setterContext: {\n    pluginCtx: CPluginCtx;\n    setCollapseHeaderExt?: (el: React.ReactNode) => void;\n    onSetterChange: (keyPaths: string[], setterName: string) => void;\n    keyPaths: string[];\n    label: string;\n    /** 当前被编辑的节点信息 */\n    nodeModel: CNode;\n  };\n} & T;\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/context.ts",
    "content": "import React, { Ref } from 'react';\nimport { CustomSchemaFormInstance } from './index';\nimport { CPluginCtx } from '../../core/pluginManager';\nimport { CForm } from './components/Form';\nimport { CFormContextData } from './components/Form/context';\n\nexport type ContextState = Record<string, any>;\n\nexport type CCustomSchemaFormContextData = {\n  onSetterChange: (keyPaths: string[], setterName: string) => void;\n  /** 存储 field 默认的 setter 类型*/\n  defaultSetterConfig: Record<string, { name: string; setter: string }>;\n  /** schema 中的全局 setter map 配置*/\n  customSetterMap: CFormContextData['customSetterMap'];\n  formRef?: Ref<CustomSchemaFormInstance | CForm>;\n  pluginCtx?: CPluginCtx;\n  /** 当前编辑节点的 id */\n  nodeId?: string;\n};\n\nexport const CCustomSchemaFormContext = React.createContext<CCustomSchemaFormContextData>({\n  defaultSetterConfig: {},\n  onSetterChange: () => {},\n  customSetterMap: {},\n});\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/index.tsx",
    "content": "/* eslint-disable react-refresh/only-export-components */\nimport React, { Ref } from 'react';\nimport { CMaterialPropsType, getMTitleTip, getMTitle, isSpecialMaterialPropType } from '@chamn/model';\nimport { CForm } from './components/Form';\nimport { SetterSwitcher } from './components/SetterSwitcher';\nimport { getSetterList } from './utils';\nimport styles from './style.module.scss';\nimport { ConfigProvider } from 'antd';\nimport { CCustomSchemaFormContext } from './context';\nimport { CPluginCtx } from '../../core/pluginManager';\nimport { CFormContextData } from './components/Form/context';\n\nexport type CustomSchemaFormInstance = CForm;\n\nexport type CustomSchemaFormProps = {\n  nodeId?: string;\n  pluginCtx?: CPluginCtx;\n  initialValue: Record<string, any>;\n  properties: CMaterialPropsType<any>;\n  onValueChange?: (val: any) => void;\n  onSetterChange: (keyPaths: string[], setterName: string) => void;\n  /** 存储每个字段上次使用的 setter */\n  defaultSetterConfig: Record<string, { name: string; setter: string }>;\n  customSetterMap?: CFormContextData['customSetterMap'];\n};\n\nconst CustomSchemaFormCore = (props: CustomSchemaFormProps, ref: Ref<CustomSchemaFormInstance | CForm>) => {\n  const {\n    properties: originProperties,\n    initialValue,\n    onValueChange,\n    onSetterChange,\n    defaultSetterConfig,\n    pluginCtx,\n    customSetterMap,\n  } = props;\n  const properties: CMaterialPropsType = originProperties;\n  return (\n    <CCustomSchemaFormContext.Provider\n      value={{\n        defaultSetterConfig,\n        // 设置全局 setter map\n        customSetterMap,\n        onSetterChange,\n        formRef: ref,\n        pluginCtx: pluginCtx,\n        nodeId: props.nodeId,\n      }}\n    >\n      <ConfigProvider\n        theme={{\n          token: {\n            borderRadius: 4,\n          },\n        }}\n      >\n        <div className={styles.CFromRenderBox}>\n          <CForm\n            ref={ref as any}\n            name=\"root-form\"\n            initialValue={initialValue}\n            customSetterMap={customSetterMap}\n            onValueChange={(val) => {\n              onValueChange?.(val);\n            }}\n          >\n            {properties.map((property) => {\n              if (isSpecialMaterialPropType(property)) {\n                console.log('Current not Support type config for props, wait future....');\n              } else {\n                const title = getMTitle(property.title);\n                const tip = getMTitleTip(property.title) || property?.description;\n                const setterList = getSetterList(property.setters);\n                const keyPaths = [property.name];\n                return (\n                  <SetterSwitcher\n                    key={property.name}\n                    condition={property.condition}\n                    keyPaths={keyPaths}\n                    setters={setterList}\n                    label={title}\n                    name={property.name || ''}\n                    tips={tip}\n                  />\n                );\n              }\n            })}\n          </CForm>\n        </div>\n      </ConfigProvider>\n    </CCustomSchemaFormContext.Provider>\n  );\n};\n\nexport const CustomSchemaForm = React.forwardRef(CustomSchemaFormCore);\n\nexport * from './components/SetterSwitcher';\nexport * from './components/Setters';\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/style.module.scss",
    "content": ".CFromRenderBox {\n  :global {\n    .ant-collapse .ant-collapse-content > .ant-collapse-content-box {\n      padding-right: 5px;\n    }\n\n    .ant-collapse > .ant-collapse-item:last-child > .ant-collapse-header {\n      padding: 5px 5px 5px 10px;\n      align-items: center;\n      .ant-collapse-expand-icon {\n        padding-inline-end: 4px;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/engine/src/component/CustomSchemaForm/utils.ts",
    "content": "import { SetterBasicType, SetterObjType, SetterType } from '@chamn/model';\n\nexport const getSetterList = <T extends SetterBasicType = ''>(setters: SetterType<T>[] = []): SetterObjType<T>[] => {\n  return setters.map((setter) => {\n    if (typeof setter === 'string') {\n      return {\n        componentName: setter as any,\n      };\n    } else {\n      return setter;\n    }\n  });\n};\n\nconst setterTypeMap = {\n  string: 'StringSetter',\n  number: 'NumberSetter',\n  boolean: 'BooleanSetter',\n  array: 'ArraySetter',\n  expression: 'ExpressionSetter',\n  function: 'FunctionSetter',\n  json: 'JSONSetter',\n} as const;\n\nconst getValueType = (value: any): keyof typeof setterTypeMap => {\n  if (value === undefined || value === null) {\n    return 'string';\n  }\n\n  if (Array.isArray(value)) {\n    return 'array';\n  }\n\n  if (typeof value === 'object') {\n    if (value.type === 'EXPRESSION') return 'expression';\n    if (value.type === 'FUNCTION') return 'function';\n    return 'json';\n  }\n\n  return typeof value as keyof typeof setterTypeMap;\n};\n\n/** 根据值获取默认的 setter */\nexport const getDefaultSetterByValue = (value: any, setterList: SetterObjType[]) => {\n  const valueType = getValueType(value);\n  const setterName = setterTypeMap[valueType];\n  return setterList.find((setter) => setter.componentName === setterName);\n};\n"
  },
  {
    "path": "packages/engine/src/component/DesignerSizer/index.tsx",
    "content": "import { DesignerPluginInstance } from '@/plugins/Designer/type';\nimport { EnginContext } from '@/type';\nimport { BorderOutlined, MobileOutlined } from '@ant-design/icons';\nimport { InputNumber, Segmented, Space } from 'antd';\nimport { debounce } from 'lodash-es';\nimport { useCallback, useEffect, useRef, useState } from 'react';\n\nexport const DesignerSizer = (props: { ctx: EnginContext }) => {\n  const designerRef = useRef<DesignerPluginInstance>();\n\n  const [currentSize, setCurrentSize] = useState('AUTO');\n  const [width, setWith] = useState(1200);\n  const getSubWindowWidth = useCallback(() => {\n    const designer = designerRef.current;\n    const subWin = designer?.export.getDesignerWindow();\n    const w = subWin?.innerWidth;\n    setWith(Number(w));\n  }, []);\n\n  useEffect(() => {\n    let resizeHandler: any;\n    let subWin: Window | null;\n    props.ctx.pluginManager.onPluginReadyOk('Designer').then((designer: DesignerPluginInstance) => {\n      designerRef.current = designer;\n      subWin = designer?.export.getDesignerWindow();\n      resizeHandler = () => {\n        setWith(Number(subWin?.innerWidth));\n      };\n      subWin?.addEventListener('resize', resizeHandler);\n      getSubWindowWidth();\n    });\n\n    return () => {\n      subWin?.removeEventListener('resize', resizeHandler);\n    };\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n\n  // eslint-disable-next-line react-hooks/exhaustive-deps\n  const setCanvasWidth = useCallback(\n    debounce((w: number) => {\n      const designer = designerRef.current;\n      designer?.export.setCanvasWidth(w);\n    }, 100),\n    []\n  );\n  return (\n    <Space>\n      <Segmented\n        defaultValue={currentSize}\n        onChange={(value) => {\n          const designer = designerRef.current;\n          if (!designer) {\n            return;\n          }\n          if (value === 'AUTO') {\n            designer.export.setCanvasWidth('100%');\n            getSubWindowWidth();\n          } else if (value === 'IPAD') {\n            designer.export.setCanvasWidth(768);\n          } else {\n            designer.export.setCanvasWidth(350);\n          }\n          setCurrentSize(value);\n        }}\n        options={[\n          {\n            label: (\n              <span>\n                <span\n                  onClick={() => {\n                    const designer = designerRef.current;\n                    designer?.export.setCanvasWidth('100%');\n                    getSubWindowWidth();\n                  }}\n                >\n                  Auto\n                </span>\n                {currentSize === 'AUTO' && (\n                  <InputNumber\n                    size=\"small\"\n                    style={{\n                      marginLeft: '10px',\n                    }}\n                    controls={false}\n                    changeOnWheel\n                    suffix=\"px\"\n                    value={width}\n                    min={350}\n                    max={1920}\n                    onChange={(val) => {\n                      setWith(Number(val));\n                      setCanvasWidth(Number(val));\n                    }}\n                  ></InputNumber>\n                )}\n              </span>\n            ),\n            value: 'AUTO',\n          },\n          {\n            label: <BorderOutlined />,\n            value: 'IPAD',\n          },\n          {\n            label: <MobileOutlined />,\n            value: 'MOBILE',\n          },\n        ]}\n      />\n    </Space>\n  );\n};\n"
  },
  {
    "path": "packages/engine/src/component/InputNumberPlus/index.tsx",
    "content": "import { Input, InputProps } from 'antd';\nimport styles from './style.module.scss';\nimport { useMemo } from 'react';\n\nexport type InputNumberPlusProps = Omit<InputProps, 'onChange'> & {\n  min?: number;\n  max?: number;\n  value?: number;\n  onChange?: (newVal: number | undefined) => void;\n};\n\nexport const InputNumberPlus = ({ min, max, value, onChange, addonAfter, ...restProps }: InputNumberPlusProps) => {\n  const updateInnerVal = (newVal: number | undefined) => {\n    onChange?.(newVal);\n  };\n\n  const processUpdateVal = (num: number) => {\n    let resVal = num;\n    if (min !== undefined) {\n      resVal = Math.max(min, resVal);\n    }\n    if (max !== undefined) {\n      resVal = Math.min(max, resVal);\n    }\n    updateInnerVal(resVal);\n  };\n  // 是否是合法的值\n  const isLegal = useMemo(() => {\n    if (value === undefined) {\n      return true;\n    }\n    let tempVal = value;\n    if (min !== undefined) {\n      tempVal = Math.max(min, tempVal);\n    }\n    if (max !== undefined) {\n      tempVal = Math.min(max, tempVal);\n    }\n\n    if (tempVal !== value) {\n      return false;\n    }\n    return true;\n  }, [max, min, value]);\n\n  return (\n    <div className={styles.inputNumberPlus}>\n      <div\n        style={{\n          display: 'flex',\n          position: 'relative',\n        }}\n      >\n        <Input\n          allowClear\n          {...restProps}\n          status={isLegal ? undefined : 'error'}\n          value={value}\n          onClear={() => {\n            updateInnerVal(undefined);\n          }}\n          onChange={(e) => {\n            const newNum = parseInt(e.target.value || '0', 10);\n            processUpdateVal(newNum);\n          }}\n          addonAfter={addonAfter}\n        />\n      </div>\n    </div>\n  );\n};\n"
  },
  {
    "path": "packages/engine/src/component/InputNumberPlus/style.module.scss",
    "content": ".inputNumberPlus {\n  display: flex;\n  &:hover {\n    .delIcon {\n      display: block;\n    }\n  }\n\n  .delIcon {\n    cursor: pointer;\n    display: none;\n  }\n}\n"
  },
  {
    "path": "packages/engine/src/component/MonacoEditor/index.tsx",
    "content": "import Editor, { EditorProps, Monaco } from '@monaco-editor/react';\n\nexport type EditorType = EditorProps;\n\nexport type MonacoEditorInstance = Parameters<Required<EditorType>['onMount']>[0];\n\nexport type MonacoEditorProps = {\n  beforeMount?: (monaco: Monaco) => void;\n  onDidMount?: (editor: MonacoEditorInstance) => void;\n  options?: EditorType['options'];\n  onChange?: EditorType['onChange'];\n  initialValue?: string;\n  language?: 'json' | 'javascript' | 'typescript' | 'css';\n  theme?: 'vs-dark' | 'light';\n};\n\nexport const MonacoEditor = (props: MonacoEditorProps) => {\n  const handleEditorBeforeMount: EditorType['beforeMount'] = (monaco) => {\n    props.beforeMount?.(monaco);\n  };\n\n  const handleEditorDidMount: EditorType['onMount'] = (editor) => {\n    props.onDidMount?.(editor);\n  };\n\n  return (\n    <Editor\n      height=\"100%\"\n      width=\"100%\"\n      defaultLanguage={props.language}\n      defaultValue={props.initialValue}\n      beforeMount={handleEditorBeforeMount}\n      onMount={handleEditorDidMount}\n      options={props.options}\n      theme={props.theme}\n      onChange={props.onChange}\n    />\n  );\n};\n"
  },
  {
    "path": "packages/engine/src/component/MoveableModal/index.tsx",
    "content": "import React, { useRef, useState } from 'react';\nimport { CSS, Transform } from '@dnd-kit/utilities';\nimport { Modal, ModalProps } from 'antd';\nimport { DndContext, PointerSensor, useDraggable, useSensor, useSensors } from '@dnd-kit/core';\nimport { getRandomStr } from '@chamn/model';\n\nexport type SortItemOrderProps = {} & ModalProps;\n\n/** 可拖拽移动的 Modal */\nexport const MoveableModal = (props: ModalProps) => {\n  const sensors = useSensors(\n    useSensor(PointerSensor, {\n      activationConstraint: {\n        distance: 15,\n      },\n    })\n  );\n\n  const [modalTransform, setModalTransform] = useState<Transform>({\n    x: 0,\n    y: 0,\n    scaleX: 1,\n    scaleY: 1,\n  });\n\n  return (\n    <Modal\n      {...props}\n      styles={{\n        mask: {\n          backgroundColor: 'rgba(0, 0, 0, 0.1)',\n          pointerEvents: 'none',\n        },\n      }}\n      modalRender={(modal) => {\n        return (\n          <DndContext\n            sensors={sensors}\n            onDragEnd={({ delta }) => {\n              const res = {\n                ...modalTransform,\n                ...delta,\n                x: modalTransform.x + (delta?.x || 0),\n                y: modalTransform.y + (delta?.y || 0),\n              };\n              setModalTransform(res);\n            }}\n          >\n            <ModalDragView modal={modal} transform={modalTransform} setModalTransform={setModalTransform} />\n          </DndContext>\n        );\n      }}\n    ></Modal>\n  );\n};\n\nconst ModalDragView = ({\n  modal,\n  transform,\n  setModalTransform,\n}: {\n  modal: React.ReactNode;\n  transform: Transform;\n  setModalTransform: any;\n}) => {\n  const id = useRef(getRandomStr());\n  const {\n    setNodeRef,\n    attributes,\n    listeners,\n    transform: tempTransform,\n  } = useDraggable({\n    id: id.current,\n  });\n\n  const finalTransform = {\n    ...transform,\n    ...tempTransform,\n    x: transform.x + (tempTransform?.x || 0),\n    y: transform.y + (tempTransform?.y || 0),\n  };\n\n  return (\n    <div\n      style={{\n        pointerEvents: 'auto',\n        transform: CSS.Transform.toString(finalTransform),\n      }}\n    >\n      <div\n        onDoubleClick={() => {\n          setModalTransform((oldValue: any) => {\n            return {\n              ...oldValue,\n              x: 0,\n              y: 0,\n            };\n          });\n        }}\n        ref={setNodeRef}\n        style={{\n          cursor: 'move',\n          position: 'absolute',\n          top: 0,\n          left: 0,\n          right: 0,\n          height: '55px',\n          zIndex: 1,\n        }}\n        {...attributes}\n        {...listeners}\n      />\n      {modal}\n    </div>\n  );\n};\n"
  },
  {
    "path": "packages/engine/src/component/StylePanel/BackgroundInput/index.tsx",
    "content": "import { Row, Col, Radio, Input } from 'antd';\nimport styles from '../style.module.scss';\nimport { forwardRef, useCallback, useImperativeHandle, useMemo, useRef, useState } from 'react';\nimport { DEFAULT_PRESET_COLORS } from '@/config/colorPickerColorList';\nimport clsx from 'clsx';\nimport { InputCommonRef } from '../type';\nimport { pick } from 'lodash-es';\nimport { CustomColorPicker, CustomColorPickerRef } from '@/component/CustomColorPicker';\n\nconst CSS_BACKGROUND_KEY_LIST = [\n  'background-color',\n  'background-image',\n  'background-repeat',\n  'background-size',\n] as const;\n\ntype CSSBackgroundKeyType = typeof CSS_BACKGROUND_KEY_LIST[number];\n\ntype Value = Record<CSSBackgroundKeyType, string>;\n\nexport type BackgroundInputProps = {\n  value?: Value;\n  initialValue?: Value;\n  onChange?: (newVal: Value) => void;\n};\n\nconst getEmptyVal = () => {\n  return {\n    'background-color': '',\n    'background-image': '',\n    'background-size': '',\n    'background-repeat': '',\n  };\n};\n\nexport const BackgroundInput = forwardRef<InputCommonRef, BackgroundInputProps>((props, ref) => {\n  const [innerVal, setInnerVal] = useState<Value>(props.initialValue ?? getEmptyVal());\n  const colorRef = useRef<CustomColorPickerRef>(null);\n\n  const updateInnerVal = useCallback(\n    (newVal: Partial<Value>, noTrigger?: boolean) => {\n      setInnerVal((oldVal) => {\n        const finalVal = {\n          ...oldVal,\n          ...newVal,\n        };\n        colorRef.current?.updateColor(finalVal['background-color']);\n        if (noTrigger !== true) {\n          props.onChange?.(finalVal);\n        }\n        return finalVal;\n      });\n    },\n    [props]\n  );\n\n  useImperativeHandle(\n    ref,\n    () => {\n      return {\n        setValue: (newVal) => {\n          const emptyVal = getEmptyVal();\n          if (Object.keys(newVal).length) {\n            updateInnerVal(\n              {\n                ...emptyVal,\n                ...pick(newVal, CSS_BACKGROUND_KEY_LIST),\n              },\n              true\n            );\n          } else {\n            updateInnerVal(emptyVal, true);\n          }\n        },\n        setEmptyValue: () => {\n          const emptyVal = getEmptyVal();\n          updateInnerVal(emptyVal, true);\n        },\n      };\n    },\n    [updateInnerVal]\n  );\n\n  const realValue = useMemo(() => {\n    return props.value ?? innerVal;\n  }, [props.value, innerVal]);\n\n  return (\n    <div>\n      <Row className={styles.row}>\n        <Col\n          span={24}\n          style={{\n            display: 'flex',\n          }}\n        >\n          <span className={clsx([styles.label])}>Color:</span>\n          <CustomColorPicker\n            ref={colorRef}\n            showText={true}\n            size=\"small\"\n            value={realValue['background-color'] || ''}\n            onChange={(color) => {\n              updateInnerVal({\n                'background-color': color,\n              });\n            }}\n            presets={DEFAULT_PRESET_COLORS}\n          />\n        </Col>\n      </Row>\n      <Row className={styles.row}>\n        <Col\n          span={24}\n          style={{\n            display: 'flex',\n          }}\n        >\n          <span className={styles.label}>Image:</span>\n          <Input\n            style={{ flex: 1 }}\n            size=\"small\"\n            value={/url\\(['|\"|*]?(.+?)['|\"|*]?\\)/.exec(realValue['background-image'])?.[1] || ''}\n            onChange={(e) => {\n              console.log(`url(\"${e.target.value}\")`);\n              updateInnerVal({\n                'background-image': `url(\"${e.target.value}\")`,\n              });\n            }}\n          />\n        </Col>\n      </Row>\n      <Row className={styles.row}>\n        <span className={styles.label}>Size:</span>\n        <Radio.Group\n          size=\"small\"\n          defaultValue=\"a\"\n          buttonStyle=\"solid\"\n          value={realValue['background-size']}\n          onChange={(e) => {\n            updateInnerVal({ 'background-size': e.target.value });\n          }}\n        >\n          <Radio.Button value=\"contain\">Contain</Radio.Button>\n          <Radio.Button value=\"cover\">Cover</Radio.Button>\n          <Radio.Button value=\"auto\">Auto</Radio.Button>\n        </Radio.Group>\n      </Row>\n      <Row className={styles.row}>\n        <span className={styles.label}>Repeat:</span>\n        <Radio.Group\n          size=\"small\"\n          buttonStyle=\"solid\"\n          value={realValue['background-repeat']}\n          onChange={(e) => {\n            updateInnerVal({ 'background-repeat': e.target.value });\n          }}\n        >\n          <Radio.Button value=\"repeat\">Repeat</Radio.Button>\n          <Radio.Button value=\"no-repeat\">NoRepeat</Radio.Button>\n        </Radio.Group>\n      </Row>\n    </div>\n  );\n});\n"
  },
  {
    "path": "packages/engine/src/component/StylePanel/BorderInput/index.tsx",
    "content": "import { CSSSizeInput } from '@/component/CSSSizeInput';\nimport { Row, Col, Radio } from 'antd';\nimport styles from '../style.module.scss';\nimport { forwardRef, useCallback, useImperativeHandle, useMemo, useRef, useState } from 'react';\nimport { DEFAULT_PRESET_COLORS } from '@/config/colorPickerColorList';\nimport clsx from 'clsx';\nimport { InputCommonRef } from '../type';\nimport { CloseCircleOutlined } from '@ant-design/icons';\nimport { pick } from 'lodash-es';\nimport { CustomColorPicker, CustomColorPickerRef } from '@/component/CustomColorPicker';\n\nconst CSS_BORDER_KEY_LIST = ['border', 'border-radius'] as const;\n\ntype CSSBorderKeyType = typeof CSS_BORDER_KEY_LIST[number];\ntype Value = Record<CSSBorderKeyType, string>;\n\nconst maxVal = {\n  px: 100,\n  vw: 100,\n  vh: 100,\n  rem: 100,\n};\n\nconst getEmptyVal = () => ({\n  border: '',\n  'border-radius': '',\n});\n\nexport type BorderInputProps = {\n  value?: Value;\n  initialValue?: Value;\n  onChange?: (newVal: Value) => void;\n};\n\nexport const BorderInput = forwardRef<InputCommonRef, BorderInputProps>((props, ref) => {\n  const [innerVal, setInnerVal] = useState<Value>(props.initialValue ?? getEmptyVal());\n  const colorRef = useRef<CustomColorPickerRef>(null);\n\n  const updateInnerVal = useCallback(\n    (newVal: Partial<Value>, noTrigger?: boolean) => {\n      setInnerVal((oldVal) => {\n        const finalVal = {\n          ...oldVal,\n          ...newVal,\n        };\n        if (noTrigger !== true) {\n          props.onChange?.(finalVal);\n        }\n        return finalVal;\n      });\n    },\n    [props]\n  );\n\n  useImperativeHandle(\n    ref,\n    () => {\n      return {\n        setValue: (newVal) => {\n          const emptyVal = getEmptyVal();\n          if (Object.keys(newVal).length) {\n            updateInnerVal(\n              {\n                ...emptyVal,\n                ...pick(newVal, CSS_BORDER_KEY_LIST),\n              },\n              true\n            );\n          } else {\n            updateInnerVal(emptyVal, true);\n          }\n        },\n        setEmptyValue: () => {\n          updateInnerVal(\n            {\n              ...getEmptyVal(),\n            },\n            true\n          );\n        },\n      };\n    },\n    [updateInnerVal]\n  );\n\n  const currentEditVal = useMemo(() => {\n    const tempVal = innerVal['border'] || '';\n    const list = tempVal.split(' ');\n    const tmpVal = {\n      size: list[0] || '',\n      type: list[1] || '',\n      color: tempVal.replace(' ', '').replace(list[0], '').replace(list[1], ''),\n    };\n    colorRef.current?.updateColor(tmpVal.color);\n\n    return tmpVal;\n  }, [innerVal]);\n\n  const updateSingleBorderVal = (newBorderVal: typeof currentEditVal) => {\n    updateInnerVal({\n      border: `${newBorderVal.size || '0px'} ${newBorderVal.type || 'none'} ${\n        newBorderVal.color || 'transparent'\n      }`.trim(),\n    });\n  };\n\n  return (\n    <div>\n      <Row className={styles.row}>\n        <Col\n          style={{\n            position: 'relative',\n          }}\n          span={24}\n        >\n          <CloseCircleOutlined\n            style={{\n              position: 'absolute',\n              right: 0,\n              top: '5px',\n              color: '#4e4e4e',\n              cursor: 'pointer',\n            }}\n            onClick={() => {\n              updateInnerVal({\n                border: undefined,\n              });\n            }}\n          />\n          <span className={styles.label}>Size:</span>\n          <CSSSizeInput\n            min={0}\n            max={maxVal}\n            value={currentEditVal.size}\n            style={{\n              width: '100px',\n            }}\n            unitList={['px']}\n            onValueChange={(val) => {\n              updateSingleBorderVal({\n                ...currentEditVal,\n                size: val || '',\n              });\n            }}\n            size=\"small\"\n          />\n        </Col>\n      </Row>\n      <Row className={styles.row}>\n        <Col\n          style={{\n            display: 'flex',\n          }}\n        >\n          <span className={clsx([styles.label])}>Color:</span>\n          <CustomColorPicker\n            ref={colorRef}\n            showText={true}\n            size=\"small\"\n            value={currentEditVal.color || ''}\n            onChange={(color) => {\n              updateSingleBorderVal({\n                ...currentEditVal,\n                color: color,\n              });\n            }}\n            presets={DEFAULT_PRESET_COLORS}\n          />\n        </Col>\n      </Row>\n      <Row className={styles.row}>\n        <Col\n          style={{\n            display: 'flex',\n          }}\n        >\n          <span className={clsx([styles.label])}>Type:</span>\n          <Radio.Group\n            value={currentEditVal.type}\n            size=\"small\"\n            onChange={(e) => {\n              updateSingleBorderVal({\n                ...currentEditVal,\n                type: e.target.value || '',\n              });\n            }}\n          >\n            <Radio.Button value=\"solid\">Solid</Radio.Button>\n            <Radio.Button value=\"dashed\">Dashed</Radio.Button>\n            <Radio.Button value=\"dotted\">Dotted</Radio.Button>\n            <Radio.Button value=\"none\">None</Radio.Button>\n          </Radio.Group>\n        </Col>\n      </Row>\n      <Row className={styles.row}>\n        <Col>\n          <span className={styles.label}>Radius:</span>\n          <CSSSizeInput\n            min={0}\n            max={maxVal}\n            value={innerVal['border-radius']}\n            onValueChange={(val) => {\n              updateInnerVal({\n                'border-radius': val,\n              });\n            }}\n            style={{\n              width: '158px',\n            }}\n            size=\"small\"\n          />\n        </Col>\n      </Row>\n    </div>\n  );\n});\n"
  },
  {
    "path": "packages/engine/src/component/StylePanel/DimensionInput/index.tsx",
    "content": "import { CSSSizeInput } from '@/component/CSSSizeInput';\nimport { Row, Col, Radio, Button } from 'antd';\nimport styles from '../style.module.scss';\nimport { forwardRef, useCallback, useImperativeHandle, useMemo, useState } from 'react';\nimport { InputCommonRef } from '../type';\nimport { pick } from 'lodash-es';\nimport {\n  AlignHorizontalDistributeEnd,\n  AlignHorizontalDistributeStart,\n  AlignHorizontalSpaceAround,\n  AlignHorizontalSpaceBetween,\n  AlignVerticalJustifyEnd,\n  AlignVerticalJustifyStart,\n  AlignVerticalSpaceAround,\n  AlignVerticalSpaceBetween,\n  ArrowDownFromLine,\n  ArrowLeftFromLine,\n  ArrowRightFromLine,\n  ArrowUpFromLine,\n  MoveHorizontal,\n  MoveVertical,\n  SquareX,\n  StretchHorizontal,\n  StretchVertical,\n} from 'lucide-react';\nconst keyList = [\n  'width',\n  'height',\n  'min-width',\n  'max-width',\n  'min-height',\n  'max-height',\n  'flex',\n  'display',\n  'flex-direction',\n  'justify-content',\n  'align-items',\n  'flex-wrap',\n] as const;\n\ntype Value = Record<typeof keyList[number], string>;\n\nexport type DimensionInputProps = {\n  value?: Value;\n  initialValue?: Value;\n  onChange?: (newVal: Value) => void;\n};\n\nfunction getDefaultValue(list: string[]): any {\n  return list.reduce((res, el) => {\n    return {\n      ...res,\n      [el]: '',\n    };\n  }, {});\n}\n\nconst maxVal = {\n  px: 2048,\n  vw: 100,\n  vh: 100,\n  rem: 100,\n};\n\nexport const DimensionInput = forwardRef<InputCommonRef, DimensionInputProps>((props, ref) => {\n  const [innerVal, setInnerVal] = useState<Value>(props.initialValue ?? getDefaultValue(keyList as any));\n\n  const updateInnerVal = useCallback(\n    (newVal: Partial<Value>, noTrigger?: boolean) => {\n      setInnerVal((oldVal) => {\n        const finalVal = {\n          ...oldVal,\n          ...pick(newVal, keyList),\n        };\n\n        if (noTrigger !== true) {\n          props.onChange?.(finalVal);\n        }\n        return finalVal;\n      });\n    },\n    [props]\n  );\n\n  useImperativeHandle(\n    ref,\n    () => {\n      return {\n        setValue: (newVal) =>\n          updateInnerVal(\n            {\n              ...getDefaultValue(keyList as any),\n              ...newVal,\n            },\n            true\n          ),\n        setEmptyValue: () => {\n          setInnerVal(getDefaultValue(keyList as any));\n        },\n      };\n    },\n    [updateInnerVal]\n  );\n\n  const realValue = useMemo(() => {\n    return props.value ?? innerVal;\n  }, [props.value, innerVal]);\n\n  return (\n    <div>\n      <Row className={styles.row}>\n        <Col span={24} className=\"flex\">\n          <span className={styles.label}>layout:</span>\n          <Radio.Group\n            value={realValue.display}\n            size=\"small\"\n            buttonStyle=\"solid\"\n            onChange={(e) => {\n              updateInnerVal({\n                display: e.target.value,\n              });\n            }}\n          >\n            <Radio.Button value=\"block\">Block</Radio.Button>\n            <Radio.Button value=\"flex\">Flex</Radio.Button>\n            <Radio.Button value=\"inline-block\">InlineB</Radio.Button>\n            <Radio.Button value=\"inline\">Inline</Radio.Button>\n            <Radio.Button value=\"none\">None</Radio.Button>\n          </Radio.Group>\n        </Col>\n      </Row>\n      {/* flex 布局属性 */}\n      {realValue.display === 'flex' && (\n        <>\n          <Row className={styles.row}>\n            <Col\n              span={24}\n              style={{\n                display: 'flex',\n                flexDirection: 'row',\n                alignItems: 'center',\n              }}\n            >\n              <span className={styles.label}>AxisD:</span>\n              <div\n                style={{\n                  width: '200px',\n                  marginLeft: '3px',\n                }}\n              >\n                <Radio.Group\n                  size=\"small\"\n                  value={realValue['flex-direction']}\n                  buttonStyle={'outline'}\n                  options={[\n                    {\n                      label: <ArrowRightFromLine size={14} style={{ top: '3px', position: 'relative' }} />,\n                      value: 'row',\n                    },\n                    {\n                      label: <ArrowLeftFromLine size={14} style={{ top: '3px', position: 'relative' }} />,\n                      value: 'row-reverse',\n                    },\n                    {\n                      label: <ArrowDownFromLine size={14} style={{ top: '3px', position: 'relative' }} />,\n                      value: 'column',\n                    },\n                    {\n                      label: <ArrowUpFromLine size={14} style={{ top: '3px', position: 'relative' }} />,\n                      value: 'column-reverse',\n                    },\n                    {\n                      label: <SquareX size={14} style={{ top: '3px', position: 'relative' }} />,\n                      value: '',\n                    },\n                  ]}\n                  onChange={(e) => {\n                    updateInnerVal({\n                      'flex-direction': e.target.value || '',\n                    });\n                  }}\n                  optionType=\"button\"\n                />\n              </div>\n            </Col>\n          </Row>\n          <Row className={styles.row}>\n            <Col\n              span={24}\n              style={{\n                display: 'flex',\n                flexDirection: 'row',\n                alignItems: 'center',\n              }}\n            >\n              <span className={styles.label}>MAxis:</span>\n              <div\n                style={{\n                  width: '200px',\n                  marginLeft: '3px',\n                }}\n              >\n                <Radio.Group\n                  size=\"small\"\n                  value={realValue['justify-content']}\n                  buttonStyle={'outline'}\n                  options={[\n                    {\n                      label: <AlignHorizontalDistributeStart size={14} style={{ top: '3px', position: 'relative' }} />,\n                      value: 'flex-start',\n                    },\n                    {\n                      label: <AlignHorizontalDistributeEnd size={14} style={{ top: '3px', position: 'relative' }} />,\n                      value: 'flex-end',\n                    },\n                    {\n                      label: <StretchVertical size={14} style={{ top: '3px', position: 'relative' }} />,\n                      value: 'center',\n                    },\n                    {\n                      label: <AlignHorizontalSpaceBetween size={14} style={{ top: '3px', position: 'relative' }} />,\n                      value: 'space-between',\n                    },\n                    {\n                      label: <AlignHorizontalSpaceAround size={14} style={{ top: '3px', position: 'relative' }} />,\n                      value: 'space-around',\n                    },\n                    {\n                      label: <SquareX size={14} style={{ top: '3px', position: 'relative' }} />,\n                      value: '',\n                    },\n                  ]}\n                  onChange={(e) => {\n                    updateInnerVal({\n                      'justify-content': e.target.value || '',\n                    });\n                  }}\n                  optionType=\"button\"\n                />\n              </div>\n            </Col>\n          </Row>\n          <Row className={styles.row}>\n            <Col\n              span={24}\n              style={{\n                display: 'flex',\n                flexDirection: 'row',\n                alignItems: 'center',\n              }}\n            >\n              <span className={styles.label}>CAxis:</span>\n              <div\n                style={{\n                  width: '200px',\n                  marginLeft: '3px',\n                }}\n              >\n                <Radio.Group\n                  size=\"small\"\n                  value={realValue['align-items']}\n                  buttonStyle={'outline'}\n                  options={[\n                    {\n                      label: <AlignVerticalJustifyStart size={14} style={{ top: '3px', position: 'relative' }} />,\n                      value: 'flex-start',\n                    },\n                    {\n                      label: <AlignVerticalJustifyEnd size={14} style={{ top: '3px', position: 'relative' }} />,\n                      value: 'flex-end',\n                    },\n                    {\n                      label: <StretchHorizontal size={14} style={{ top: '3px', position: 'relative' }} />,\n                      value: 'center',\n                    },\n                    {\n                      label: <AlignVerticalSpaceBetween size={14} style={{ top: '3px', position: 'relative' }} />,\n                      value: 'space-between',\n                    },\n                    {\n                      label: <AlignVerticalSpaceAround size={14} style={{ top: '3px', position: 'relative' }} />,\n                      value: 'space-around',\n                    },\n                    {\n                      label: <SquareX size={14} style={{ top: '3px', position: 'relative' }} />,\n                      value: '',\n                    },\n                  ]}\n                  onChange={(e) => {\n                    updateInnerVal({\n                      'align-items': e.target.value || '',\n                    });\n                  }}\n                  optionType=\"button\"\n                />\n              </div>\n            </Col>\n          </Row>\n          <Row className={styles.row}>\n            <Col span={12} className=\"flex\">\n              <span className={styles.label}>flex:</span>\n              <CSSSizeInput\n                className={styles.inputWidth}\n                unit={false}\n                min={0}\n                max={maxVal}\n                value={realValue['flex']}\n                onValueChange={(newVal) => {\n                  updateInnerVal({\n                    flex: newVal || '',\n                  });\n                }}\n                size=\"small\"\n              />\n            </Col>\n          </Row>\n        </>\n      )}\n      {/* flex 布局属性 end */}\n\n      <Row className={styles.row}>\n        <Col\n          span={24}\n          style={{\n            display: 'flex',\n            alignItems: 'center',\n          }}\n        >\n          <span className={styles.label}>width:</span>\n          <CSSSizeInput\n            style={{\n              width: '180px',\n            }}\n            min={0}\n            max={maxVal}\n            value={realValue.width}\n            onValueChange={(val) => {\n              updateInnerVal({\n                width: val,\n              });\n            }}\n            className={styles.inputWidth}\n            size=\"small\"\n          />\n          <Button\n            size=\"small\"\n            style={{\n              marginLeft: '10px',\n            }}\n            onClick={() => {\n              updateInnerVal({\n                width: '100%',\n              });\n            }}\n          >\n            <MoveHorizontal size={14} />\n          </Button>\n        </Col>\n      </Row>\n      <Row className={styles.row}>\n        <Col\n          span={24}\n          style={{\n            display: 'flex',\n            alignItems: 'center',\n          }}\n        >\n          <span className={styles.label}>height:</span>\n          <CSSSizeInput\n            min={0}\n            style={{\n              width: '180px',\n            }}\n            max={maxVal}\n            value={realValue.height}\n            onValueChange={(val) => {\n              updateInnerVal({\n                height: val,\n              });\n            }}\n            className={styles.inputWidth}\n            size=\"small\"\n          />\n          <Button\n            size=\"small\"\n            style={{\n              marginLeft: '10px',\n            }}\n            onClick={() => {\n              updateInnerVal({\n                height: '100%',\n              });\n            }}\n          >\n            <MoveVertical size={14} />\n          </Button>\n        </Col>\n      </Row>\n      <div>\n        <Row className={styles.row}>\n          <Col span={12} className=\"flex\">\n            <span className={styles.label}>minW:</span>\n            <CSSSizeInput\n              className={styles.inputWidth}\n              min={0}\n              max={maxVal}\n              value={realValue['min-width']}\n              onValueChange={(newVal) => {\n                updateInnerVal({\n                  'min-width': newVal,\n                });\n              }}\n              size=\"small\"\n            />\n          </Col>\n          <Col span={12}>\n            <span className={styles.label}>minH:</span>\n            <CSSSizeInput\n              min={0}\n              max={maxVal}\n              value={realValue['min-height']}\n              onValueChange={(newVal) => {\n                updateInnerVal({\n                  'min-height': newVal,\n                });\n              }}\n              className={styles.inputWidth}\n              size=\"small\"\n            />\n          </Col>\n        </Row>\n        <Row className={styles.row}>\n          <Col span={12} className=\"flex\">\n            <span className={styles.label}>maxW:</span>\n            <CSSSizeInput\n              className={styles.inputWidth}\n              min={0}\n              max={maxVal}\n              value={realValue['max-width']}\n              onValueChange={(newVal) => {\n                updateInnerVal({\n                  'max-width': newVal,\n                });\n              }}\n              size=\"small\"\n            />\n          </Col>\n          <Col span={12}>\n            <span className={styles.label}>maxH:</span>\n            <CSSSizeInput\n              className={styles.inputWidth}\n              min={0}\n              max={maxVal}\n              value={realValue['max-height']}\n              onValueChange={(newVal) => {\n                updateInnerVal({\n                  'max-height': newVal,\n                });\n              }}\n              size=\"small\"\n            />\n          </Col>\n        </Row>\n      </div>\n    </div>\n  );\n});\n"
  },
  {
    "path": "packages/engine/src/component/StylePanel/FontInput/index.tsx",
    "content": "import { CSSSizeInput } from '@/component/CSSSizeInput';\nimport { Row, Col, Radio } from 'antd';\nimport styles from '../style.module.scss';\nimport { forwardRef, useCallback, useImperativeHandle, useMemo, useRef, useState } from 'react';\nimport { DEFAULT_PRESET_COLORS } from '@/config/colorPickerColorList';\nimport clsx from 'clsx';\nimport { InputCommonRef } from '../type';\nimport { pick } from 'lodash-es';\nimport { CustomColorPicker, CustomColorPickerRef } from '@/component/CustomColorPicker';\n\nconst FONT_CSS_KEY_LIST = ['font-size', 'color', 'text-align', 'font-weight'] as const;\ntype FontKeyType = typeof FONT_CSS_KEY_LIST[number];\ntype Value = Record<FontKeyType, string>;\n\nexport type FontInputProps = {\n  initialValue?: Value;\n  value?: Value;\n  onChange?: (newVal: Value) => void;\n};\n\nconst maxVal = {\n  px: 100,\n  vw: 100,\n  vh: 100,\n  rem: 100,\n};\n\nconst alignOptions = [\n  { label: <span className={styles.fontOption}>Left</span>, value: 'left' },\n  { label: <span className={styles.fontOption}>Center</span>, value: 'center' },\n  { label: <span className={styles.fontOption}>Right</span>, value: 'Right' },\n];\n\nconst fontWeightOptions = [\n  { label: <span className={styles.fontOption}>Regular</span>, value: '400' },\n  { label: <span className={styles.fontOption}>Medium</span>, value: '500' },\n  { label: <span className={styles.fontOption}>Bold</span>, value: '700' },\n];\n\nconst getEmptyVal = () => ({\n  'font-size': '',\n  color: '',\n  'text-align': '',\n  'font-weight': '',\n});\n\nexport const FontInput = forwardRef<InputCommonRef, FontInputProps>((props, ref) => {\n  const [innerVal, setInnerVal] = useState<Value>(props.initialValue ?? getEmptyVal());\n  const colorRef = useRef<CustomColorPickerRef>(null);\n  const updateInnerVal = useCallback(\n    (newVal: Partial<Value>, noTrigger?: boolean) => {\n      setInnerVal((oldVal) => {\n        const finalVal = {\n          ...oldVal,\n          ...newVal,\n        };\n        if (noTrigger !== true) {\n          props.onChange?.(finalVal);\n        }\n        return finalVal;\n      });\n    },\n    [props]\n  );\n\n  useImperativeHandle(\n    ref,\n    () => {\n      return {\n        setValue: (newVal) => {\n          colorRef.current?.updateColor(newVal?.color || '');\n          updateInnerVal(\n            {\n              ...getEmptyVal(),\n              ...pick(newVal, FONT_CSS_KEY_LIST),\n            },\n            true\n          );\n        },\n        setEmptyValue: () => {\n          updateInnerVal(\n            {\n              ...getEmptyVal(),\n            },\n            true\n          );\n        },\n      };\n    },\n    [updateInnerVal]\n  );\n\n  const realValue = useMemo(() => {\n    return props.value ?? innerVal;\n  }, [props.value, innerVal]);\n\n  return (\n    <div>\n      <Row className={styles.row}>\n        <Col\n          style={{\n            display: 'flex',\n          }}\n        >\n          <span className={clsx([styles.label])}>Color:</span>\n          <CustomColorPicker\n            ref={colorRef}\n            showText={true}\n            size=\"small\"\n            onChange={(color) => {\n              updateInnerVal({\n                color: color,\n              });\n            }}\n            presets={DEFAULT_PRESET_COLORS}\n          />\n        </Col>\n      </Row>\n      <Row className={styles.row}>\n        <Col>\n          <span className={styles.label}>Size:</span>\n          <CSSSizeInput\n            min={0}\n            max={maxVal}\n            value={realValue['font-size']}\n            onValueChange={(val) => {\n              updateInnerVal({\n                'font-size': val,\n              });\n            }}\n            style={{\n              width: '158px',\n            }}\n            size=\"small\"\n          />\n        </Col>\n      </Row>\n      <Row className={styles.row}>\n        <span className={clsx([styles.label])}>Align:</span>\n        <Radio.Group\n          options={alignOptions}\n          onChange={({ target: { value: newVal } }) => {\n            updateInnerVal({\n              'text-align': newVal,\n            });\n          }}\n          value={realValue['text-align']}\n        />\n      </Row>\n      <Row className={styles.row}>\n        <span className={clsx([styles.label])}>Weight:</span>\n        <Radio.Group\n          options={fontWeightOptions}\n          onChange={({ target: { value: newVal } }) => {\n            updateInnerVal({\n              'font-weight': newVal,\n            });\n          }}\n          value={realValue['font-weight']}\n        />\n      </Row>\n    </div>\n  );\n});\n"
  },
  {
    "path": "packages/engine/src/component/StylePanel/MarginAndPaddingInput/index.tsx",
    "content": "import { CSSSizeInput } from '@/component/CSSSizeInput';\nimport { Row, Col } from 'antd';\nimport styles from '../style.module.scss';\nimport { forwardRef, useCallback, useImperativeHandle, useMemo, useState } from 'react';\nimport { InputCommonRef } from '../type';\nimport clsx from 'clsx';\nimport { pick } from 'lodash-es';\n\ntype Value = Record<string, string>;\n\nexport type MarginAndPaddingInputProps = {\n  value?: Value;\n  initialValue?: Value;\n  onChange?: (newVal: Value) => void;\n  prefix: 'margin' | 'padding';\n};\n\nconst maxVal = {\n  px: 2048,\n  vw: 100,\n  vh: 100,\n  rem: 100,\n};\n\nconst inputW = '100px';\n\nconst getEmptyVal = () => ({\n  left: '',\n  right: '',\n  top: '',\n  bottom: '',\n});\n\nexport const MarginAndPaddingInput = forwardRef<InputCommonRef, MarginAndPaddingInputProps>((props, ref) => {\n  const [innerVal, setInnerVal] = useState<Value>(props.initialValue ?? getEmptyVal());\n  const keyList = useMemo(() => {\n    return ['left', 'top', 'right', 'bottom'].map((el) => `${props.prefix}-${el}`);\n  }, [props.prefix]);\n\n  const updateInnerVal = useCallback(\n    (newVal: Partial<any>, noTrigger?: boolean) => {\n      setInnerVal((oldVal) => {\n        const finalVal = {\n          ...oldVal,\n          ...newVal,\n        };\n        const outVal = Object.keys(finalVal).reduce((res, k) => {\n          if (k === 'all') {\n            res[`${props.prefix}`] = (finalVal as unknown as any)?.[k];\n          } else {\n            res[`${props.prefix}-${k}`] = (finalVal as unknown as any)?.[k];\n          }\n          return res;\n        }, {} as any);\n        if (noTrigger !== true) {\n          props.onChange?.(pick(outVal, keyList));\n        }\n        return finalVal;\n      });\n    },\n    [keyList, props]\n  );\n\n  const formatValue = useCallback(\n    (obj: Record<string, any>, prefix: string) => {\n      const targetKeyList = Object.keys(obj || {}).filter((k) => k.includes(prefix));\n      const tempVal = targetKeyList.reduce((res, k) => {\n        if (k.replace(`${props.prefix}`, '') === '') {\n          res['all'] = (obj as unknown as any)?.[k];\n        } else {\n          res[k.replace(`${props.prefix}-`, '')] = (obj as unknown as any)?.[k];\n        }\n        return res;\n      }, {} as any);\n      return tempVal;\n    },\n    [props.prefix]\n  );\n\n  useImperativeHandle(\n    ref,\n    () => {\n      return {\n        setValue: (newVal) => {\n          updateInnerVal(\n            {\n              ...getEmptyVal(),\n              ...formatValue(newVal, props.prefix),\n            },\n            true\n          );\n        },\n        setEmptyValue: () => {\n          updateInnerVal(\n            {\n              ...getEmptyVal(),\n            },\n            true\n          );\n        },\n      };\n    },\n    [formatValue, props.prefix, updateInnerVal]\n  );\n\n  const realValue = useMemo(() => {\n    const tempVal = formatValue(props.value || {}, props.prefix);\n    if (props.value === undefined) {\n      return innerVal;\n    }\n    return tempVal;\n  }, [formatValue, props.value, props.prefix, innerVal]);\n\n  return (\n    <div>\n      <Row className={styles.row}>\n        <Col\n          span={12}\n          style={{\n            display: 'flex',\n          }}\n        >\n          <span className={clsx([styles.label, styles['m-p']])}>T:</span>\n          <CSSSizeInput\n            min={0}\n            max={maxVal}\n            value={realValue.top}\n            onValueChange={(val) => {\n              updateInnerVal({\n                top: val,\n              });\n            }}\n            style={{\n              width: inputW,\n            }}\n            size=\"small\"\n          />\n        </Col>\n        <Col span={12}>\n          <span className={clsx([styles.label, styles['m-p']])}>L:</span>\n          <CSSSizeInput\n            min={0}\n            max={maxVal}\n            value={realValue.left}\n            onValueChange={(val) => {\n              updateInnerVal({\n                left: val,\n              });\n            }}\n            style={{\n              width: inputW,\n            }}\n            size=\"small\"\n          />\n        </Col>\n      </Row>\n      <div>\n        <Row className={styles.row}>\n          <Col span={12}>\n            <span className={clsx([styles.label, styles['m-p']])}>B:</span>\n            <CSSSizeInput\n              min={0}\n              max={maxVal}\n              value={realValue.bottom}\n              onValueChange={(newVal) => {\n                updateInnerVal({\n                  bottom: newVal,\n                });\n              }}\n              style={{\n                width: inputW,\n              }}\n              size=\"small\"\n            />\n          </Col>\n          <Col\n            span={12}\n            style={{\n              display: 'flex',\n            }}\n          >\n            <span className={clsx([styles.label, styles['m-p']])}>R:</span>\n            <CSSSizeInput\n              style={{\n                width: inputW,\n              }}\n              min={0}\n              max={maxVal}\n              value={realValue.right}\n              onValueChange={(newVal) => {\n                updateInnerVal({\n                  right: newVal,\n                });\n              }}\n              size=\"small\"\n            />\n          </Col>\n        </Row>\n      </div>\n    </div>\n  );\n});\n"
  },
  {
    "path": "packages/engine/src/component/StylePanel/ShadowInput/index.tsx",
    "content": "import { CSSSizeInput } from '@/component/CSSSizeInput';\nimport { Row, Col } from 'antd';\nimport styles from '../style.module.scss';\nimport { forwardRef, useCallback, useImperativeHandle, useMemo, useRef, useState } from 'react';\nimport { DEFAULT_PRESET_COLORS } from '@/config/colorPickerColorList';\nimport clsx from 'clsx';\nimport { BoxShadowObjType, parseBoxShadowString } from '@/utils';\nimport { InputCommonRef } from '../type';\nimport { CustomColorPicker, CustomColorPickerRef } from '@/component/CustomColorPicker';\n\ntype Value = Record<'box-shadow', string>;\n\nconst getEmptyVal = () => ({\n  'box-shadow': '',\n});\n\nexport type ShadowInputProps = {\n  value?: Value;\n  initialValue?: Value;\n  onChange?: (newVal: Value) => void;\n};\n\nexport const ShadowInput = forwardRef<InputCommonRef, ShadowInputProps>((props, ref) => {\n  const [innerVal, setInnerVal] = useState<Value>(props.initialValue ?? getEmptyVal());\n  const colorRef = useRef<CustomColorPickerRef>(null);\n\n  const realValue = useMemo(() => {\n    const tempVal = props.value?.['box-shadow'] ?? innerVal['box-shadow'];\n    const obj = parseBoxShadowString(tempVal || '')[0] || {};\n    colorRef.current?.updateColor(obj.color || '');\n    return obj;\n  }, [props.value, innerVal]);\n\n  const updateInnerVal = useCallback(\n    (newVal: Partial<BoxShadowObjType>, noTrigger?: boolean) => {\n      setInnerVal((oldVal) => {\n        console.log('set val', oldVal, newVal);\n        const tempVal = props.value?.['box-shadow'];\n        const obj = parseBoxShadowString(tempVal || '')[0] || {};\n        const newUpdateVal = {\n          ...obj,\n          ...newVal,\n        };\n        const boxShadowStr = `${newUpdateVal.offsetX || '0'} ${newUpdateVal.offsetY || '0'} ${\n          newUpdateVal.blur || '0'\n        } ${newUpdateVal.spread || '0'} ${newUpdateVal.color || 'transparent'}`;\n        const finalVal: Value = {\n          ...oldVal,\n          'box-shadow': boxShadowStr,\n        };\n        if (noTrigger !== true) {\n          props.onChange?.(finalVal);\n        }\n        return finalVal;\n      });\n    },\n    [props]\n  );\n\n  useImperativeHandle(\n    ref,\n    () => {\n      return {\n        setValue: (newVal) => {\n          updateInnerVal(\n            {\n              ...getEmptyVal(),\n              ...parseBoxShadowString(newVal['box-shadow'] || '')[0],\n            },\n            true\n          );\n        },\n        setEmptyValue: () => {\n          updateInnerVal(\n            {\n              ...(getEmptyVal() as any),\n            },\n            true\n          );\n        },\n      };\n    },\n    [updateInnerVal]\n  );\n\n  return (\n    <div>\n      <Row className={styles.row}>\n        <Col>\n          <span className={styles.label}>OffsetX:</span>\n          <CSSSizeInput\n            min={-40}\n            max={40}\n            unitList={['px']}\n            value={realValue['offsetX']}\n            onValueChange={(val) => {\n              updateInnerVal({\n                ...realValue,\n                offsetX: val,\n              });\n            }}\n            style={{\n              width: '158px',\n            }}\n            size=\"small\"\n          />\n        </Col>\n      </Row>\n      <Row className={styles.row}>\n        <Col>\n          <span className={styles.label}>OffsetY:</span>\n          <CSSSizeInput\n            min={-40}\n            max={40}\n            unitList={['px']}\n            value={realValue['offsetY']}\n            onValueChange={(val) => {\n              updateInnerVal({\n                ...realValue,\n\n                offsetY: val,\n              });\n            }}\n            style={{\n              width: '158px',\n            }}\n            size=\"small\"\n          />\n        </Col>\n      </Row>\n      <Row className={styles.row}>\n        <Col>\n          <span className={styles.label}>Blur:</span>\n          <CSSSizeInput\n            min={-40}\n            max={40}\n            unitList={['px']}\n            value={realValue['blur']}\n            onValueChange={(val) => {\n              updateInnerVal({\n                ...realValue,\n\n                blur: val,\n              });\n            }}\n            style={{\n              width: '158px',\n            }}\n            size=\"small\"\n          />\n        </Col>\n      </Row>\n      <Row className={styles.row}>\n        <Col>\n          <span className={styles.label}>Spread:</span>\n          <CSSSizeInput\n            min={-40}\n            max={40}\n            unitList={['px']}\n            value={realValue['spread']}\n            onValueChange={(val) => {\n              updateInnerVal({\n                ...realValue,\n\n                spread: val,\n              });\n            }}\n            style={{\n              width: '158px',\n            }}\n            size=\"small\"\n          />\n        </Col>\n      </Row>\n      <Row className={styles.row}>\n        <Col\n          span={24}\n          style={{\n            display: 'flex',\n          }}\n        >\n          <span className={clsx([styles.label])}>Color:</span>\n          <CustomColorPicker\n            ref={colorRef}\n            showText={true}\n            size=\"small\"\n            onChange={(color) => {\n              updateInnerVal({\n                ...realValue,\n\n                color: color,\n              });\n            }}\n            presets={DEFAULT_PRESET_COLORS}\n          />\n        </Col>\n      </Row>\n    </div>\n  );\n});\n"
  },
  {
    "path": "packages/engine/src/component/StylePanel/index.tsx",
    "content": "import { Button, Card, Collapse, CollapseProps, ConfigProvider } from 'antd';\nimport { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';\nimport styles from './style.module.scss';\nimport { DimensionInput } from './DimensionInput';\nimport { MarginAndPaddingInput } from './MarginAndPaddingInput';\nimport { FontInput } from './FontInput';\nimport { BorderInput } from './BorderInput';\nimport { BackgroundInput } from './BackgroundInput';\nimport { ShadowInput } from './ShadowInput';\nimport { InputCommonRef } from './type';\nimport { waitReactUpdate } from '@/utils';\nimport { isNil, omitBy } from 'lodash-es';\nimport { CSSCodeEditor, CSSCodeEditorRef } from '../CSSCodeEditor';\nimport { Component, SquareCode } from 'lucide-react';\n\nexport type StyleUIPanelProps = {\n  initialVal?: Record<string, string>;\n  value?: Record<string, string>;\n  onValueChange?: (newVal: Record<string, string>) => void;\n  noCard?: boolean;\n};\n\nexport type StyleUIPanelRef = InputCommonRef;\n\nexport const StyleUIPanel = forwardRef<StyleUIPanelRef, StyleUIPanelProps>(\n  ({ value, initialVal, onValueChange, noCard }, ref) => {\n    const [mode, setMode] = useState<'VISUAL' | 'CODE'>(\n      (localStorage.getItem('CHAMN_STYLE_EDITOR_MODE') as any) || 'VISUAL'\n    );\n    const dimensionRef = useRef<InputCommonRef>(null);\n    const marinRef = useRef<InputCommonRef>(null);\n    const paddingRef = useRef<InputCommonRef>(null);\n    const borderRef = useRef<InputCommonRef>(null);\n    const backgroundRef = useRef<InputCommonRef>(null);\n    const shadowRef = useRef<InputCommonRef>(null);\n    const fontRef = useRef<InputCommonRef>(null);\n    const cssCodeEditorRef = useRef<CSSCodeEditorRef>(null);\n\n    useEffect(() => {\n      localStorage.setItem('CHAMN_STYLE_EDITOR_MODE', mode);\n    }, [mode]);\n\n    const inputRefs = useMemo(() => {\n      return [dimensionRef, marinRef, paddingRef, borderRef, backgroundRef, shadowRef, fontRef];\n    }, [dimensionRef, marinRef, paddingRef, borderRef, backgroundRef, shadowRef, fontRef]);\n\n    const tempValueRef = useRef<any>(null);\n\n    const updateInnerVal = useCallback(\n      (newVal: any) => {\n        if (!newVal) {\n          return;\n        }\n        // 外部赋值\n        tempValueRef.current = newVal;\n        if (mode === 'CODE') {\n          cssCodeEditorRef.current?.setValue(newVal);\n          return;\n        }\n\n        inputRefs.forEach((ref) => {\n          setTimeout(() => {\n            ref.current?.setValue(newVal);\n          });\n        });\n      },\n      [inputRefs, mode]\n    );\n\n    const onStyleItemChange = useCallback(\n      (val: any) => {\n        const newVal = {\n          ...(tempValueRef.current || {}),\n          ...val,\n        };\n        tempValueRef.current = newVal;\n\n        const finalValue = omitBy(newVal, (value) => isNil(value) || value === '');\n        onValueChange?.(finalValue);\n      },\n      [onValueChange]\n    );\n\n    useEffect(() => {\n      updateInnerVal(tempValueRef.current);\n    }, [mode, updateInnerVal]);\n\n    useImperativeHandle(\n      ref,\n      () => {\n        return {\n          setValue: updateInnerVal,\n          setEmptyValue: () => updateInnerVal({}),\n        };\n      },\n      [updateInnerVal]\n    );\n\n    const items: CollapseProps['items'] = useMemo(() => {\n      return [\n        {\n          key: 'dimension',\n          label: 'Dimension',\n          children: (\n            <DimensionInput\n              ref={dimensionRef}\n              initialValue={initialVal as any}\n              value={value as any}\n              onChange={onStyleItemChange}\n            />\n          ),\n        },\n        {\n          key: 'margin',\n          label: 'Margin',\n          children: (\n            <MarginAndPaddingInput\n              prefix=\"margin\"\n              ref={marinRef}\n              initialValue={initialVal as any}\n              value={value as any}\n              onChange={onStyleItemChange}\n            />\n          ),\n        },\n        {\n          key: 'padding',\n          label: 'Padding',\n          children: (\n            <MarginAndPaddingInput\n              ref={paddingRef}\n              initialValue={initialVal as any}\n              prefix=\"padding\"\n              value={value as any}\n              onChange={onStyleItemChange}\n            />\n          ),\n        },\n\n        {\n          key: 'background',\n          label: 'Background',\n          children: (\n            <BackgroundInput\n              ref={backgroundRef}\n              value={value as any}\n              initialValue={initialVal as any}\n              onChange={onStyleItemChange}\n            />\n          ),\n        },\n        {\n          key: 'font',\n          label: 'Font',\n          children: (\n            <FontInput\n              ref={fontRef}\n              initialValue={initialVal as any}\n              value={value as any}\n              onChange={onStyleItemChange}\n            />\n          ),\n        },\n        {\n          key: 'border',\n          label: 'Border',\n          children: <BorderInput ref={borderRef} initialValue={initialVal as any} onChange={onStyleItemChange} />,\n        },\n        {\n          key: 'shadow',\n          label: 'Shadow',\n          children: (\n            <ShadowInput\n              ref={shadowRef}\n              initialValue={initialVal as any}\n              value={value as any}\n              onChange={onStyleItemChange}\n            />\n          ),\n        },\n      ];\n    }, [initialVal, onStyleItemChange, value]);\n\n    const coreEditView = (\n      <>\n        {mode === 'CODE' && (\n          <CSSCodeEditor\n            onValueChange={(newVal) => {\n              tempValueRef.current = newVal;\n              onValueChange?.(newVal);\n            }}\n            ref={cssCodeEditorRef}\n          />\n        )}\n\n        {mode !== 'CODE' && (\n          <Collapse\n            className={styles.styleUIPanel}\n            items={items}\n            bordered={false}\n            defaultActiveKey={['dimension', 'background', 'padding', 'margin']}\n            onChange={async () => {\n              await waitReactUpdate();\n              // 每次展开需要重新同步值\n              updateInnerVal(tempValueRef.current);\n            }}\n          />\n        )}\n      </>\n    );\n\n    return (\n      <ConfigProvider\n        theme={{\n          token: {\n            borderRadius: 2,\n          },\n        }}\n      >\n        {noCard && coreEditView}\n        {!noCard && (\n          <Card\n            size=\"small\"\n            type=\"inner\"\n            title={<span style={{ fontSize: '12px' }}>Style</span>}\n            extra={\n              <Button\n                type=\"text\"\n                size=\"small\"\n                onClick={() => {\n                  setMode((oldVal) => {\n                    if (oldVal === 'CODE') {\n                      return 'VISUAL';\n                    } else {\n                      return 'CODE';\n                    }\n                  });\n                }}\n              >\n                {mode === 'VISUAL' && <SquareCode size={16} />}\n                {mode !== 'VISUAL' && <Component size={16} />}\n              </Button>\n            }\n            style={{\n              marginBottom: '10px',\n              borderRadius: '8px',\n            }}\n          >\n            {coreEditView}\n          </Card>\n        )}\n      </ConfigProvider>\n    );\n  }\n);\n"
  },
  {
    "path": "packages/engine/src/component/StylePanel/style.module.scss",
    "content": ".label {\n  width: 50px;\n  display: inline-block;\n  color: $fontColor;\n  font-size: 14px;\n  flex-shrink: 0;\n  &.font {\n    width: 50px;\n  }\n\n  &.m-p {\n    width: 30px;\n  }\n}\n\n.fontOption {\n  display: inline-block;\n  width: 50px;\n}\n\n.row {\n  padding-bottom: 10px;\n}\n\n.styleUIPanel {\n  :global {\n    .ant-radio-button-wrapper {\n      font-size: 12px !important;\n    }\n  }\n}\n\n.inputWidth {\n  width: 95px;\n}\n"
  },
  {
    "path": "packages/engine/src/component/StylePanel/type.ts",
    "content": "export type InputCommonRef = {\n  setValue: (newVal: Record<string, string>) => void;\n  setEmptyValue: () => void;\n};\n"
  },
  {
    "path": "packages/engine/src/component/Workbench/index.tsx",
    "content": "import React from 'react';\nimport { Resizable, ResizeCallback } from 're-resizable';\nimport styles from './style.module.scss';\nimport { Button } from 'antd';\nimport { CloseOutlined, DoubleRightOutlined, PushpinOutlined } from '@ant-design/icons';\nimport clsx from 'clsx';\nimport mitt, { Emitter } from 'mitt';\nimport { waitReactUpdate } from '../../utils';\nimport { createPortal } from 'react-dom';\n\nexport interface PluginContext {\n  openPanel: () => void;\n  closePanel: () => void;\n  getPlugin: (pluginName: string) => any;\n  emitter: Emitter<any>;\n}\n\ntype PanelItem = {\n  name: string;\n  title: string | React.ReactNode;\n  icon: React.ReactNode;\n  render: React.ReactNode;\n};\n\nexport type WorkbenchCustomView = {\n  key: string;\n  view: React.ReactNode;\n};\n\nexport type TWidgetVisible = {\n  hiddenTopBar?: boolean;\n  hiddenLeftPanel?: boolean;\n  hiddenRightPanel?: boolean;\n  /** canvas 区域不添加任何 padding margining */\n  canvasFull?: boolean;\n};\n\ntype WorkbenchStateType = {\n  leftBoxVisible: boolean;\n  leftBoxSize: {\n    width: number;\n    height: number | string;\n  };\n  leftBoxFixed: boolean;\n  rightBoxSize: {\n    width: number;\n    height: number | string;\n  };\n  rightBoxVisible: boolean;\n  currentActiveLeftPanel: string;\n  leftPanels: PanelItem[];\n  bodyView: React.ReactNode | null;\n  rightView: React.ReactNode | null;\n  topToolBarView: React.ReactNode | null;\n  subTopToolBarView: React.ReactNode | null;\n  customViewList: WorkbenchCustomView[];\n} & TWidgetVisible;\n\nexport type WorkbenchPropsType = {\n  emitter: Emitter<any>;\n} & TWidgetVisible;\n\nexport class Workbench extends React.Component<WorkbenchPropsType, WorkbenchStateType> {\n  emitter: Emitter<any>;\n  leftPanelContentRef: React.RefObject<HTMLDivElement>;\n  constructor(props: WorkbenchPropsType) {\n    super(props);\n    this.emitter = props.emitter || mitt();\n    this.leftPanelContentRef = React.createRef<HTMLDivElement>();\n    this.state = {\n      leftBoxVisible: true,\n      leftBoxSize: {\n        width: 300,\n        height: '100%',\n      },\n      leftBoxFixed: true,\n      rightBoxVisible: true,\n      rightBoxSize: {\n        width: 350,\n        height: '100%',\n      },\n      currentActiveLeftPanel: 'ComponentLib',\n      leftPanels: [],\n      bodyView: null,\n      rightView: null,\n      topToolBarView: null,\n      subTopToolBarView: null,\n      customViewList: [],\n      hiddenTopBar: props.hiddenTopBar ?? false,\n      hiddenLeftPanel: props.hiddenLeftPanel ?? false,\n      hiddenRightPanel: props.hiddenRightPanel ?? false,\n      canvasFull: props.canvasFull ?? false,\n    };\n  }\n\n  getHiddenWidgetConfig() {\n    return {\n      hiddenTopBar: this.state.hiddenTopBar,\n      hiddenLeftPanel: this.state.hiddenLeftPanel,\n      hiddenRightPanel: this.state.hiddenRightPanel,\n    };\n  }\n\n  /** 隐藏编辑器的小部件 */\n  hiddenWidget(config: Partial<TWidgetVisible>) {\n    this.setState({\n      ...(config || {}),\n    });\n  }\n\n  addLeftPanel = (panel: PanelItem) => {\n    this.state.leftPanels.push(panel);\n    this.setState({\n      leftPanels: [...this.state.leftPanels],\n    });\n  };\n\n  /** 替换对应的 panel */\n  replaceLeftPanel = (panelName: string, newPanel: PanelItem) => {\n    const targetIndex = this.state.leftPanels.findIndex((el) => el.name === panelName);\n    if (targetIndex > -1) {\n      const newPanels = [...this.state.leftPanels];\n      newPanels[targetIndex] = newPanel;\n      this.setState({\n        leftPanels: newPanels,\n      });\n    }\n  };\n\n  /**\n   * 添加属于插件的自定义视图\n   * @param view\n   * @returns dispose: 调用后移除 view\n   */\n  addCustomView(view: WorkbenchCustomView) {\n    const newViewList = this.state.customViewList;\n    newViewList.push(view);\n\n    this.setState({\n      customViewList: [...newViewList],\n    });\n\n    return () => {\n      const newViewList = this.state.customViewList.filter((el) => el !== view);\n      this.setState({\n        customViewList: [...newViewList],\n      });\n    };\n  }\n\n  openLeftPanel = async (currentActiveLeftPanel?: string) => {\n    const newActive = currentActiveLeftPanel || this.state.currentActiveLeftPanel;\n    this.setState({\n      leftBoxVisible: true,\n      leftBoxSize: {\n        width: 350,\n        height: '100%',\n      },\n      currentActiveLeftPanel: newActive,\n    });\n\n    await waitReactUpdate();\n    this.emitter.emit('leftPanelVisible', {\n      visible: true,\n      panelName: newActive,\n    });\n  };\n\n  closeLeftPanel = async () => {\n    this.setState({\n      leftBoxVisible: false,\n      leftBoxSize: {\n        width: 0,\n        height: '100%',\n      },\n    });\n    await waitReactUpdate();\n    this.emitter.emit('leftPanelVisible', {\n      visible: false,\n      panelName: this.state.currentActiveLeftPanel,\n    });\n  };\n\n  toggleLeftPanel = () => {\n    const { leftBoxVisible } = this.state;\n    const newVisible = !leftBoxVisible;\n    if (newVisible) {\n      this.openLeftPanel();\n    } else {\n      this.closeLeftPanel();\n    }\n  };\n\n  onPluginIconClick = (panel: PanelItem) => {\n    const { currentActiveLeftPanel } = this.state;\n    if (currentActiveLeftPanel === panel.name && this.state.leftBoxVisible) {\n      this.closeLeftPanel();\n    } else {\n      this.openLeftPanel(panel.name);\n    }\n  };\n\n  openRightPanel = () => {\n    const { rightBoxSize } = this.state;\n    this.setState({\n      rightBoxVisible: true,\n      rightBoxSize: {\n        width: 400,\n        height: rightBoxSize.height,\n      },\n    });\n  };\n\n  closeRightPanel = () => {\n    const { rightBoxSize } = this.state;\n    this.setState({\n      rightBoxVisible: false,\n      rightBoxSize: {\n        width: 0,\n        height: rightBoxSize.height,\n      },\n    });\n  };\n\n  replaceBodyView = (newView: React.ReactNode) => {\n    this.setState({\n      bodyView: newView,\n    });\n  };\n\n  replaceRightView = (newView: React.ReactNode) => {\n    this.setState({\n      rightView: newView,\n    });\n  };\n\n  replaceTopBarView = (newView: React.ReactNode) => {\n    this.setState({\n      topToolBarView: newView,\n    });\n  };\n\n  replaceSubTopBarView = (newView: React.ReactNode) => {\n    this.setState({\n      subTopToolBarView: newView,\n    });\n  };\n\n  toggleRightPanel = () => {\n    const { rightBoxVisible, rightBoxSize } = this.state;\n    const newVisible = !rightBoxVisible;\n    this.setState({\n      rightBoxVisible: newVisible,\n      rightBoxSize: {\n        width: newVisible ? 400 : 0,\n        height: rightBoxSize.height,\n      },\n    });\n  };\n\n  onLeftBoxResizeStop: ResizeCallback = (_e, _direction, _ref, d) => {\n    const newW = this.state.leftBoxSize.width + d.width;\n    this.setState({\n      leftBoxSize: {\n        width: newW,\n        height: this.state.leftBoxSize.height,\n      },\n    });\n  };\n\n  onGlobalClick = (e: MouseEvent) => {\n    if (e.target && this.leftPanelContentRef.current?.contains(e.target as any)) {\n      return;\n    }\n\n    if (!this.state.leftBoxFixed && this.state.leftBoxVisible) {\n      this.closeLeftPanel();\n    }\n  };\n\n  componentDidMount() {\n    document.addEventListener('click', this.onGlobalClick);\n  }\n\n  render() {\n    const {\n      leftBoxVisible,\n      leftBoxSize,\n      leftBoxFixed,\n      rightBoxSize,\n      rightBoxVisible,\n      leftPanels,\n      currentActiveLeftPanel,\n      bodyView,\n      rightView,\n      topToolBarView,\n      subTopToolBarView,\n      customViewList,\n      hiddenTopBar,\n      hiddenLeftPanel,\n      hiddenRightPanel,\n    } = this.state;\n    const leftBoContentStyle: React.CSSProperties = {};\n    if (!leftBoxFixed) {\n      leftBoContentStyle.position = 'absolute';\n      leftBoContentStyle.left = '50px';\n      leftBoContentStyle.top = 0;\n    }\n\n    const currentActivePluginObj = leftPanels.find((el) => el.name === currentActiveLeftPanel);\n\n    const CurrentPluginRenderView = currentActivePluginObj?.render || null;\n    const { onPluginIconClick, toggleRightPanel, toggleLeftPanel, onLeftBoxResizeStop } = this;\n    return (\n      <div className={styles.workbenchContainer}>\n        {!hiddenTopBar && (\n          <div className={styles.topToolBarBox}>\n            <div className={styles.topToolBarView}>{topToolBarView}</div>\n          </div>\n        )}\n\n        <div className={styles.bodyContent}>\n          {!hiddenLeftPanel && (\n            <div\n              className={clsx([styles.leftBox, hiddenLeftPanel ?? styles.hiddenLeftPanel])}\n              ref={this.leftPanelContentRef}\n            >\n              <div className={styles.pluginIconBar}>\n                {leftPanels.map((pl) => {\n                  return (\n                    <div\n                      key={pl.name}\n                      onClick={() => onPluginIconClick(pl)}\n                      className={clsx([\n                        styles.pluginIconItem,\n                        currentActivePluginObj?.name === pl.name && styles.active,\n                      ])}\n                    >\n                      {pl.icon}\n                    </div>\n                  );\n                })}\n              </div>\n              {leftBoxVisible && (\n                <Resizable\n                  className={styles.pluginPanelBoxResizeBox}\n                  size={leftBoxSize}\n                  style={leftBoContentStyle}\n                  minWidth={300}\n                  maxWidth={600}\n                  enable={{\n                    right: leftBoxVisible,\n                  }}\n                  onResizeStop={onLeftBoxResizeStop}\n                >\n                  <div className={styles.pluginHeader}>\n                    <span className={styles.pluginNameText}>\n                      {currentActivePluginObj?.title || currentActivePluginObj?.name}\n                    </span>\n                    <Button\n                      className={clsx([styles.fixedBtn])}\n                      type=\"text\"\n                      size=\"small\"\n                      onClick={() => {\n                        this.setState({\n                          leftBoxFixed: !this.state.leftBoxFixed,\n                        });\n                      }}\n                    >\n                      <PushpinOutlined className={clsx([leftBoxFixed && styles.active])} />\n                    </Button>\n                    <Button className={styles.closeBtn} type=\"text\" size=\"small\" onClick={toggleLeftPanel}>\n                      <CloseOutlined />\n                    </Button>\n                  </div>\n                  <div className={styles.pluginPanelBox}>{CurrentPluginRenderView}</div>\n                </Resizable>\n              )}\n            </div>\n          )}\n\n          <div className={styles.centerBox}>\n            {subTopToolBarView && <div className={styles.subTopToolbarBox}>{subTopToolBarView}</div>}\n            <div className={clsx([styles.canvasBox, this.state.canvasFull !== true && styles.canvasBoxPadding])}>\n              <div className={styles.scrollBox}>{bodyView}</div>\n            </div>\n          </div>\n\n          <div className={clsx([styles.rightResizeBox, hiddenRightPanel && styles.hiddenRightPanel])}>\n            <div className={styles.arrowCursor} onClick={toggleRightPanel}>\n              <DoubleRightOutlined className={clsx([!rightBoxVisible && styles.active])} />\n            </div>\n            <div\n              style={{\n                display: rightBoxVisible ? 'block' : 'none',\n                height: '100%',\n              }}\n            >\n              <Resizable\n                minWidth={400}\n                maxWidth={600}\n                enable={{\n                  left: rightBoxVisible,\n                }}\n                size={rightBoxSize}\n                onResizeStop={(_e, _direction, _ref, d) => {\n                  this.setState({\n                    rightBoxSize: {\n                      width: this.state.rightBoxSize.width + d.width,\n                      height: this.state.rightBoxSize.height,\n                    },\n                  });\n                }}\n              >\n                <div className={styles.rightBox}>{rightView}</div>\n              </Resizable>\n            </div>\n          </div>\n        </div>\n        {createPortal(\n          <div className={styles.customViewBox}>\n            {customViewList.map((el) => {\n              return (\n                <div\n                  key={el.key}\n                  style={{\n                    pointerEvents: 'auto',\n                    display: 'inline-block',\n                  }}\n                >\n                  {el.view}\n                </div>\n              );\n            })}\n          </div>,\n          document.body\n        )}\n      </div>\n    );\n  }\n}\n"
  },
  {
    "path": "packages/engine/src/component/Workbench/style.module.scss",
    "content": ".workbenchContainer {\n  width: 100%;\n  height: 100%;\n  display: flex;\n  transition: all 0.2s;\n\n  flex-direction: column;\n\n  .topToolBarBox {\n    height: 64px;\n    width: 100%;\n    border-bottom: 1px solid $borderColor;\n    display: flex;\n\n    .topToolBarView {\n      flex: 1;\n    }\n  }\n\n  .bodyContent {\n    display: flex;\n    width: 100%;\n    flex: 1;\n    overflow: hidden;\n\n    .leftBox {\n      position: relative;\n      display: flex;\n      background-color: #fff;\n      transition: all 0.2s;\n      z-index: 100;\n      .pluginIconBar {\n        width: 50px;\n        height: 100%;\n        border-right: 1px solid $borderColor;\n\n        .pluginIconItem {\n          position: relative;\n          cursor: pointer;\n          width: 50px;\n          height: 50px;\n          display: flex;\n          align-items: center;\n          justify-content: center;\n          transition: all 0.1s;\n\n          &.active ::before,\n          &:hover ::before {\n            transition: all 0.1s;\n            display: block;\n            content: '';\n            width: 2px;\n            height: 100%;\n            position: absolute;\n            left: 0;\n            top: 0;\n          }\n\n          &.active ::before {\n            border-left: 4px solid $baseColor !important;\n          }\n\n          &:hover ::before {\n            border-left: 4px solid $hoverColor;\n          }\n        }\n      }\n\n      .pluginPanelBoxResizeBox {\n        display: flex;\n        flex-direction: column;\n        background-color: #fff;\n        z-index: 100;\n        border-right: 1px solid $borderColor;\n\n        .pluginHeader {\n          display: flex;\n          color: $fontColor;\n          height: 30px;\n          margin: 10px;\n          overflow: hidden;\n          align-items: center;\n          .pluginNameText {\n            font-size: 16px;\n            font-weight: 700;\n            margin-right: auto;\n            flex-wrap: nowrap;\n            word-break: keep-all;\n          }\n          .closeBtn,\n          .fixedBtn {\n            color: $fontColor;\n          }\n\n          .fixedBtn {\n            > * {\n              transition: all 0.1s;\n            }\n            > .active {\n              transform: rotate(-45deg);\n            }\n          }\n        }\n      }\n\n      .pluginPanelBox {\n        transition: all 0.2s;\n        width: 100%;\n        flex: 1;\n        background-color: ghostwhite;\n        overflow: hidden;\n      }\n    }\n\n    .hiddenLeftPanel {\n      width: 0px;\n    }\n\n    .rightResizeBox {\n      transition: all 0.2s;\n      position: relative;\n      .arrowCursor {\n        cursor: pointer;\n        position: absolute;\n        left: -18px;\n        top: 50%;\n        transform: translateY(-50%);\n        z-index: 2;\n        background-color: #fff;\n        padding: 10px 3px;\n        border-radius: 4px 0 0 4px;\n        font-size: 12px;\n        > .active {\n          transform: rotate(-180deg);\n        }\n      }\n      .rightBox {\n        transition: all 0.2s;\n        position: relative;\n        width: 100%;\n        height: 100%;\n        border-left: 1px $borderColor solid;\n      }\n    }\n    .hiddenRightPanel {\n      transition: all 0.2s;\n      width: 0px;\n    }\n\n    .centerBox {\n      transition: all 0.2s;\n      display: flex;\n      flex-direction: column;\n      flex: 1;\n      overflow: hidden;\n      .subTopToolbarBox {\n        width: 100%;\n        height: 50px;\n        background-color: #fff;\n      }\n      .canvasBox {\n        width: 100%;\n        flex: 1;\n        overflow: hidden;\n        transition: all 0.2s;\n        background-color: $baseBackgroundColor;\n        .scrollBox {\n          width: 100%;\n          height: 100%;\n        }\n      }\n\n      .canvasBoxPadding {\n        padding: 20px 30px;\n      }\n    }\n  }\n}\n\n.customViewBox {\n  position: fixed;\n  width: 100%;\n  height: 100%;\n  top: 0;\n  left: 0;\n  pointer-events: none;\n}\n"
  },
  {
    "path": "packages/engine/src/component/index.tsx",
    "content": "/* eslint-disable react-refresh/only-export-components */\nexport * from './CSSEditor';\nexport * from './CSSPropertiesEditor';\nexport * from './CSSPropertiesVariableBindEditor';\nexport * from './ClassNameEditor';\nexport * from './CustomSchemaForm';\nexport * from './MonacoEditor';\nexport * from './Workbench';\nexport * from './DesignerSizer';\n"
  },
  {
    "path": "packages/engine/src/config/colorPickerColorList.ts",
    "content": "export const DEFAULT_PRESET_COLORS = [\n  {\n    label: 'Recommended',\n    colors: [\n      '#000000',\n      '#000000E0',\n      '#000000A6',\n      '#00000073',\n      '#00000040',\n      '#00000026',\n      '#0000001A',\n      '#00000012',\n      '#0000000A',\n      '#00000005',\n      '#F5222D',\n      '#FA8C16',\n      '#FADB14',\n      '#8BBB11',\n      '#52C41A',\n      '#13A8A8',\n      '#1677FF',\n      '#2F54EB',\n      '#722ED1',\n      '#EB2F96',\n      '#F5222D4D',\n      '#FA8C164D',\n      '#FADB144D',\n      '#8BBB114D',\n      '#52C41A4D',\n      '#13A8A84D',\n      '#1677FF4D',\n      '#2F54EB4D',\n      '#722ED14D',\n      '#EB2F964D',\n    ],\n  },\n];\n"
  },
  {
    "path": "packages/engine/src/core/assetPackagesListManage.ts",
    "content": "import { AssetPackage } from '@chamn/model';\n\nexport class AssetsPackageListManager {\n  private _assets: AssetPackage[];\n  constructor(assets: AssetPackage[]) {\n    this._assets = assets;\n  }\n\n  getList() {\n    return this._assets;\n  }\n\n  setList(newAssets: AssetPackage[]) {\n    const tempList = [...newAssets, ...this._assets];\n    const newList = getUniqueAssetsList(tempList);\n    // 去重复\n    this._assets = newList;\n  }\n}\n\nexport const getUniqueAssetsList = (tempList: AssetPackage[]) => {\n  const newList: AssetPackage[] = [];\n  const idMap: Record<string, any> = {};\n  tempList.forEach((el) => {\n    if (!idMap[el.package]) {\n      idMap[el.package] = el;\n      newList.push(el);\n    }\n  });\n\n  return newList;\n};\n"
  },
  {
    "path": "packages/engine/src/core/pluginManager.ts",
    "content": "import { Engine } from '@/index';\nimport { CPage } from '@chamn/model';\nimport mitt, { Emitter } from 'mitt';\nimport { Workbench } from '../component/Workbench';\nimport { CustomI18n } from '../i18n';\nimport { AssetsPackageListManager } from './assetPackagesListManage';\n\nexport type PluginObj<C, E = any> = {\n  name: string;\n  PLUGIN_NAME?: string;\n  init: (ctx: CPluginCtx<C>) => Promise<void>;\n  destroy: (ctx: CPluginCtx<C>) => Promise<void>;\n  /** 用于暴露给外部重载插件 */\n  reload?: (ctx: CPluginCtx<C>) => Promise<void>;\n  /** 插件暴露给外部可以调用的方法 */\n  export: (ctx: CPluginCtx<C>) => E;\n  meta: {\n    engine: {\n      version: string;\n    };\n  };\n};\n\ninterface PluginFunction<C, E> {\n  (ctx: CPluginCtx<C>): PluginObj<C, E>;\n  ['PLUGIN_NAME']?: string;\n}\n\nexport type CPlugin<C = Record<string, any>, E = any> = PluginObj<C, E> | PluginFunction<C, E>;\n\ntype PluginManagerOptions = {\n  getWorkbench: () => Workbench;\n  emitter: Emitter<any>;\n  pageModel: CPage;\n  i18n: CustomI18n;\n  assetsPackageListManager: AssetsPackageListManager;\n  engine: Engine;\n};\n\nexport type CPluginCtx<C = any> = {\n  name?: string;\n  globalEmitter: Emitter<any>;\n  /** 当前插件外部传入的配置 **/\n  config: C;\n  pluginManager: PluginManager;\n  pluginReadyOk: () => void;\n} & PluginManagerOptions;\n\nexport type PluginInstance<C = any, E = any> = {\n  ctx: CPluginCtx<C>;\n  export: E;\n  source: PluginObj<C, E>;\n  ready: boolean;\n};\n\nexport type CustomPluginHook<P extends PluginInstance<any, any> = any> = (pluginInstance: P) => P;\n\nexport class PluginManager {\n  plugins: Map<string, PluginInstance> = new Map();\n  emitter: Emitter<any> = mitt();\n  getWorkbench!: () => Workbench;\n  pageModel!: CPage;\n  i18n: CustomI18n;\n  assetsPackageListManager: AssetsPackageListManager;\n  engine: Engine;\n  customPluginHooks: Record<string, CustomPluginHook[]> = {};\n\n  constructor({ getWorkbench, emitter, pageModel, i18n, assetsPackageListManager, engine }: PluginManagerOptions) {\n    this.getWorkbench = getWorkbench;\n    this.emitter = emitter;\n    this.pageModel = pageModel;\n    this.i18n = i18n;\n    this.assetsPackageListManager = assetsPackageListManager;\n    this.engine = engine;\n  }\n\n  /** 自定义插件, 可以修改插件的配置 */\n  customPlugin = <P extends PluginInstance<any, any>>(pluginName: string, customPluginHook: CustomPluginHook<P>) => {\n    const customPluginHooks = this.customPluginHooks;\n    const hookList = customPluginHooks[pluginName] || [];\n    hookList.push(customPluginHook);\n    customPluginHooks[pluginName] = hookList;\n    this.customPluginHooks = customPluginHooks;\n  };\n\n  createPluginCtx = () => {\n    const ctx: CPluginCtx = {\n      globalEmitter: this.emitter,\n      emitter: mitt(),\n      config: {},\n      getWorkbench: this.getWorkbench,\n      pluginManager: this,\n      pageModel: this.pageModel,\n      i18n: this.i18n,\n      assetsPackageListManager: this.assetsPackageListManager,\n      engine: this.engine,\n      pluginReadyOk: () => {},\n    };\n    return ctx;\n  };\n\n  async add(plugin: CPlugin) {\n    const ctx = this.createPluginCtx();\n\n    let innerPlugin: PluginObj<any, any>;\n    if (typeof plugin === 'function') {\n      innerPlugin = plugin(ctx);\n    } else {\n      innerPlugin = plugin;\n    }\n    let pluginCtx: PluginInstance = {\n      source: innerPlugin,\n      ctx: ctx,\n      export: innerPlugin.export?.(ctx) || {},\n      ready: false,\n    };\n    const customHookList = this.customPluginHooks[innerPlugin.name] || [];\n    customHookList.forEach((cb) => {\n      pluginCtx = cb(pluginCtx);\n    });\n    ctx.pluginReadyOk = () => {\n      this.emitter.emit(`${innerPlugin.name}:ready`);\n      pluginCtx.ready = true;\n    };\n    this.plugins.set(innerPlugin.name, pluginCtx);\n    await innerPlugin.init(ctx);\n  }\n\n  async get<P extends PluginInstance<any, any>>(pluginName: string): Promise<P | undefined> {\n    const pluginInstance = this.plugins.get(pluginName);\n    if (pluginInstance?.ready) {\n      return pluginInstance as any;\n    } else {\n      await this.onPluginReadyOk(pluginName);\n      return pluginInstance as any;\n    }\n  }\n\n  async remove(name: string) {\n    const p = this.plugins.get(name);\n    await p?.source.destroy(p.ctx);\n    this.plugins.delete(name);\n  }\n\n  onPluginReadyOk(pluginName: string, cb?: (pluginHandle: PluginInstance) => void) {\n    return new Promise<PluginInstance>((resolve) => {\n      const pluginObj = this.plugins.get(pluginName);\n      if (pluginObj?.ready) {\n        resolve(pluginObj);\n      } else if (pluginObj === undefined) {\n        console.warn(`plugin: ${pluginName} not found.`);\n      }\n      this.emitter.on(`${pluginName}:ready`, () => {\n        const newPluginObj = this.plugins.get(pluginName);\n        if (newPluginObj) {\n          newPluginObj.ready = true;\n          cb?.(newPluginObj);\n          resolve(newPluginObj);\n        }\n      });\n    });\n  }\n}\n"
  },
  {
    "path": "packages/engine/src/i18n/en_US/index.ts",
    "content": "export const en_US = {\n  translation: {\n    'Welcome to React': 'Welcome to React and react-i18next',\n  },\n};\n"
  },
  {
    "path": "packages/engine/src/i18n/index.ts",
    "content": "import i18n from 'i18next';\nimport { initReactI18next } from 'react-i18next';\nimport { zh_CN } from './zh_CN';\nimport { en_US } from './en_US';\n\n// the translations\n// (tip move them in a JSON file and import them,\n// or even better, manage them separated from your code: https://react.i18next.com/guides/multiple-translation-files)\nconst resources = {\n  zh_CN,\n  en_US,\n};\n\ni18n\n  .use(initReactI18next) // passes i18n down to react-i18next\n  .init({\n    resources,\n    lng: 'zh_CN', // language to use, more information here: https://www.i18next.com/overview/configuration-options#languages-namespaces-resources\n    // you can use the i18n.changeLanguage function to change the language manually: https://www.i18next.com/overview/api#changelanguage\n    // if you're using a language detector, do not define the lng option\n    react: {\n      bindI18n: 'added languageChanged',\n      bindI18nStore: 'added',\n    },\n  });\n\nexport type CustomI18n = typeof i18n & {\n  update: () => void;\n};\n\nconst customI18n: CustomI18n = i18n as any;\n\ncustomI18n.update = () => {\n  setTimeout(() => {\n    i18n.emit('added');\n  }, 0);\n};\n\ncustomI18n.changeLanguage('en_US');\n\nexport default customI18n;\n"
  },
  {
    "path": "packages/engine/src/i18n/zh_CN/index.ts",
    "content": "export const zh_CN = {\n  translation: {\n    'Welcome to React': '欢迎使用 React',\n  },\n};\n"
  },
  {
    "path": "packages/engine/src/index.tsx",
    "content": "/* eslint-disable react-refresh/only-export-components */\nimport React from 'react';\nimport { TWidgetVisible, Workbench } from './component/Workbench';\nimport { loader } from '@monaco-editor/react';\n\nimport styles from './Engine.module.scss';\nimport i18n from './i18n/index';\nimport { PluginManager } from './core/pluginManager';\nimport mitt, { Emitter } from 'mitt';\nimport { AssetPackage, CMaterialType, CNode, CPage, CPageDataType, CRootNode, EmptyPage } from '@chamn/model';\nimport { getDefaultRender, beforeInitRender } from './utils/defaultEngineConfig';\nimport { DesignerPluginInstance } from './plugins/Designer/type';\nimport clsx from 'clsx';\nimport { AssetLoader, ComponentsType, collectVariable } from '@chamn/render';\nimport { AssetsPackageListManager } from './core/assetPackagesListManage';\nimport { flatObject } from '@chamn/render';\nimport customI18n from './i18n/index';\nimport { EngineProps } from './type';\n\nexport class Engine extends React.Component<EngineProps> {\n  static version = __PACKAGE_VERSION__;\n  currentSelectNode: CNode | CRootNode | null;\n\n  pluginManager!: PluginManager;\n  workbenchRef = React.createRef<Workbench>();\n  pageSchema: CPageDataType | undefined;\n  pageModel: CPage;\n  material: CMaterialType[] | undefined;\n  emitter: Emitter<any>;\n  assetsPackageListManager: AssetsPackageListManager;\n\n  workbenchConfig?: EngineProps['workbenchConfig'] = {};\n  _oldHiddenWidgetConfig:\n    | { hiddenTopBar: boolean | undefined; hiddenLeftPanel: boolean | undefined; hiddenRightPanel: boolean | undefined }\n    | undefined;\n\n  constructor(props: EngineProps) {\n    super(props);\n    this.pageSchema = props.schema;\n    this.material = props.material;\n    this.currentSelectNode = null;\n    (window as any).__CHAMELEON_ENG__ = this;\n    this.assetsPackageListManager = new AssetsPackageListManager(props.assetPackagesList || []);\n    this.workbenchConfig = props.workbenchConfig || {};\n\n    if (props.monacoEditor?.cndUrl) {\n      loader.config({\n        paths: {\n          vs: props.monacoEditor?.cndUrl,\n        },\n      });\n    }\n\n    try {\n      this.pageModel = new CPage(this.pageSchema, {\n        materials: this.material || [],\n        assetPackagesList: props.assetPackagesList || [],\n      });\n    } catch (e) {\n      console.error(e);\n      this.pageModel = new CPage(EmptyPage);\n    }\n    this.emitter = mitt();\n  }\n\n  updateCurrentSelectNode(node: CNode | CRootNode | null) {\n    this.currentSelectNode = node;\n    this.emitter.emit('onSelectNodeChange', {\n      node,\n    });\n  }\n\n  async componentDidMount() {\n    (window as any).__C_ENGINE__ = this;\n    const plugins = this.props.plugins;\n    const pluginManager = new PluginManager({\n      engine: this,\n      getWorkbench: () => this.workbenchRef.current!,\n      emitter: this.emitter,\n      pageModel: this.pageModel,\n      i18n,\n      assetsPackageListManager: this.assetsPackageListManager,\n    });\n    this.pluginManager = pluginManager;\n    // 使用默认的渲染策略\n    pluginManager.customPlugin<DesignerPluginInstance>('Designer', (pluginInstance) => {\n      pluginInstance.ctx.config.beforeInitRender = beforeInitRender;\n      pluginInstance.ctx.config.customRender = getDefaultRender({\n        components: this.props.components || {},\n        renderProps: this.props.renderProps || {},\n      });\n      pluginInstance.ctx.config.components = this.props.components;\n      return pluginInstance;\n    });\n    this.props.beforePluginRun?.({\n      pluginManager: this.pluginManager,\n    });\n\n    const pList = plugins.map((p) => {\n      return this.pluginManager.add(p);\n    });\n\n    this.props.onMount?.({\n      pluginManager: this.pluginManager,\n      engine: this,\n    });\n\n    await Promise.all(pList);\n\n    this.pageModel.emitter.on('onReloadPage', () => {\n      if (!this.currentSelectNode) {\n        return;\n      }\n      const newSelectNode = this.pageModel.getNode(this.currentSelectNode?.id);\n      if (newSelectNode) {\n        this.updateCurrentSelectNode(newSelectNode);\n      }\n    });\n\n    this.props.onReady?.({\n      pluginManager: this.pluginManager,\n      engine: this,\n    });\n  }\n\n  getActiveNode() {\n    if (!this.currentSelectNode?.id) {\n      return null;\n    }\n    const node = this.pageModel.getNode(this.currentSelectNode.id) ?? null;\n    this.currentSelectNode = node;\n    return node;\n  }\n\n  updatePage = (page: CPageDataType) => {\n    this.pageModel.updatePage(page);\n  };\n\n  updateMaterials = async (\n    materials: CMaterialType[],\n    assetPackagesList: AssetPackage[],\n    options?: {\n      formatComponents?: (componentMap: ComponentsType) => ComponentsType;\n    }\n  ) => {\n    const designerPlugin = await this.pluginManager.get<DesignerPluginInstance>('Designer');\n    const designerPluginExport = designerPlugin?.export;\n\n    this.pluginManager.assetsPackageListManager.setList(assetPackagesList);\n    this.pageModel.assetPackagesList = this.pluginManager.assetsPackageListManager.getList();\n\n    const subWin = designerPluginExport?.getDesignerWindow();\n\n    if (!subWin) {\n      console.warn('subWin not exits');\n      return;\n    }\n\n    // 从子窗口获取物料对象\n    const assetLoader = new AssetLoader(assetPackagesList, subWin);\n    await assetLoader.load();\n    const componentCollection = collectVariable(assetPackagesList, subWin);\n    // 排平\n    let newComponents = flatObject(componentCollection);\n    if (options?.formatComponents) {\n      newComponents = options.formatComponents(newComponents);\n    }\n    // 更新 render 中的组件库\n    designerPluginExport?.updateRenderComponents(newComponents);\n    this.pageModel.materialsModel.addMaterials(materials);\n    this.emitter.emit('updateMaterials');\n  };\n\n  refresh = async () => {\n    this.pageModel.reloadPage(this.pageModel.export('design'));\n  };\n\n  getWorkbench = () => {\n    return this.workbenchRef.current;\n  };\n\n  /** return i18n object */\n  getI18n() {\n    return customI18n;\n  }\n\n  /** 进入预览模式 */\n  async preview() {\n    const oldHiddenWidgetConfig = this.workbenchRef.current?.getHiddenWidgetConfig();\n    this._oldHiddenWidgetConfig = oldHiddenWidgetConfig;\n    this.workbenchRef.current?.hiddenWidget({\n      hiddenLeftPanel: true,\n      hiddenRightPanel: true,\n      hiddenTopBar: true,\n      canvasFull: true,\n    });\n\n    const designerPlugin = await this.pluginManager.get<DesignerPluginInstance>('Designer');\n    const designerPluginExport = designerPlugin?.export;\n    designerPluginExport?.setPreviewMode();\n  }\n\n  async existPreview() {\n    this.workbenchRef.current?.hiddenWidget({\n      ...(this._oldHiddenWidgetConfig || {}),\n      canvasFull: false,\n    });\n    const designerPlugin = await this.pluginManager.get<DesignerPluginInstance>('Designer');\n    const designerPluginExport = designerPlugin?.export;\n    designerPluginExport?.setEditMode();\n  }\n\n  hiddenWidget(config: Partial<TWidgetVisible>) {\n    this.workbenchRef.current?.hiddenWidget(config || {});\n  }\n\n  render() {\n    return (\n      <div className={clsx([styles.engineContainer, this.props.className])} style={this.props.style}>\n        <Workbench ref={this.workbenchRef} emitter={this.emitter} {...this.workbenchConfig} />\n      </div>\n    );\n  }\n}\n\nexport * as plugins from './plugins';\nexport * from './plugins';\nexport * from './component';\nexport * from '@chamn/layout';\n\nexport * from './material/innerMaterial';\nexport * from './component/CustomSchemaForm/components/Setters/type';\nexport * from './utils/index';\n\n/** 注册自定义 setter */\nexport { registerCustomSetter } from './component/CustomSchemaForm/components/Form';\n\nexport * from './type';\nexport * from './core/pluginManager';\nexport * from './core/assetPackagesListManage';\n"
  },
  {
    "path": "packages/engine/src/material/container.meta.ts",
    "content": "import { CMaterialPropsType } from '@chamn/model';\n\nexport const containerProps: CMaterialPropsType<'FastLayoutSetter'> = [\n  {\n    name: 'style',\n    title: '布局',\n    valueType: 'string',\n    setters: [\n      {\n        componentName: 'FastLayoutSetter',\n        initialValue: {},\n        hiddenLabel: true,\n        props: {},\n      },\n    ],\n  },\n];\n"
  },
  {
    "path": "packages/engine/src/material/innerMaterial.tsx",
    "content": "import { CMaterialPropsType, CMaterialType, DEFAULT_EVENT_LIST, HTMl_TAGS } from '@chamn/model';\nimport { capitalize } from 'lodash-es';\nimport { containerProps } from './container.meta';\n\nconst customAttributesMeta: CMaterialPropsType[number] = {\n  name: '$$attributes',\n  title: '属性',\n  valueType: 'object',\n  setters: [\n    {\n      componentName: 'ArraySetter',\n      props: {\n        collapse: {\n          open: true,\n        },\n        sortLabelKey: 'key',\n        item: {\n          setters: [\n            {\n              componentName: 'ShapeSetter',\n              props: {\n                elements: [\n                  {\n                    name: 'key',\n                    title: '属性名',\n                    valueType: 'string',\n                    setters: ['StringSetter'],\n                  },\n                  {\n                    name: 'value',\n                    title: '值',\n                    valueType: 'string',\n                    setters: ['StringSetter', 'NumberSetter', 'JSONSetter', 'FunctionSetter', 'ExpressionSetter'],\n                  },\n                ],\n              },\n              initialValue: {},\n            },\n          ],\n          initialValue: {},\n        },\n      },\n      initialValue: [],\n    },\n  ],\n};\n\nconst groupName = '内置组件';\nconst categoryName = '原子组件';\n\nconst INNER_META_VERSION = '1.0.0';\nconst PKG_NAME = 'CHAMELEON_INNER_PKG';\n\nconst htmlNativeComponentMeta = HTMl_TAGS.map((tag) => {\n  const DivMeta: CMaterialType = {\n    title: capitalize(tag),\n    componentName: tag,\n    props: [customAttributesMeta],\n    snippets: [],\n    npm: {\n      name: tag,\n      package: PKG_NAME,\n      version: INNER_META_VERSION,\n    },\n  };\n\n  return DivMeta;\n});\n\nconst BaseComponentMeta: CMaterialType[] = [\n  {\n    title: '块',\n    componentName: 'CBlock',\n    props: [\n      {\n        name: 'children',\n        title: '文本',\n        valueType: 'string',\n        setters: ['StringSetter', 'ExpressionSetter'],\n      },\n      customAttributesMeta,\n    ],\n    groupName: groupName,\n    snippets: [\n      {\n        title: '块',\n        snapshotText: 'Block',\n        category: categoryName,\n        schema: {\n          props: {},\n          css: {\n            value: [\n              {\n                state: 'normal',\n                media: [],\n                text: 'background: white ; width: 100%; height: 100px',\n              },\n            ],\n          },\n        },\n      },\n    ],\n  },\n  {\n    title: '容器',\n    componentName: 'CContainer',\n    isContainer: true,\n    props: [...(containerProps as any), customAttributesMeta],\n    groupName: groupName,\n    snippets: [\n      {\n        title: '容器',\n        snapshotText: 'Con',\n        category: categoryName,\n        schema: {\n          css: {\n            value: [\n              {\n                state: 'normal',\n                media: [],\n                text: 'background: white;width: 100%;',\n              },\n            ],\n          },\n        },\n      },\n    ],\n  },\n  {\n    title: '图片',\n    componentName: 'CImage',\n    props: [\n      {\n        name: 'src',\n        title: '地址',\n        valueType: 'string',\n        setters: ['StringSetter', 'ExpressionSetter'],\n      },\n      customAttributesMeta,\n    ],\n    advanceCustom: {\n      wrapComponent: (comp) => {\n        return (props: any) => {\n          const Comp = comp;\n          return <Comp {...props} style={{ ...(props.style || {}), userSelect: 'none', WebkitUserDrag: 'none' }} />;\n        };\n      },\n    },\n\n    groupName: groupName,\n    snippets: [\n      {\n        title: '图片',\n        snapshotText: 'Img',\n        category: categoryName,\n        schema: {\n          css: {\n            value: [\n              {\n                text: 'background:white;width:500px;height:300px;overflow:auto;',\n                media: [],\n                state: 'normal',\n              },\n            ],\n          },\n          props: {\n            src: 'https://images.unsplash.com/photo-1584080277544-2db5b2c2d9dd?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1470&q=80',\n          },\n        },\n      },\n    ],\n  },\n  {\n    title: '视频',\n    componentName: 'CVideo',\n    props: [\n      {\n        name: 'src',\n        title: '地址',\n        valueType: 'string',\n        setters: ['StringSetter', 'ExpressionSetter'],\n      },\n      {\n        name: 'autoPlay',\n        title: '自动播放',\n        valueType: 'string',\n        setters: ['BooleanSetter', 'ExpressionSetter'],\n      },\n      {\n        name: 'controls',\n        title: '控制面板',\n        valueType: 'string',\n        setters: ['BooleanSetter', 'ExpressionSetter'],\n      },\n      customAttributesMeta,\n    ],\n    fixedProps: {\n      autoPlay: false,\n    },\n    advanceCustom: {\n      wrapComponent: (Comp) => {\n        return (props) => {\n          //  原生的控制面板会阻断页面级别的事件监听，导致拖拽失效，这里在编辑态禁用 video 的控制面板相关事件触发\n          return (\n            <div\n              style={{\n                ...props.style,\n                display: 'inline-flex',\n              }}\n            >\n              <Comp\n                {...props}\n                style={{\n                  ...props.style,\n                  pointerEvents: 'none',\n                }}\n              ></Comp>\n            </div>\n          );\n        };\n      },\n    },\n    groupName: groupName,\n    snippets: [\n      {\n        title: '视频',\n        snapshotText: 'Video',\n        category: categoryName,\n        schema: {\n          props: {\n            src: 'https://vjs.zencdn.net/v/oceans.mp4',\n          },\n          css: {\n            value: [\n              {\n                state: 'normal',\n                media: [],\n                text: 'background:white;width:300px;height:150px;',\n              },\n            ],\n          },\n        },\n      },\n    ],\n  },\n  {\n    title: '音频',\n    groupName: groupName,\n    componentName: 'CAudio',\n    props: [\n      {\n        name: 'src',\n        title: '地址',\n        valueType: 'string',\n        setters: ['StringSetter', 'ExpressionSetter'],\n      },\n      {\n        name: 'autoPlay',\n        title: '自动播放',\n        valueType: 'string',\n        setters: ['BooleanSetter', 'ExpressionSetter'],\n      },\n      {\n        name: 'controls',\n        title: '控制面板',\n        valueType: 'string',\n        setters: ['BooleanSetter', 'ExpressionSetter'],\n      },\n      customAttributesMeta,\n    ],\n    advanceCustom: {\n      wrapComponent: () => {\n        return (props) => {\n          return (\n            <div\n              style={{\n                display: 'inline-block',\n                fontSize: 0,\n              }}\n            >\n              <audio\n                {...props}\n                style={{\n                  pointerEvents: 'none',\n                  ...props.style,\n                }}\n              ></audio>\n            </div>\n          );\n        };\n      },\n    },\n    snippets: [\n      {\n        title: '音频',\n        snapshotText: 'Audio',\n        category: categoryName,\n        schema: {\n          props: {\n            src: 'https://vjs.zencdn.net/v/oceans.mp4',\n            controls: true,\n          },\n        },\n      },\n    ],\n  },\n  {\n    title: '文本',\n    componentName: 'CText',\n    groupName: groupName,\n    props: [\n      {\n        name: 'content',\n        title: '内容',\n        valueType: 'string',\n        setters: ['TextAreaSetter', 'ExpressionSetter'],\n      },\n      customAttributesMeta,\n    ],\n    snippets: [\n      {\n        title: '文本',\n        snapshotText: 'Text',\n        category: categoryName,\n        schema: {\n          props: {\n            content: 'text',\n          },\n        },\n      },\n    ],\n  },\n  {\n    title: 'Canvas',\n    componentName: 'CCanvas',\n    props: [\n      {\n        name: 'afterMount',\n        title: '渲染之后',\n        valueType: 'function',\n        setters: ['FunctionSetter', 'ExpressionSetter', 'ActionFlowSetter' as any],\n      },\n      {\n        name: 'beforeDestroy',\n        title: '销毁之前',\n        valueType: 'function',\n        setters: ['FunctionSetter', 'ExpressionSetter'],\n      },\n      customAttributesMeta,\n    ],\n    groupName: groupName,\n    advanceCustom: {\n      onNewAdd: async (node) => {\n        const props = node.getPlainProps();\n        const id = Math.random().toString(32).slice(3, 9);\n        props.$$attributes = [\n          {\n            key: 'id',\n            value: id,\n          },\n          {\n            key: 'style',\n            value: {\n              display: 'block',\n              margin: '0 auto',\n            },\n          },\n        ];\n        props.afterMount.value = props.afterMount.value.replace('$[id]', id);\n        node.updateWithPlainObj({\n          props,\n        });\n        return {\n          addNode: node,\n        };\n      },\n    },\n    snippets: [\n      {\n        title: '画布',\n        snapshotText: 'Cavs',\n        category: categoryName,\n        schema: {\n          props: {\n            width: '600px',\n            height: '150px',\n            style: {\n              margin: '0 auto',\n            },\n            afterMount: {\n              type: 'FUNCTION',\n              value: `\n              function run () {\n                var ctx = document.getElementById(\"$[id]\").getContext(\"2d\");\n                ctx.font = \"48px serif\";\n                ctx.fillText(\"Hello Canvas\", 10, 50);\n              }\n              `,\n            },\n          },\n        },\n      },\n    ],\n  },\n  {\n    title: 'HTML 标签',\n    componentName: 'CNativeTag',\n    props: [\n      {\n        name: 'htmlTag',\n        title: '标签名',\n        valueType: 'string',\n        setters: [\n          {\n            componentName: 'SelectSetter',\n            props: {\n              options: HTMl_TAGS.map((tag) => {\n                return {\n                  name: tag,\n                  value: tag,\n                };\n              }),\n            },\n          },\n        ],\n      },\n      customAttributesMeta,\n    ],\n    isContainer: true,\n    groupName: groupName,\n    snippets: [\n      {\n        title: 'HTML',\n        snapshotText: 'HTML',\n        category: categoryName,\n        schema: {\n          props: {\n            htmlTag: 'div',\n          },\n        },\n      },\n    ],\n  },\n];\nBaseComponentMeta.forEach((el) => {\n  el.events = DEFAULT_EVENT_LIST;\n});\n\nconst BaseComponentMetaWithVersion = BaseComponentMeta.map((el) => {\n  return {\n    ...el,\n    npm: {\n      name: el.componentName,\n      package: PKG_NAME,\n      version: INNER_META_VERSION,\n    },\n  };\n});\n\nexport const InnerComponentMeta = [...BaseComponentMetaWithVersion, ...htmlNativeComponentMeta];\n"
  },
  {
    "path": "packages/engine/src/plugins/AdvancePanel/index.tsx",
    "content": "/* eslint-disable react-refresh/only-export-components */\nimport { useEffect, useMemo, useRef } from 'react';\nimport { CMaterialPropsType, CNode, CRootNode } from '@chamn/model';\nimport { CustomSchemaForm, CustomSchemaFormInstance, CustomSchemaFormProps } from '../../component/CustomSchemaForm';\nimport { CPluginCtx } from '../../core/pluginManager';\nimport { CRightPanelItem } from '../RightPanel/view';\nimport styles from './style.module.scss';\n\nexport type AdvancePanelProps = {\n  node: CNode | CRootNode | null;\n  pluginCtx: CPluginCtx;\n};\n\nconst properties: CMaterialPropsType = [\n  {\n    name: 'id',\n    title: {\n      label: 'id',\n      tip: 'node unique id',\n    },\n    valueType: 'string',\n    setters: [\n      {\n        componentName: 'StringSetter',\n        props: {\n          disabled: true,\n          variant: 'borderless',\n        },\n      },\n    ],\n  },\n  {\n    name: 'condition',\n    title: {\n      label: 'Render',\n      tip: 'controller component render',\n    },\n    valueType: 'boolean',\n    setters: ['BooleanSetter', 'ExpressionSetter'],\n  },\n  {\n    name: 'loop',\n    title: 'loop render',\n    valueType: 'object',\n    setters: [\n      {\n        componentName: 'ShapeSetter',\n        props: {\n          collapse: {\n            open: true,\n          },\n          elements: [\n            {\n              name: 'open',\n              title: 'open',\n              valueType: 'boolean',\n              setters: ['BooleanSetter', 'ExpressionSetter'],\n            },\n            {\n              name: 'data',\n              title: 'data',\n              valueType: 'array',\n              setters: [\n                {\n                  componentName: 'ArraySetter',\n                  initialValue: [],\n                  props: {\n                    item: {\n                      setters: ['JSONSetter', 'ExpressionSetter'],\n                      initialValue: {},\n                    },\n                  },\n                },\n                'JSONSetter',\n                'ExpressionSetter',\n              ],\n            },\n            {\n              name: 'forName',\n              title: {\n                label: 'name',\n                tip: 'loop element name',\n              },\n              valueType: 'string',\n              setters: ['StringSetter'],\n            },\n            {\n              name: 'forIndex',\n              title: {\n                label: 'index',\n                tip: 'loop element index',\n              },\n              valueType: 'string',\n              setters: ['StringSetter'],\n            },\n            {\n              name: 'key',\n              title: {\n                label: 'key',\n                tip: 'loop element key',\n              },\n              valueType: 'expression',\n              setters: ['ExpressionSetter'],\n            },\n            {\n              name: 'name',\n              title: {\n                label: 'variable \\n name',\n                tip: 'loop variable name',\n              },\n              valueType: 'string',\n              setters: [\n                {\n                  componentName: 'StringSetter',\n                  props: {\n                    prefix: 'loopData',\n                  },\n                },\n              ],\n            },\n          ],\n        },\n        initialValue: {\n          open: false,\n          data: [],\n        },\n      },\n    ],\n  },\n  {\n    name: 'refId',\n    title: {\n      label: 'refId',\n      tip: 'unique node flag',\n    },\n    valueType: 'string',\n    setters: ['StringSetter'],\n  },\n  {\n    name: 'nodeName',\n    title: {\n      label: (\n        <>\n          node <br></br> name\n        </>\n      ) as any,\n      tip: 'alias for node',\n    },\n    valueType: 'string',\n    setters: ['StringSetter'],\n  },\n];\n\nexport const AdvancePanel = (props: AdvancePanelProps) => {\n  const { node } = props;\n\n  const innerProperties = useMemo(() => {\n    let newList = properties;\n\n    // 如果元素是容器不允许 loop, 或者主动指定不然 loop\n    const canLoop =\n      !node?.material?.value.advanceCustom?.rightPanel?.advanceOptions?.loop === false ||\n      !node?.material?.value.isContainer;\n    if (!canLoop) {\n      newList = newList.filter((el) => (el as any)?.name !== 'loop');\n    }\n\n    // 如果元素是容器不允许 canRender, 或者主动指定不然 canRender\n    const canRender = !node?.material?.value.advanceCustom?.rightPanel?.advanceOptions?.render === false;\n\n    if (!canRender) {\n      newList = newList.filter((el) => (el as any)?.name !== 'condition');\n    }\n\n    return newList;\n  }, [\n    node?.material?.value.advanceCustom?.rightPanel?.advanceOptions?.loop,\n    node?.material?.value.advanceCustom?.rightPanel?.advanceOptions?.render,\n    node?.material?.value.isContainer,\n  ]);\n\n  const onSetterChange: CustomSchemaFormProps['onSetterChange'] = (keyPaths, setterName) => {\n    if (!node) {\n      return;\n    }\n    node.value.configure = node.value.configure || {};\n    node.value.configure.advanceSetter = node.value.configure.advanceSetter || {};\n    node.value.configure.advanceSetter[keyPaths.join('.')] = {\n      name: keyPaths.join('.'),\n      setter: setterName,\n    };\n  };\n\n  const formRef = useRef<CustomSchemaFormInstance>(null);\n\n  useEffect(() => {\n    const loopObj = node?.value.loop;\n    const newValue = {\n      id: node?.id,\n      condition: node?.value.condition ?? true,\n      loop: {\n        open: loopObj?.open || false,\n        data: loopObj?.data || [],\n        forName: loopObj?.forName || 'item',\n        forIndex: loopObj?.forIndex || 'index',\n        key: loopObj?.key || '',\n        name: loopObj?.name || '',\n      },\n      refId: node?.value.refId,\n      nodeName: node?.value.nodeName,\n    };\n    formRef.current?.setFields(newValue);\n  }, [node]);\n\n  const onValueChange = (newVal: { refId: string; loop: any; condition: any; nodeName: any }) => {\n    if (!node) {\n      return;\n    }\n    node.value.loop = newVal.loop;\n    node.value.condition = newVal.condition;\n    node.value.refId = newVal.refId;\n    node.value.nodeName = newVal.nodeName;\n    node.updateValue();\n  };\n  if (!node) {\n    return <></>;\n  }\n\n  return (\n    <div className={styles.advanceBox}>\n      <CustomSchemaForm\n        nodeId={props.node?.id}\n        key={node.id}\n        pluginCtx={props.pluginCtx}\n        defaultSetterConfig={node.value.configure?.advanceSetter || {}}\n        onSetterChange={onSetterChange}\n        properties={innerProperties}\n        initialValue={{}}\n        ref={formRef}\n        onValueChange={onValueChange}\n      />\n    </div>\n  );\n};\n\nexport const AdvancePanelConfig: CRightPanelItem = {\n  key: 'Advance',\n  name: 'Advance',\n  view: ({ node, pluginCtx }) => {\n    if (!node) {\n      return <></>;\n    }\n    return <AdvancePanel node={node} pluginCtx={pluginCtx} />;\n  },\n  show: (props) => {\n    return props.node?.material?.value.advanceCustom?.rightPanel?.advance !== false;\n  },\n};\n"
  },
  {
    "path": "packages/engine/src/plugins/AdvancePanel/style.module.scss",
    "content": ".advanceBox {\n  padding: 0 20px;\n  :global {\n    .ant-input-affix-wrapper .ant-input-prefix {\n      margin-inline-end: 0;\n    }\n  }\n}\n"
  },
  {
    "path": "packages/engine/src/plugins/ComponentLibrary/components/DragItem/index.tsx",
    "content": "import { Popover } from 'antd';\nimport clsx from 'clsx';\nimport React, { useMemo } from 'react';\nimport styles from './style.module.scss';\n\nexport type DragComponentItemProps = {\n  id: string;\n  name: string;\n  description?: any;\n  icon: React.ReactNode | string;\n  iconText?: string;\n  style?: React.CSSProperties;\n  containerClassName?: string;\n};\n\nexport const DragComponentItem = (props: DragComponentItemProps) => {\n  const dragInfo = {\n    [DRAG_ITEM_KEY]: props.id,\n  };\n\n  const icon = useMemo(() => {\n    if (props.iconText) {\n      return <div className={styles.iconText}>{String(props.iconText).toUpperCase()}</div>;\n    }\n    if (typeof props.icon === 'string') {\n      return <img className={styles.iconImg} src={props.icon} draggable={false} />;\n    } else {\n      return props.icon;\n    }\n  }, [props.icon, props.iconText]);\n\n  const contentView = (\n    <div className={clsx([styles.square, props.containerClassName])}>\n      <div {...dragInfo} className={styles.componentItem} style={props.style}>\n        <div className={styles.iconBox}>{icon}</div>\n        <span>{props.name}</span>\n      </div>\n    </div>\n  );\n\n  if (props.description) {\n    return (\n      <Popover\n        overlayInnerStyle={{\n          maxWidth: '300px',\n          maxHeight: '200px',\n          overflow: 'auto',\n        }}\n        content={props.description || ''}\n        zIndex={1000}\n        placement=\"right\"\n      >\n        {contentView}\n      </Popover>\n    );\n  } else {\n    return contentView;\n  }\n};\n\nexport const DRAG_ITEM_KEY = 'data-drag-key';\n"
  },
  {
    "path": "packages/engine/src/plugins/ComponentLibrary/components/DragItem/style.module.scss",
    "content": ".square {\n  user-select: none;\n  position: relative;\n  flex: 33.3% 0;\n  &::before {\n    content: '';\n    padding-top: 100%;\n    float: left;\n  }\n  &::after {\n    content: '';\n    display: block;\n    clear: both;\n  }\n}\n\n.componentItem {\n  user-select: none;\n  overflow: hidden;\n  position: absolute;\n  width: 100%;\n  height: 100%;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  border-right: 1px solid $borderColor;\n  border-bottom: 1px solid $borderColor;\n  transition: all 0.2s;\n\n  &:hover {\n    box-shadow: 0 6px 16px 0 rgb(0 0 0 / 15%);\n    border-color: transparent;\n    cursor: grab;\n  }\n  &:active {\n    cursor: grabbing;\n  }\n}\n\n.iconImg {\n  width: 100%;\n}\n\n.iconBox {\n  width: 60%;\n  height: 60%;\n  overflow: hidden;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.iconText {\n  font-size: 18px;\n  font-weight: bolder;\n  color: $textColor;\n}\n"
  },
  {
    "path": "packages/engine/src/plugins/ComponentLibrary/components/ListView/index.tsx",
    "content": "import { useMemo } from 'react';\nimport { SnippetsType } from '@chamn/model';\nimport { Collapse } from 'antd';\nimport { DragComponentItem } from '../DragItem';\nimport styles from './style.module.scss';\n\nexport type ListViewProps = {\n  dataSource: {\n    name: string;\n    list: SnippetsType[];\n  }[];\n};\nexport const ListView = (props: ListViewProps) => {\n  const { dataSource } = props;\n  const defaultActiveKey = dataSource.map((el) => el.name || '');\n\n  const items = useMemo(() => {\n    return dataSource.map((el) => {\n      const category = el.name || '';\n      const contentView = (\n        <div className={styles.collapsePanel}>\n          {el.list.map((it) => {\n            return (\n              <DragComponentItem\n                id={it.id!}\n                key={it.id!}\n                name={it.title}\n                icon={it.snapshot || it.snapshotText}\n                iconText={it.snapshotText}\n                description={it.description || ''}\n              />\n            );\n          })}\n        </div>\n      );\n\n      return {\n        key: category,\n        label: category,\n        children: contentView,\n      };\n    });\n  }, [dataSource]);\n\n  if (!dataSource.length) {\n    return null;\n  }\n\n  return (\n    <div className={styles.ListBox}>\n      <Collapse style={{ width: '100%' }} defaultActiveKey={defaultActiveKey} items={items}></Collapse>\n    </div>\n  );\n};\n"
  },
  {
    "path": "packages/engine/src/plugins/ComponentLibrary/components/ListView/style.module.scss",
    "content": ".ListBox {\n  display: flex;\n  flex-wrap: wrap;\n  overflow: auto;\n  :global {\n    .ant-collapse .ant-collapse-content > .ant-collapse-content-box {\n      padding: 0;\n    }\n    .ant-collapse {\n      border: none;\n      background-color: white;\n    }\n    .ant-collapse-header {\n      padding: 10px 16px !important;\n    }\n    .ant-collapse-expand-icon {\n      padding-inline-end: 0 !important;\n    }\n    .ant-collapse-item {\n      border-color: $borderColor;\n    }\n  }\n}\n\n.collapsePanel {\n  display: flex;\n  flex-wrap: wrap;\n}\n"
  },
  {
    "path": "packages/engine/src/plugins/ComponentLibrary/index.tsx",
    "content": "/* eslint-disable react-refresh/only-export-components */\nimport React, { DOMAttributes } from 'react';\nimport { AppstoreAddOutlined, SearchOutlined } from '@ant-design/icons';\nimport { LayoutDragAndDropExtraDataType, Sensor } from '@chamn/layout';\nimport { Input, Tabs } from 'antd';\nimport { CPlugin, CPluginCtx } from '../../core/pluginManager';\nimport localize from './localize';\nimport styles from './style.module.scss';\nimport { withTranslation, WithTranslation } from 'react-i18next';\nimport { ListView } from './components/ListView';\nimport { getTargetMNodeKeyVal, searchComponentSnippets } from './util';\nimport { DRAG_ITEM_KEY } from './components/DragItem';\nimport {\n  CNode,\n  CRootNode,\n  CSlot,\n  DragAndDropEventExtraData,\n  findContainerNode,\n  isPageModel,\n  SnippetsCollection,\n  SnippetsType,\n} from '@chamn/model';\nimport { capitalize } from 'lodash-es';\nimport { InsertNodePosType } from '@chamn/model/src';\nimport { DesignerPluginInstance } from '../Designer/type';\n\ninterface ComponentLibViewProps extends WithTranslation {\n  pluginCtx: CPluginCtx<ComponentLibPluginConfig>;\n}\n\nexport const PLUGIN_NAME = 'ComponentLib';\nconst i18nNamespace = `plugin:${PLUGIN_NAME}`;\n\nconst TabTitle = ({ children }: { children: any }) => {\n  return <div className={styles.tabTitle}>{children}</div>;\n};\n\ntype ComponentLibViewState = {\n  componentsList: SnippetsCollection;\n  searchString: string;\n  searchMode: boolean;\n  searchResultList: {\n    name: string;\n    list: SnippetsType[];\n  }[];\n};\n\nexport type ComponentLibPluginConfig = {\n  customSearchBar?: (props: {\n    defaultInputView: React.ReactNode;\n    updateState: (newState: Partial<ComponentLibViewState>) => string;\n  }) => React.ReactNode;\n};\n\nclass ComponentLibView extends React.Component<ComponentLibViewProps, ComponentLibViewState> {\n  containerRef: React.RefObject<HTMLDivElement>;\n  disposeList: (() => void)[] = [];\n  boxSensor!: Sensor<DragAndDropEventExtraData>;\n\n  constructor(props: ComponentLibViewProps) {\n    super(props);\n    this.containerRef = React.createRef<HTMLDivElement>();\n    this.state = {\n      componentsList: [],\n      searchString: '',\n      searchMode: false,\n      searchResultList: [],\n    };\n  }\n\n  leftPanelVisibleCb = async ({ visible, panelName }: { visible: boolean; panelName: string }) => {\n    await this.props.pluginCtx.pluginManager.onPluginReadyOk('Designer');\n    if (panelName === PLUGIN_NAME && visible) {\n      this.registerDragEvent();\n    }\n  };\n\n  updateComponentsList() {\n    const { pluginCtx } = this.props;\n    const { pageModel } = pluginCtx;\n    const { materialsModel } = pageModel;\n    const allSnippets = materialsModel.getAllSnippets();\n\n    if (allSnippets.length && allSnippets[0].name === 'default') {\n      const df = allSnippets.shift();\n      if (df) {\n        allSnippets.push(df);\n      }\n    }\n\n    this.setState({\n      componentsList: allSnippets,\n    });\n  }\n\n  async componentDidMount() {\n    await this.props.pluginCtx.pluginManager.onPluginReadyOk('Designer');\n    this.registerDragEvent();\n    const { pluginCtx } = this.props;\n    const { i18n } = pluginCtx;\n    Object.keys(localize).forEach((lng) => {\n      i18n.addResourceBundle(lng, i18nNamespace, localize[lng], true, true);\n    });\n\n    i18n.update();\n    this.updateComponentsList();\n    pluginCtx.globalEmitter.on('updateMaterials', () => {\n      this.updateComponentsList();\n    });\n  }\n\n  componentWillUnmount(): void {\n    this.disposeList.map((el) => el());\n  }\n\n  getNewNode = (targetDom: HTMLElement | null) => {\n    const { pluginCtx } = this.props;\n    const pageModel = pluginCtx.pageModel;\n\n    if (!targetDom) {\n      return;\n    }\n    const targetNodeId = getTargetMNodeKeyVal(targetDom as HTMLElement, DRAG_ITEM_KEY);\n\n    if (!targetNodeId) {\n      return;\n    }\n\n    const meta = pageModel.materialsModel.findSnippetById(targetNodeId);\n    if (!meta) {\n      return;\n    }\n    return pageModel?.createNode(meta.schema);\n  };\n\n  registerDragEvent = async () => {\n    const { containerRef } = this;\n    const { pluginCtx } = this.props;\n    const designerHandle = await pluginCtx.pluginManager.get<DesignerPluginInstance>('Designer');\n\n    if (!designerHandle) {\n      return;\n    }\n    if (!containerRef.current) {\n      return;\n    }\n    const designerExport = designerHandle.export;\n    const dnd = designerExport.getDnd()!;\n    const boxSensor = new Sensor<LayoutDragAndDropExtraDataType>({\n      name: 'ComponentListBox',\n      container: containerRef.current,\n      mainDocument: document,\n    });\n\n    this.boxSensor = boxSensor;\n\n    boxSensor.setCanDrag(async (eventObj) => {\n      const newNode = this.getNewNode(eventObj.event.target as any);\n\n      this.props.pluginCtx.pluginManager.get<DesignerPluginInstance>('Designer').then((designerHandle) => {\n        const designerExport = designerHandle?.export;\n        designerExport?.selectNode('');\n      });\n\n      return {\n        ...eventObj,\n        extraData: {\n          dropType: 'NEW_ADD',\n          dragNode: newNode,\n        },\n      };\n    });\n    dnd.registerSensor(boxSensor);\n\n    const dragStart = () => {\n      const { getWorkbench } = this.props.pluginCtx;\n      const workbench = getWorkbench();\n      if (!workbench.state.leftBoxFixed) {\n        workbench.closeLeftPanel();\n      }\n    };\n\n    this.disposeList.push(() => {\n      dnd.emitter.off('dragStart', dragStart);\n    });\n    dnd.emitter.on('dragStart', dragStart);\n  };\n\n  toAddNewNode: DOMAttributes<HTMLDivElement>['onClick'] = async (event) => {\n    const newNode = this.getNewNode(event.nativeEvent.target as HTMLElement);\n    if (!newNode) {\n      return;\n    }\n\n    const { pageModel } = this.props.pluginCtx;\n\n    return this.props.pluginCtx.pluginManager.get<DesignerPluginInstance>('Designer').then(async (designerHandle) => {\n      const designerExport = designerHandle?.export;\n      // 获取当前选中，如果存在，就插入到当前选中的下面，否则就插入到根节点下面\n      const selectedNodeId = designerExport?.getSelectedNodeId();\n      const selectedNode = pageModel.getNode(selectedNodeId);\n      const containerNode = findContainerNode(selectedNode);\n      let pos: InsertNodePosType = 'CHILD_END';\n      let dropNode: CNode | CRootNode | CSlot | null = selectedNode ?? null;\n      if (selectedNode) {\n        // 当前节点的父级节点是容器时\n        if (containerNode === selectedNode.parent) {\n          if (selectedNode.isContainer()) {\n            pos = 'CHILD_END';\n            dropNode = selectedNode;\n          } else {\n            pos = 'AFTER';\n            dropNode = selectedNode;\n          }\n        } else if (containerNode && !isPageModel(containerNode)) {\n          pos = 'CHILD_END';\n          dropNode = containerNode;\n        }\n      } else {\n        const rootNode = pageModel.getRootNode();\n        if (rootNode) {\n          pos = 'CHILD_END';\n          dropNode = rootNode;\n        }\n      }\n      const designerInstance = designerExport?.getInstance();\n\n      const addFlag = await designerInstance?.customAdvanceHook.onNewAdd({\n        dragNode: newNode,\n        dropNode: dropNode as any,\n        eventObj: {\n          from: event.nativeEvent,\n          fromSensor: this.boxSensor,\n          pointer: null as any,\n          fromPointer: null as any,\n          extraData: {},\n        },\n      });\n      if (addFlag === false) {\n        return;\n      }\n      let addNodeInfo = {\n        dragNode: newNode,\n        dropNode: dropNode,\n        pos: pos,\n      };\n      if (typeof addFlag === 'object') {\n        addNodeInfo = {\n          ...addNodeInfo,\n          dragNode: (addFlag.addNode as any) ?? addNodeInfo.dragNode,\n          dropNode: (addFlag.dropNode as any) ?? addNodeInfo.dropNode,\n          pos: (addFlag.dropPosInfo?.pos as any) ?? addNodeInfo.pos,\n        };\n      }\n      pageModel.addNode(addNodeInfo.dragNode, addNodeInfo.dropNode as CNode, addNodeInfo.pos);\n      setTimeout(() => {\n        designerExport?.selectNode(addNodeInfo.dragNode.id);\n      }, 16);\n    });\n  };\n\n  onSearch = () => {\n    if (!this.state.searchString.length) {\n      this.setState({\n        searchResultList: [],\n        searchMode: false,\n      });\n      return;\n    }\n    const newList = searchComponentSnippets(this.state.componentsList, this.state.searchString);\n    this.setState({\n      searchResultList: newList,\n      searchMode: true,\n    });\n  };\n\n  render(): React.ReactNode {\n    const { componentsList, searchString, searchResultList, searchMode } = this.state;\n    const { toAddNewNode } = this;\n    const CustomSearchBar = this.props.pluginCtx.config.customSearchBar;\n    const items = componentsList.map((el) => {\n      return {\n        label: <TabTitle>{capitalize(el.name)}</TabTitle>,\n        key: el.name,\n        children: <ListView dataSource={el.list} />,\n      };\n    });\n\n    const defaultSearchValue = (\n      <Input\n        value={searchString}\n        placeholder=\"input search text\"\n        suffix={<SearchOutlined style={{ color: 'rgba(0,0,0,.25)' }} />}\n        onPressEnter={this.onSearch}\n        allowClear\n        onClear={() => {\n          this.setState({\n            searchMode: false,\n            searchResultList: [],\n          });\n        }}\n        onChange={(e) => {\n          this.setState({\n            searchString: e.target.value,\n          });\n        }}\n      />\n    );\n    return (\n      <div ref={this.containerRef} className={styles.container} onClick={toAddNewNode}>\n        <div className={styles.header}>\n          {CustomSearchBar ? (\n            <CustomSearchBar defaultInputView={defaultSearchValue} updateState={this.setState.bind(this) as any} />\n          ) : (\n            defaultSearchValue\n          )}\n        </div>\n        {searchMode && (\n          <div>\n            {!searchResultList.length && <div className={styles.notFoundComponent}>暂无相关组件</div>}\n            <ListView dataSource={searchResultList} />\n          </div>\n        )}\n        {!searchResultList.length && !searchMode && (\n          <Tabs\n            defaultActiveKey=\"BaseComponent\"\n            items={items}\n            style={{\n              width: '100%',\n            }}\n          />\n        )}\n      </div>\n    );\n  }\n}\n\nexport const ComponentLibPlugin: CPlugin<ComponentLibPluginConfig> = {\n  name: PLUGIN_NAME,\n  PLUGIN_NAME,\n  async init(ctx) {\n    const ComponentLibViewWithLocalize = withTranslation(i18nNamespace)(ComponentLibView);\n    const Title = withTranslation(i18nNamespace)(({ t }) => {\n      return <>{t('pluginName')}</>;\n    });\n    const workbench = ctx.getWorkbench();\n    workbench.addLeftPanel({\n      title: <Title />,\n      name: 'ComponentLib',\n      icon: <AppstoreAddOutlined />,\n      render: <ComponentLibViewWithLocalize pluginCtx={ctx} />,\n    });\n    ctx.pluginReadyOk();\n  },\n  reload: async () => {\n    console.log(PLUGIN_NAME, 'not suooprt reload');\n  },\n  async destroy(ctx) {\n    console.log('destroy', ctx);\n  },\n  export: () => {\n    return {};\n  },\n  meta: {\n    engine: {\n      version: '1.0.0',\n    },\n  },\n};\n\nComponentLibPlugin.PLUGIN_NAME = PLUGIN_NAME;\n"
  },
  {
    "path": "packages/engine/src/plugins/ComponentLibrary/localize/en_US/index.ts",
    "content": "export const en_US: Record<string, any> = {\n  pluginName: 'Components',\n};\n"
  },
  {
    "path": "packages/engine/src/plugins/ComponentLibrary/localize/index.ts",
    "content": "import { zh_CN } from './zh_CN';\nimport { en_US } from './en_US';\nexport default { zh_CN, en_US } as Record<string, any>;\n"
  },
  {
    "path": "packages/engine/src/plugins/ComponentLibrary/localize/zh_CN/index.ts",
    "content": "export const zh_CN: Record<string, any> = {\n  pluginName: '组件库',\n};\n"
  },
  {
    "path": "packages/engine/src/plugins/ComponentLibrary/style.module.scss",
    "content": ".container {\n  width: 100%;\n  height: 100%;\n  overflow: auto;\n  background-color: white;\n  .header {\n    padding: 10px 10px 20px;\n  }\n\n  :global {\n    .ant-tabs .ant-tabs-tab + .ant-tabs-tab {\n      margin-left: 12px;\n    }\n\n    .ant-tabs-content.ant-tabs-content-top {\n      height: 100%;\n      overflow: auto;\n    }\n    .ant-tabs-tab {\n      padding: 6px 0;\n    }\n    .ant-tabs-nav {\n      margin-bottom: 0;\n    }\n  }\n}\n\n.tabTitle {\n  padding: 0 10px;\n  font-size: 12px;\n  font-weight: 400;\n  text-rendering: optimizeLegibility;\n}\n\n.header {\n  display: flex;\n  align-items: center;\n}\n\n.notFoundComponent {\n  padding: 5px 10px;\n}\n"
  },
  {
    "path": "packages/engine/src/plugins/ComponentLibrary/util.ts",
    "content": "import { SnippetsCollection, SnippetsType } from '@chamn/model';\n\nexport const getTargetMNodeKeyVal = (dom: HTMLElement | null, key: string): null | string => {\n  if (!dom) {\n    return null;\n  }\n  const val = dom.getAttribute(key);\n  if (!val) {\n    return getTargetMNodeKeyVal(dom.parentElement, key);\n  } else {\n    return val;\n  }\n};\n\nexport const searchComponentSnippets = (componentList: SnippetsCollection, searchKeyWords: string) => {\n  const newResList: {\n    name: string;\n    list: SnippetsType[];\n  }[] = [];\n  const searchString = searchKeyWords?.toLowerCase();\n  componentList.forEach((el) => {\n    // category\n    const tempList = [...el.list];\n    tempList.forEach((it) => {\n      const snippets = it.list.filter((i) => {\n        let hit: boolean | undefined = false;\n        if (typeof i.description === 'string') {\n          hit = i.description?.includes(searchString);\n        }\n        hit = hit || i.title?.toLowerCase().includes(searchString);\n        hit = hit || i.snapshotText?.toLowerCase().includes(searchString);\n        hit = hit || i.category?.toLowerCase().includes(searchString);\n        return hit;\n      });\n      if (snippets.length) {\n        newResList.push({\n          name: it.name,\n          list: snippets,\n        });\n      }\n    });\n  });\n\n  return newResList;\n};\n"
  },
  {
    "path": "packages/engine/src/plugins/ComponentStatePanel/index.tsx",
    "content": "/* eslint-disable react-refresh/only-export-components */\nimport { useEffect, useRef } from 'react';\nimport { CNode, CRootNode } from '@chamn/model';\nimport { CPluginCtx } from '../../core/pluginManager';\nimport { CRightPanelItem } from '../RightPanel/view';\nimport { MonacoEditor, MonacoEditorInstance } from '../../component/MonacoEditor';\n\nexport type ComponentStatePanelProps = {\n  node: CNode | CRootNode | null;\n  pluginCtx: CPluginCtx;\n};\n\nexport const ComponentStatePanel = (props: ComponentStatePanelProps) => {\n  const node = props.node!;\n\n  const nodeState = node.value.state || {};\n  const editorRef = useRef<MonacoEditorInstance | null>(null);\n  useEffect(() => {\n    const currentState = node.value.state || {};\n    editorRef?.current?.setValue(JSON.stringify(currentState, null, 2));\n  }, [node]);\n\n  const onValueChange = (newValStr?: string) => {\n    try {\n      const newVal = JSON.parse(newValStr || '{}');\n      node.value.state = newVal;\n      node.updateValue();\n    } catch (e) {\n      console.warn(e);\n    }\n  };\n\n  return (\n    <>\n      <MonacoEditor\n        initialValue={JSON.stringify(nodeState, null, 2)}\n        language={'json'}\n        options={{\n          automaticLayout: true,\n          // lineDecorationsWidth: 0,\n          tabSize: 2,\n          minimap: { enabled: false },\n          quickSuggestions: false,\n          // lineNumbers: 'off',\n          suggestOnTriggerCharacters: false,\n          folding: false,\n        }}\n        onDidMount={(editor) => {\n          editorRef.current = editor;\n        }}\n        onChange={onValueChange}\n      />\n    </>\n  );\n};\n\nexport const ComponentStatePanelConfig: CRightPanelItem = {\n  key: 'State',\n  name: 'State',\n  view: ({ node, pluginCtx }) => {\n    if (!node) {\n      return <></>;\n    }\n    return <ComponentStatePanel node={node} pluginCtx={pluginCtx} />;\n  },\n  show: (props) => {\n    return props.node?.material?.value.advanceCustom?.rightPanel?.state !== false;\n  },\n};\n"
  },
  {
    "path": "packages/engine/src/plugins/Designer/components/Canvas/advanceCustomHook.ts",
    "content": "import { CPluginCtx } from '@/core/pluginManager';\nimport { Layout, LayoutDragAndDropExtraDataType, LayoutDragEvent } from '@/index';\nimport { waitReactUpdate } from '@/utils';\nimport { AdvanceCustom, CNode, CRootNode } from '@chamn/model';\n\nexport interface AdvanceCustomHookOptions {\n  getPortalViewCtx: () => {\n    setView: (view: React.ReactNode) => void;\n    clearView: () => void;\n  };\n  ctx: CPluginCtx;\n  layoutRef: React.RefObject<Layout>;\n}\n\nexport type HookParameter = {\n  dragNode?: CNode | CRootNode;\n  dropNode?: CNode | CRootNode;\n  eventObj: LayoutDragEvent<LayoutDragAndDropExtraDataType>;\n};\n\nexport class AdvanceCustomHook {\n  getPortalViewCtx!: AdvanceCustomHookOptions['getPortalViewCtx'];\n  ctx: CPluginCtx;\n  layoutRef: AdvanceCustomHookOptions['layoutRef'];\n  constructor(options: AdvanceCustomHookOptions) {\n    this.getPortalViewCtx = options.getPortalViewCtx;\n    this.ctx = options.ctx;\n    this.layoutRef = options.layoutRef;\n  }\n\n  async canDrag({ dragNode, eventObj }: HookParameter): ReturnType<Required<AdvanceCustom>['canDragNode']> {\n    const nodeAdvanceCustom = dragNode?.material?.value.advanceCustom;\n    if (nodeAdvanceCustom?.canDragNode) {\n      const res = nodeAdvanceCustom?.canDragNode(dragNode!, {\n        context: this.ctx,\n        viewPortal: this.getPortalViewCtx(),\n        event: eventObj,\n        extra: eventObj.extraData,\n      });\n      return res;\n    }\n\n    return true;\n  }\n\n  async canDrop({ dragNode, dropNode, eventObj }: HookParameter): ReturnType<Required<AdvanceCustom>['canDropNode']> {\n    const nodeAdvanceCustom = dragNode?.material?.value.advanceCustom;\n    const dropNodeAdvanceCustom = dropNode!.material?.value.advanceCustom;\n    let res: Awaited<ReturnType<Required<AdvanceCustom>['canDropNode']>> = {} as any;\n    const commonParams = {\n      dropNode,\n      context: this.ctx,\n      viewPortal: this.getPortalViewCtx(),\n      event: eventObj,\n      extra: eventObj.extraData!,\n    };\n    if (nodeAdvanceCustom?.canDropNode) {\n      res = await nodeAdvanceCustom.canDropNode(dragNode!, commonParams)!;\n\n      // 判断 dropNode 是否可以接受 dragNode\n      if (dropNodeAdvanceCustom?.canAcceptNode) {\n        let canAcceptFlag: Awaited<ReturnType<Required<AdvanceCustom>['canAcceptNode']>>;\n        if (typeof res === 'object') {\n          canAcceptFlag = await dropNodeAdvanceCustom?.canAcceptNode(res.dragNode!, {\n            ...commonParams,\n            ...res,\n          });\n        } else {\n          canAcceptFlag = await dropNodeAdvanceCustom?.canAcceptNode(dragNode!, {\n            ...commonParams,\n          });\n        }\n        return canAcceptFlag;\n      } else {\n        return res;\n      }\n    } else if (dropNodeAdvanceCustom?.canAcceptNode) {\n      const canAcceptFlag = await dropNodeAdvanceCustom?.canAcceptNode(dragNode!, {\n        ...commonParams,\n      });\n      return canAcceptFlag;\n    }\n    return true;\n  }\n\n  async onNewAdd({ dragNode, dropNode, eventObj }: HookParameter): ReturnType<Required<AdvanceCustom>['onNewAdd']> {\n    const res = await dragNode!.material?.value.advanceCustom?.onNewAdd?.(dragNode!, {\n      dropNode: dropNode,\n      context: this.ctx,\n      viewPortal: this.getPortalViewCtx(),\n      event: eventObj,\n      extra: eventObj.extraData!,\n    });\n    if (res === false) {\n      return false;\n    }\n    return res;\n  }\n\n  getSelectRectViewRender(node: CNode | CRootNode) {\n    const material = node.material;\n    return material?.value.advanceCustom?.selectRectViewRender;\n  }\n\n  getHoverRectViewRender(node?: CNode | CRootNode | null) {\n    if (!node) {\n      return null;\n    }\n    const material = node.material;\n    return material?.value.advanceCustom?.hoverRectViewRender;\n  }\n\n  getDropViewRender(node?: CNode | CRootNode | null) {\n    if (!node) {\n      return null;\n    }\n    const material = node.material;\n    return material?.value.advanceCustom?.dropViewRender;\n  }\n\n  getGhostViewRender(node?: CNode | CRootNode | null) {\n    if (!node) {\n      return null;\n    }\n    const material = node.material;\n    return material?.value.advanceCustom?.ghostViewRender;\n  }\n  getToolbarViewRender(node?: CNode | CRootNode | null) {\n    if (!node) {\n      return null;\n    }\n    const material = node.material;\n    return material?.value.advanceCustom?.toolbarViewRender;\n  }\n\n  async onCopy(node: CNode | CRootNode) {\n    let resNode = node;\n    const ctx = this.ctx;\n    const material = node.material;\n    const onCopy = material?.value.advanceCustom?.onCopy;\n    if (onCopy) {\n      const newRes = await onCopy(node, {\n        viewPortal: this.getPortalViewCtx(),\n        context: ctx,\n        extra: {},\n      });\n      if (newRes === false) {\n        return false;\n      }\n      if (typeof newRes === 'object') {\n        resNode = newRes.copyNode ?? resNode;\n      }\n    }\n\n    if (resNode) {\n      const newNode = ctx.pageModel.copyNode(resNode as CNode);\n      if (newNode) {\n        await waitReactUpdate();\n      }\n    }\n\n    return resNode;\n  }\n\n  async onDelete(node: CNode | CRootNode) {\n    let resNode = node;\n    const ctx = this.ctx;\n    const material = node.material;\n    const onDelete = material?.value.advanceCustom?.onDelete;\n\n    if (onDelete) {\n      const newRes = await onDelete(node, {\n        viewPortal: this.getPortalViewCtx(),\n        context: ctx,\n        extra: {},\n      });\n      if (newRes === false) {\n        return false;\n      }\n      if (typeof newRes === 'object') {\n        resNode = newRes.deleteNode ?? resNode;\n      }\n    }\n\n    if (resNode) {\n      const newNode = ctx.pageModel.deleteNode(resNode as CNode);\n      if (newNode) {\n        await waitReactUpdate();\n      }\n    }\n\n    return resNode;\n  }\n}\n"
  },
  {
    "path": "packages/engine/src/plugins/Designer/components/Canvas/index.tsx",
    "content": "import React from 'react';\nimport { Layout, LayoutPropsType } from '@chamn/layout';\nimport { AdvanceCustom, CNode, CPage, CRootNode, InsertNodePosType } from '@chamn/model';\nimport localize from '../../localize';\nimport { PLUGIN_NAME } from '../../config';\nimport { DefaultSelectToolBar, DefaultSelectToolBarProps, getDefaultToolbarItem } from '../DefaultSelectToolBar';\nimport { getClosestNodeList } from '../../util';\nimport { GhostView } from '../GhostView';\n\nimport styles from './style.module.scss';\nimport '@chamn/layout/dist/style.css';\nimport { AssetPackage } from '@chamn/model';\nimport { createPortal } from 'react-dom';\nimport { CPluginCtx } from '@/core/pluginManager';\nimport { AdvanceCustomHook } from './advanceCustomHook';\nimport { DesignerPluginConfig } from '../../type';\nimport { AdvanceCustomFuncParam } from '@chamn/model';\n\nexport type DesignerCtx = CPluginCtx<DesignerPluginConfig>;\nexport type DesignerPropsType = {\n  pluginCtx: DesignerCtx;\n};\n\nexport type WrapComponentOptionsType = {\n  ctx: DesignerCtx;\n  node: CNode | CRootNode;\n};\n\ntype DesignerStateType = {\n  pageModel: CPage;\n  hoverToolbarView: React.ReactNode;\n  selectToolbarView: React.ReactNode;\n  selectRectViewRender: AdvanceCustom['selectRectViewRender'] | null;\n  hoverRectViewRender: AdvanceCustom['hoverRectViewRender'] | null;\n  dropViewRender: AdvanceCustom['dropViewRender'] | null;\n  ghostView: React.ReactNode;\n  portalView: React.ReactNode;\n};\n\nexport class Designer extends React.Component<DesignerPropsType, DesignerStateType> {\n  layoutRef: React.RefObject<Layout>;\n  customAdvanceHook: AdvanceCustomHook;\n  constructor(props: DesignerPropsType) {\n    super(props);\n\n    this.state = {\n      pageModel: props.pluginCtx.pageModel,\n      hoverToolbarView: null,\n      selectToolbarView: null,\n      ghostView: null,\n      portalView: null,\n      selectRectViewRender: null,\n      hoverRectViewRender: null,\n      dropViewRender: null,\n    };\n    this.layoutRef = React.createRef<Layout>();\n    this.customAdvanceHook = new AdvanceCustomHook({\n      getPortalViewCtx: this.getPortalViewCtx,\n      ctx: this.props.pluginCtx,\n      layoutRef: this.layoutRef,\n    });\n  }\n\n  getPortalViewCtx = () => {\n    return {\n      setView: (view: React.ReactNode) => {\n        this.setState({\n          portalView: view,\n        });\n      },\n      clearView: () => {\n        this.setState({\n          portalView: null,\n        });\n      },\n    };\n  };\n\n  /**\n   * 更新 render 中的 components ;\n   */\n  async updateRenderComponents(newComponentMap: Record<string, any> = {}) {\n    const oldComponents = this.props.pluginCtx.config.components || {};\n    const newComponents = {\n      ...oldComponents,\n      ...newComponentMap,\n    };\n    this.props.pluginCtx.config.components = newComponents;\n    const { layoutRef } = this;\n    if (!layoutRef.current) {\n      console.warn('layout not ready ok');\n      return;\n    }\n\n    await layoutRef.current.ready();\n\n    const layoutInstance = layoutRef.current;\n    // 直接修改到 render 中去\n    layoutInstance?.designRenderRef?.current?.updateComponents(newComponents);\n  }\n\n  componentDidMount(): void {\n    const { i18n } = this.props.pluginCtx;\n    Object.keys(localize).forEach((lng) => {\n      i18n.addResourceBundle(lng, `plugin:${PLUGIN_NAME}`, localize[lng], true, true);\n    });\n    this.init();\n  }\n\n  getIframeDom() {\n    return this.layoutRef.current?.iframeContainer;\n  }\n\n  reloadRender() {\n    this.layoutRef.current?.reload();\n    this.props.pluginCtx.pageModel.reloadPage();\n  }\n\n  async init() {\n    const { layoutRef } = this;\n    const { pluginCtx } = this.props;\n    if (!layoutRef.current) {\n      console.warn('layout not ready ok');\n      return;\n    }\n\n    await layoutRef.current.ready();\n    const layoutInstance = layoutRef.current;\n\n    pluginCtx.emitter.emit('ready', {\n      UIInstance: this,\n    });\n\n    layoutInstance.dnd.emitter.on('click', ({ event }: { event: MouseEvent }) => {\n      const workbench = pluginCtx.getWorkbench();\n      workbench.onGlobalClick(event);\n    });\n\n    this.props.pluginCtx.pageModel.emitter.on('onPageChange', ({ node }) => {\n      layoutRef.current?.designRenderRef?.current?.rerender(node);\n    });\n\n    this.props.pluginCtx.pageModel.emitter.on('onReloadPage', ({ node }) => {\n      layoutRef.current?.designRenderRef?.current?.rerender(node);\n    });\n\n    pluginCtx.pluginReadyOk();\n  }\n\n  onNodeDragging: LayoutPropsType['onNodeDragging'] = async (eventObj) => {\n    const extraData = eventObj.extraData;\n    const dragNode = extraData?.dragNode;\n    if (dragNode) {\n      const res = dragNode.material?.value.advanceCustom?.onDragging?.(dragNode, {\n        context: this.props.pluginCtx,\n        viewPortal: this.getPortalViewCtx(),\n        event: eventObj,\n        extra: extraData,\n      });\n      return res;\n    }\n  };\n\n  onNodeDragEnd: LayoutPropsType['onNodeDraEnd'] = async () => {\n    this.setState({\n      ...this.state,\n      dropViewRender: this.props.pluginCtx.config.dropViewRender,\n    });\n  };\n\n  onNodeDrop: LayoutPropsType['onNodeDrop'] = async (eventObj) => {\n    const { layoutRef } = this;\n    const pageModel = this.props.pluginCtx.pageModel;\n    const extraData = eventObj.extraData;\n    const dropPosInfo = extraData?.dropPosInfo;\n    const posFlag = {\n      before: 'BEFORE',\n      after: 'AFTER',\n      current: 'CHILD_START',\n    }[dropPosInfo?.pos || 'after'] as InsertNodePosType;\n    const dragNode = extraData?.dragNode;\n    if (dragNode) {\n      const res = await dragNode.material?.value.advanceCustom?.onDrop?.(dragNode, {\n        context: this.props.pluginCtx,\n        viewPortal: this.getPortalViewCtx(),\n        event: eventObj,\n        extra: extraData,\n      });\n      if (res === false) {\n        return false;\n      }\n    }\n    if (!extraData?.dropNode) {\n      console.warn('cancel drop, because drop node is null');\n      return false;\n    }\n    if (extraData.dropType === 'NEW_ADD') {\n      if (dragNode) {\n        const res = await dragNode.material?.value.advanceCustom?.onNewAdd?.(dragNode, {\n          dropNode: extraData.dropNode,\n          context: this.props.pluginCtx,\n          viewPortal: this.getPortalViewCtx(),\n          event: eventObj,\n          extra: extraData,\n        });\n        if (res === false) {\n          return false;\n        }\n      }\n      // 说明是根节点，直接插入到 第一个 child\n      if (extraData.dropNode?.nodeType !== 'NODE') {\n        pageModel?.addNode(extraData.dragNode as CNode, extraData.dropNode!, 'CHILD_START');\n      } else {\n        pageModel?.addNode(extraData.dragNode as CNode, extraData.dropNode!, posFlag);\n      }\n    } else {\n      if (extraData.dropNode?.id === extraData.dragNode?.id) {\n        console.warn('dragNode and dropNode id is the same');\n        return false;\n      }\n      const res = pageModel.moveNodeById(extraData.dragNode?.id || '', extraData?.dropNode?.id || '', posFlag);\n      if (!res) {\n        console.warn('drop failed');\n        return false;\n      }\n    }\n\n    this.props.pluginCtx.emitter.emit('onDrop', eventObj);\n    return new Promise((resolve) => {\n      // 延迟去选中，因为节点放置下去，可能还没有渲染\n      setTimeout(async () => {\n        if (dragNode) {\n          const flag = await this.onSelectNode(dragNode, null);\n          if (flag === false) {\n            layoutRef.current?.selectNode('');\n            return resolve(false);\n          }\n        }\n        layoutRef.current?.selectNode(extraData.dragNode?.id || '');\n        resolve(true);\n      }, 16);\n    });\n  };\n\n  onSelectNode: Required<LayoutPropsType>['onSelectNode'] = async (node: CNode | CRootNode | null, event) => {\n    const { pluginCtx } = this.props;\n    const { layoutRef } = this;\n\n    if (!node) {\n      pluginCtx.engine.updateCurrentSelectNode(null);\n      layoutRef.current?.selectNode('');\n      this.setState({\n        selectToolbarView: null,\n      });\n      return false;\n    }\n    const flag = await node.material?.value.advanceCustom?.onSelect?.(node, {\n      context: this.props.pluginCtx,\n      viewPortal: this.getPortalViewCtx(),\n      event: event,\n      extra: {},\n    });\n    if (flag === false) {\n      return flag;\n    }\n\n    const toolbarView = this.getToolbarView(node);\n    this.setState({\n      selectToolbarView: toolbarView,\n      selectRectViewRender:\n        this.customAdvanceHook.getSelectRectViewRender(node) || this.props.pluginCtx.config.selectRectViewRender,\n    });\n\n    // 延迟发送选中事件\n    setTimeout(() => {\n      pluginCtx.engine.updateCurrentSelectNode(node);\n    }, 16.66);\n\n    return true;\n  };\n\n  onDragStart: LayoutPropsType['onNodeDragStart'] = async (e) => {\n    const extraData = e.extraData;\n    const dragNode = extraData?.dragNode;\n    if (!dragNode) {\n      return false;\n    }\n\n    const commonParam = {\n      context: this.props.pluginCtx,\n      viewPortal: this.getPortalViewCtx(),\n      event: e,\n      extra: extraData,\n    };\n\n    const canDragFlag = await dragNode.material?.value.advanceCustom?.canDragNode?.(dragNode, {\n      context: this.props.pluginCtx,\n      viewPortal: this.getPortalViewCtx(),\n      event: e,\n      extra: {},\n    });\n\n    if (canDragFlag === false) {\n      return canDragFlag;\n    }\n\n    const dragFlag = await dragNode.material?.value.advanceCustom?.onDragStart?.(dragNode, { ...commonParam });\n    if (dragFlag === false) {\n      return dragFlag;\n    }\n\n    const ghostView = this.getGhostView(dragNode, commonParam);\n    this.setState({\n      ...this.state,\n      ghostView: ghostView,\n      dropViewRender: this.customAdvanceHook.getDropViewRender(dragNode) || this.props.pluginCtx.config.dropViewRender,\n    });\n    return true;\n  };\n\n  getGhostView = (dragNode: CNode | CRootNode, commonParam: AdvanceCustomFuncParam) => {\n    let ghostView: React.ReactElement = <GhostView node={dragNode} />;\n    const nodeGhostView = this.customAdvanceHook.getGhostViewRender(dragNode);\n    const CustomGhostView = nodeGhostView || this.props.pluginCtx.config.ghostViewRender;\n\n    if (CustomGhostView) {\n      ghostView = <CustomGhostView node={dragNode} params={commonParam} />;\n    }\n    return ghostView;\n  };\n\n  toSelectNode = async (nodeId: string) => {\n    const ctx = this.props.pluginCtx;\n    const node = ctx.pageModel.getNode(nodeId);\n    const flag = await this.onSelectNode(node || null, null);\n    if (flag === false) {\n      return false;\n    } else {\n      this.layoutRef.current?.selectNode(nodeId);\n      return true;\n    }\n  };\n\n  toCopyNode = async (id: string) => {\n    const ctx = this.props.pluginCtx;\n    const cpNode = ctx.pageModel.getNode(id);\n    const newNode = await this.customAdvanceHook.onCopy(cpNode as CNode);\n    if (newNode) {\n      this.toSelectNode(newNode.id);\n      return true;\n    }\n  };\n\n  toDeleteNode = async (nodeId: string) => {\n    const ctx = this.props.pluginCtx;\n    const node = ctx.pageModel.getNode(nodeId);\n    if (!node) {\n      return;\n    }\n    const delRes = await this.customAdvanceHook.onDelete(node);\n    if (delRes === false) {\n      return false;\n    }\n    this.toSelectNode('');\n    return true;\n  };\n\n  toHidden = (id: string) => {\n    const targetNodeModel = this.props.pluginCtx.pageModel.getNode(id) as CNode;\n    if (!targetNodeModel) {\n      return;\n    }\n    const devState = targetNodeModel.value.configure.devState ?? {};\n    devState.condition = false;\n    targetNodeModel.value.configure.devState = devState;\n    targetNodeModel.updateValue();\n    return true;\n  };\n\n  getToolbarView = (node: CNode | CRootNode) => {\n    const list = getClosestNodeList(node, 5);\n    const toolbarProps: DefaultSelectToolBarProps = {\n      nodeList: list,\n      toSelectNode: this.toSelectNode,\n      toCopy: this.toCopyNode,\n      toDelete: this.toDeleteNode,\n      toHidden: this.toHidden,\n    };\n    const defaultToolbarItem = getDefaultToolbarItem(toolbarProps);\n    let toolbarView = <DefaultSelectToolBar {...toolbarProps} />;\n    const ToolbarView =\n      this.customAdvanceHook.getToolbarViewRender(node) || this.props.pluginCtx.config.toolbarViewRender;\n    if (ToolbarView) {\n      toolbarView = (\n        <ToolbarView\n          node={node}\n          context={this.props.pluginCtx}\n          toolBarItems={defaultToolbarItem.map}\n          toolBarItemList={defaultToolbarItem.list}\n        />\n      );\n    }\n    return toolbarView;\n  };\n\n  onHoverNode: LayoutPropsType['onHoverNode'] = (node, dragNode, e) => {\n    this.props.pluginCtx.emitter.emit('onHover', node);\n    const material = node?.material;\n    if (!material) {\n      console.warn('material not found', node);\n    }\n    const commonParam = {\n      context: this.props.pluginCtx,\n      viewPortal: this.getPortalViewCtx(),\n      event: e,\n      extra: {},\n    };\n    const ghostView = this.getGhostView(dragNode, commonParam);\n\n    const newState = {\n      hoverToolbarView: <div className={styles.hoverTips}>{material?.value.title || material?.componentName}</div>,\n      ghostView: ghostView,\n      hoverRectViewRender:\n        this.customAdvanceHook.getHoverRectViewRender(node) || this.props.pluginCtx.config.hoverRectViewRender,\n    };\n    if (!dragNode) {\n      newState.ghostView = null as any;\n    }\n    this.setState({\n      ...newState,\n    });\n  };\n\n  nodeCanDrag: LayoutPropsType['nodeCanDrag'] = async (e) => {\n    const extraData = e.extraData;\n    const dragNode = extraData?.dragNode;\n    const res = await this.customAdvanceHook.canDrag({\n      dragNode,\n      eventObj: e,\n    });\n    return res;\n  };\n\n  nodeCanDrop: LayoutPropsType['nodeCanDrop'] = async (e) => {\n    const extraData = e.extraData;\n    const dragNode = extraData?.dragNode;\n    const res = await this.customAdvanceHook.canDrop({\n      dragNode,\n      eventObj: e,\n      ...extraData,\n    });\n    return res;\n  };\n\n  innerSelectRectViewRender: LayoutPropsType['selectRectViewRender'] = (selectViewProps) => {\n    const { selectRectViewRender } = this.state;\n    const { pluginCtx } = this.props;\n\n    const Comp = selectRectViewRender;\n    const selectNode = pluginCtx.engine.getActiveNode();\n\n    if (!Comp || !selectNode) {\n      return <></>;\n    }\n    return (\n      <Comp\n        node={selectNode}\n        componentInstance={selectViewProps.instance}\n        componentInstanceIndex={selectViewProps.index}\n        params={{\n          viewPortal: this.getPortalViewCtx(),\n          context: this.props.pluginCtx,\n          extra: {},\n        }}\n      />\n    );\n  };\n\n  innerHoverRectViewRender: LayoutPropsType['hoverRectViewRender'] = (hoverViewProps) => {\n    const { hoverRectViewRender } = this.state;\n\n    const Comp = hoverRectViewRender;\n\n    if (!Comp) {\n      return <></>;\n    }\n    return (\n      <Comp\n        node={hoverViewProps.instance._NODE_MODEL}\n        componentInstance={hoverViewProps.instance}\n        componentInstanceIndex={hoverViewProps.index}\n        params={{\n          viewPortal: this.getPortalViewCtx(),\n          context: this.props.pluginCtx,\n          extra: {},\n        }}\n      />\n    );\n  };\n\n  innerDropViewRender: LayoutPropsType['dropViewRender'] = (dropViewProps) => {\n    const { dropViewRender } = this.state;\n\n    const Comp = dropViewRender;\n\n    if (!Comp) {\n      return <></>;\n    }\n    return (\n      <Comp\n        {...(dropViewProps as any)}\n        node={dropViewProps.instance._NODE_MODEL}\n        componentInstance={dropViewProps.instance}\n        componentInstanceIndex={dropViewProps.index}\n        params={{\n          viewPortal: this.getPortalViewCtx(),\n          context: this.props.pluginCtx,\n          extra: {},\n        }}\n      />\n    );\n  };\n\n  render() {\n    const { layoutRef, props, onSelectNode, onDragStart, onHoverNode, onNodeDrop, onNodeDragging, onNodeDragEnd } =\n      this;\n    const {\n      pageModel,\n      hoverToolbarView,\n      selectToolbarView,\n      ghostView,\n      portalView,\n      selectRectViewRender,\n      hoverRectViewRender,\n      dropViewRender,\n    } = this.state;\n\n    const { pluginCtx } = props;\n    const assets = pluginCtx.assetsPackageListManager.getList() || ([] as AssetPackage[]);\n\n    const renderJSUrl = pluginCtx.engine.props.renderJSUrl || './render.umd.js';\n    const advanceCustomProps: LayoutPropsType = {};\n\n    if (selectRectViewRender) {\n      advanceCustomProps.selectRectViewRender = this.innerSelectRectViewRender;\n    }\n\n    if (hoverRectViewRender) {\n      advanceCustomProps.hoverRectViewRender = this.innerHoverRectViewRender;\n    }\n\n    if (dropViewRender) {\n      advanceCustomProps.dropViewRender = this.innerDropViewRender;\n    }\n    if (selectToolbarView) {\n      advanceCustomProps.selectToolbarView = this.state.selectToolbarView;\n    }\n\n    return (\n      <>\n        <Layout\n          beforeInitRender={props.pluginCtx.config.beforeInitRender}\n          customRender={props.pluginCtx.config.customRender}\n          ref={layoutRef}\n          pageModel={pageModel}\n          renderJSUrl={renderJSUrl}\n          {...props}\n          hoverToolBarView={hoverToolbarView}\n          selectBoxStyle={{}}\n          hoverBoxStyle={{}}\n          nodeCanDrag={this.nodeCanDrag}\n          nodeCanDrop={this.nodeCanDrop}\n          onSelectNode={onSelectNode}\n          onNodeDragStart={onDragStart}\n          onHoverNode={onHoverNode}\n          onNodeDrop={onNodeDrop}\n          onNodeDragging={onNodeDragging}\n          onNodeDraEnd={onNodeDragEnd}\n          {...advanceCustomProps}\n          ghostView={ghostView}\n          assets={assets}\n          pluginCtx={this.props.pluginCtx}\n        />\n        {portalView && createPortal(portalView, document.body)}\n      </>\n    );\n  }\n}\n"
  },
  {
    "path": "packages/engine/src/plugins/Designer/components/Canvas/style.module.scss",
    "content": ".hoverTips {\n  display: inline-block;\n  color: $baseColor;\n  opacity: 0.9;\n  font-size: 12px;\n  margin-bottom: 2px;\n  background-color: white;\n  padding: 1px 3px;\n  border-radius: 2px;\n}\n"
  },
  {
    "path": "packages/engine/src/plugins/Designer/components/DefaultSelectToolBar/index.tsx",
    "content": "/* eslint-disable react-refresh/only-export-components */\nimport React, { useMemo, useState } from 'react';\nimport { CopyOutlined, DeleteOutlined, EyeInvisibleOutlined } from '@ant-design/icons';\nimport styles from './style.module.scss';\nimport { CNode } from '@chamn/model';\nimport clsx from 'clsx';\n\nexport type DefaultSelectToolBarProps = {\n  nodeList: CNode[];\n  toSelectNode: (id: string) => void;\n  toDelete: (id: string) => void;\n  toCopy: (idd: string) => void;\n  toHidden: (idd: string) => void;\n};\n\nconst LayoutSelect = ({\n  dataSource,\n  children,\n  onSelect,\n}: {\n  dataSource: { key: string; label: string }[];\n  children: React.ReactNode;\n  onSelect: (key: string) => void;\n}) => {\n  const [hover, setHover] = useState(false);\n  return (\n    <div className={styles.layoutSelectBox}>\n      <div className={clsx([styles.hoverBox, hover && styles.hoverBoxActive])}>\n        {dataSource.map((el) => {\n          return (\n            <div className={styles.hoverItem} key={el.key} onClick={() => onSelect(el.key)}>\n              {el.label}\n            </div>\n          );\n        })}\n      </div>\n      <div className={styles.placeholder} onMouseOver={() => setHover(true)} onMouseOut={() => setHover(false)}>\n        {children}\n      </div>\n    </div>\n  );\n};\n\nexport const getDefaultToolbarItem = (props: DefaultSelectToolBarProps) => {\n  const { nodeList, toSelectNode, toDelete, toCopy, toHidden } = props;\n  const tempList = [...nodeList];\n  const currentNode = tempList.shift();\n  const parentNodeItems = tempList.map((el) => {\n    const node = el;\n    const nodeMeta = node.materialsModel.findByComponentName(node.value.componentName)?.value.title;\n    const label = node.value.title || nodeMeta || node.value.componentName || '';\n    return {\n      key: el.id,\n      label: label || 'Empty',\n    };\n  });\n\n  const copyItem = (\n    <div className={styles.item} onClick={() => toCopy(currentNode?.id || '')} key={'CopyOutlined'}>\n      <CopyOutlined />\n    </div>\n  );\n\n  const deleteItem = (\n    <div className={styles.item} onClick={() => toDelete(currentNode?.id || '')} key={'DeleteOutlined'}>\n      <DeleteOutlined />\n    </div>\n  );\n\n  const visibleItem = (\n    <div className={styles.item} onClick={() => toHidden(currentNode?.id || '')} key={'EyeInvisibleOutlined'}>\n      <EyeInvisibleOutlined />\n    </div>\n  );\n\n  const nodeLayout = (\n    <LayoutSelect dataSource={parentNodeItems.reverse()} onSelect={toSelectNode} key={'LayoutSelect'}>\n      <div>{currentNode?.value.title || currentNode?.material?.value.title || 'Empty'}</div>\n    </LayoutSelect>\n  );\n\n  return {\n    map: { copyItem, deleteItem, visibleItem, nodeLayout },\n    list: [nodeLayout, visibleItem, copyItem, deleteItem],\n  };\n};\n\nexport const DefaultSelectToolBar = (props: DefaultSelectToolBarProps) => {\n  const { copyItem, deleteItem, visibleItem, nodeLayout } = useMemo(() => {\n    return getDefaultToolbarItem(props).map;\n  }, [props]);\n\n  return (\n    <div className={styles.toolBarBox}>\n      {nodeLayout}\n      {visibleItem}\n      {copyItem}\n      {deleteItem}\n    </div>\n  );\n};\n"
  },
  {
    "path": "packages/engine/src/plugins/Designer/components/DefaultSelectToolBar/style.module.scss",
    "content": ".toolBarBox {\n  display: flex;\n  margin-bottom: 3px;\n  margin-right: -2px;\n  pointer-events: all;\n  float: right;\n}\n\n.item {\n  background-color: $baseColor;\n  color: white;\n  padding: 2px 3px;\n  font-size: 12px;\n  overflow: hidden;\n  margin-left: 1px;\n  cursor: pointer;\n}\n\n.layoutSelectBox {\n  position: relative;\n  margin-right: 1px;\n  height: 21px;\n\n  .hoverBox {\n    transition: all 0.3s;\n    position: absolute;\n    left: 0;\n    top: 0;\n    white-space: nowrap;\n    opacity: 0;\n    transform: translateY(-100%);\n    pointer-events: none;\n\n    &:hover,\n    &.hoverBoxActive {\n      opacity: 1;\n      pointer-events: all;\n    }\n\n    .hoverItem {\n      background-color: $baseColor;\n      padding: 2px 3px;\n      color: white;\n      font-size: 12px;\n      margin-bottom: 1px;\n      cursor: pointer;\n    }\n  }\n  .placeholder {\n    overflow: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n    background-color: $baseColor;\n    color: white;\n    padding: 2px 5px;\n    font-size: 12px;\n    max-width: 100px;\n  }\n}\n"
  },
  {
    "path": "packages/engine/src/plugins/Designer/components/GhostView/index.tsx",
    "content": "import React from 'react';\nimport { CNode, CRootNode } from '@chamn/model';\n\nexport const GhostView = ({ node }: { node: CNode | CRootNode }) => {\n  return (\n    <div\n      style={{\n        backgroundColor: 'rgba(100,100,100)',\n        padding: '3px 10px 3px 15px',\n        borderRadius: '2px',\n        opacity: 0.9,\n        color: 'rgba(220,220,220)',\n      }}\n    >\n      {node.value.componentName}\n    </div>\n  );\n};\n"
  },
  {
    "path": "packages/engine/src/plugins/Designer/config.ts",
    "content": "export const PLUGIN_NAME = 'Designer';\n"
  },
  {
    "path": "packages/engine/src/plugins/Designer/index.tsx",
    "content": "import React from 'react';\nimport { CPage, CPageDataType } from '@chamn/model';\n\nimport { PLUGIN_NAME } from './config';\nimport { Designer } from './components/Canvas';\nimport { DesignerPluginType } from './type';\nimport { LayoutMode } from '@chamn/layout';\n\nexport const DesignerPlugin: DesignerPluginType = () => {\n  const designerRef = React.createRef<Designer>();\n  return {\n    name: PLUGIN_NAME,\n    PLUGIN_NAME,\n    async init(ctx) {\n      ctx.name = PLUGIN_NAME;\n      const workbench = ctx.getWorkbench();\n      workbench.replaceBodyView(<Designer ref={designerRef} pluginCtx={ctx} />);\n    },\n    async destroy(ctx) {\n      console.log('destroy', ctx);\n    },\n    export: () => {\n      return {\n        /** 设置 canvas 的渲染模式 */\n        setMode(newMode) {\n          return designerRef.current?.layoutRef.current?.setMode(newMode);\n        },\n        setPreviewMode() {\n          return designerRef.current?.layoutRef.current?.setMode(LayoutMode.PREVIEW);\n        },\n        setEditMode() {\n          return designerRef.current?.layoutRef.current?.setMode(LayoutMode.EDIT);\n        },\n        setCanvasWidth(width: number | string) {\n          const iframeContainer = designerRef.current?.getIframeDom();\n\n          if (iframeContainer?.containerDom) {\n            let newW = width;\n            if (typeof width === 'number') {\n              newW = `${width}px`;\n            }\n            iframeContainer.containerDom.style.width = String(newW);\n            iframeContainer.containerDom.style.margin = '0 auto';\n          }\n        },\n        getIframeDom() {\n          return designerRef.current?.getIframeDom();\n        },\n        getInstance: () => {\n          return designerRef.current;\n        },\n        getDnd: () => {\n          return designerRef.current?.layoutRef.current?.dnd;\n        },\n        selectNode: async (nodeId) => {\n          return await designerRef.current?.toSelectNode(nodeId);\n        },\n        copyNode: async (nodeId) => {\n          return await designerRef.current?.toCopyNode(nodeId);\n        },\n        deleteNode: async (nodeId) => {\n          return await designerRef.current?.toDeleteNode(nodeId);\n        },\n        getSelectedNodeId: () => {\n          return designerRef.current?.layoutRef.current?.state.currentSelectId;\n        },\n        updatePage: (page: CPageDataType | CPage) => {\n          designerRef.current?.layoutRef.current?.designRenderRef?.current?.rerender(page);\n        },\n        reload: () => {\n          designerRef.current?.reloadRender();\n        },\n        getComponentInstances: (id: string) => {\n          return designerRef.current?.layoutRef.current?.designRenderRef.current?.getInstancesById(id) || [];\n        },\n        getDynamicComponentInstances: (id: string) => {\n          const map =\n            designerRef.current?.layoutRef.current?.designRenderRef.current?.renderRef.current\n              ?.dynamicComponentInstanceMap;\n          return map?.get(id) || ([] as any);\n        },\n        getLayoutRef: () => {\n          return designerRef.current?.layoutRef as any;\n        },\n        getDesignerWindow: () => {\n          return designerRef.current?.layoutRef.current?.iframeContainer.getWindow() as any;\n        },\n        updateRenderComponents: (newComponents: Record<string, string>) => {\n          return designerRef.current?.updateRenderComponents(newComponents);\n        },\n      };\n    },\n    meta: {\n      engine: {\n        version: '1.0.0',\n      },\n    },\n  };\n};\n\nDesignerPlugin.PLUGIN_NAME = PLUGIN_NAME;\n"
  },
  {
    "path": "packages/engine/src/plugins/Designer/localize/en_US/index.ts",
    "content": "export const en_US: Record<string, any> = {\n  pluginName: 'Designer Canvas',\n};\n"
  },
  {
    "path": "packages/engine/src/plugins/Designer/localize/index.ts",
    "content": "import { zh_CN } from './zh_CN';\nimport { en_US } from './en_US';\nexport default { zh_CN, en_US } as Record<string, any>;\n"
  },
  {
    "path": "packages/engine/src/plugins/Designer/localize/zh_CN/index.ts",
    "content": "export const zh_CN: Record<string, any> = {\n  pluginName: '组件库',\n};\n"
  },
  {
    "path": "packages/engine/src/plugins/Designer/type.ts",
    "content": "import { CPlugin, PluginInstance } from '@/core/pluginManager';\nimport { DragAndDrop, IFrameContainer, Layout, LayoutMode, LayoutPropsType } from '@chamn/layout';\nimport { AdvanceCustom, AssetPackage, CPageDataType } from '@chamn/model';\nimport { RenderInstance, RenderPropsType } from '@chamn/render';\nimport { Designer } from './components/Canvas';\n\nexport type DesignerExport = {\n  reload: (params?: { assets?: AssetPackage[] }) => void;\n  getInstance: () => Designer | null;\n  getDnd: () => DragAndDrop | undefined;\n  selectNode: (nodeId: string) => Promise<boolean | undefined>;\n  copyNode: (nodeId: string) => Promise<boolean | undefined>;\n  deleteNode: (nodeId: string) => Promise<boolean | undefined>;\n  getSelectedNodeId: () => string | undefined;\n  updatePage: (page: CPageDataType) => void;\n  getComponentInstances: (id: string) => RenderInstance[];\n  getDynamicComponentInstances: (id: string) => RenderInstance;\n  getLayoutRef: () => React.RefObject<Layout>;\n  getDesignerWindow: () => Window | null;\n  getIframeDom: () => IFrameContainer | undefined;\n  updateRenderComponents: (newComponentMap: Record<string, string>) => void;\n  /** set canvas width, width must below visible area width*/\n  setCanvasWidth: (width: number | string) => void;\n  setMode: (mode: LayoutMode) => void;\n  setPreviewMode: () => void;\n  setEditMode: () => void;\n};\n\nexport type DesignerPluginConfig = Omit<\n  LayoutPropsType,\n  | 'selectRectViewRender'\n  | 'hoverRectViewRender'\n  | 'dropViewRender'\n  | 'ghostView'\n  | 'selectToolBarView'\n  | 'hoverToolBarView'\n> &\n  Pick<\n    AdvanceCustom,\n    'selectRectViewRender' | 'hoverRectViewRender' | 'dropViewRender' | 'ghostViewRender' | 'toolbarViewRender'\n  > & {\n    renderProps?: Partial<RenderPropsType>;\n  };\nexport type DesignerPluginType = CPlugin<DesignerPluginConfig, DesignerExport>;\nexport type DesignerPluginInstance = PluginInstance<DesignerPluginConfig, DesignerExport>;\n"
  },
  {
    "path": "packages/engine/src/plugins/Designer/util.ts",
    "content": "import { CNode, CPage, CProp, CRootNode, CSlot, isNodeModel } from '@chamn/model';\n\nexport const getClosestNodeList = (node: CNode | CRootNode, level = 5) => {\n  const res = [];\n  let count = 0;\n  let currentNode: CNode | CRootNode | CSlot | CProp | CPage | null = node;\n\n  while (count < level && currentNode) {\n    if (isNodeModel(currentNode)) {\n      res.push(currentNode);\n      count++;\n    }\n\n    currentNode = currentNode.parent || null;\n  }\n\n  return res;\n};\n"
  },
  {
    "path": "packages/engine/src/plugins/DisplaySourceSchema/index.tsx",
    "content": "import React, { useRef, useState } from 'react';\nimport { Modal } from 'antd';\nimport { MonacoEditor, MonacoEditorInstance } from '../../component/MonacoEditor';\nimport { CPage } from '@chamn/model';\nimport { waitReactUpdate } from '../../utils';\nimport { DesignerPluginInstance } from '../Designer/type';\nimport { EnginContext } from '@/type';\n\nexport type DisplaySourceSchemaProps = {\n  pageModel: CPage;\n  engineCtx: EnginContext;\n  children: React.ReactNode;\n};\n\nexport const DisplaySourceSchema = (props: DisplaySourceSchemaProps) => {\n  const initialValue = props.pageModel.export();\n  const { engineCtx } = props;\n  const [open, setOpen] = useState(false);\n  const editorRef = useRef<MonacoEditorInstance | null>(null);\n  return (\n    <>\n      <div\n        onClick={() => {\n          setOpen(true);\n        }}\n      >\n        {props.children}\n      </div>\n      <Modal\n        open={open}\n        title=\"Source Schema\"\n        width={'100%'}\n        onCancel={() => setOpen(false)}\n        onOk={async () => {\n          setOpen(false);\n          const newPage = editorRef.current?.getValue();\n          if (!newPage) {\n            return;\n          }\n          const newPageJSON = JSON.parse(newPage);\n          props.pageModel.updatePage(newPageJSON);\n          await waitReactUpdate();\n          const currentSelectNode = engineCtx.engine.getActiveNode();\n          const designerPluginInstance = await engineCtx.pluginManager.get<DesignerPluginInstance>('Designer');\n          const nodeId = currentSelectNode?.id || '';\n          designerPluginInstance?.ctx.emitter.on('ready', () => {\n            const designerExport = designerPluginInstance.export;\n            designerExport.selectNode(nodeId);\n          });\n          if (designerPluginInstance) {\n            const designerExport = designerPluginInstance.export;\n            designerExport.selectNode(nodeId);\n          }\n        }}\n        style={{\n          height: 'calc(100vh - 50px)',\n          top: '25px',\n        }}\n        destroyOnClose\n      >\n        <div style={{ width: '100%', height: 'calc(100vh - 200px)' }}>\n          <MonacoEditor\n            initialValue={JSON.stringify(initialValue, null, 2)}\n            language={'json'}\n            options={{\n              automaticLayout: true,\n            }}\n            onDidMount={(editor) => {\n              editorRef.current = editor;\n            }}\n          />\n        </div>\n      </Modal>\n    </>\n  );\n};\n"
  },
  {
    "path": "packages/engine/src/plugins/EventPanel/index.tsx",
    "content": "import { CRightPanelItem } from '../RightPanel/view';\nimport { EventPanel } from './panel';\n\nexport const EventPanelConfig: CRightPanelItem = {\n  key: 'Event',\n  name: 'Event',\n  view: ({ node, pluginCtx }) => {\n    if (!node) {\n      return <></>;\n    }\n    return <EventPanel node={node} pluginCtx={pluginCtx} />;\n  },\n  show: (props) => {\n    return props.node?.material?.value.advanceCustom?.rightPanel?.advance !== false;\n  },\n};\n"
  },
  {
    "path": "packages/engine/src/plugins/EventPanel/panel.tsx",
    "content": "import { useEffect, useState } from 'react';\nimport { CNode, CRootNode } from '@chamn/model';\nimport { CPluginCtx } from '../../core/pluginManager';\nimport styles from './style.module.scss';\nimport { Button, Select } from 'antd';\nimport { ActionFlowSetter } from '@/component/CustomSchemaForm/components/Setters/ActionFlowSetter';\nimport { DeleteOutlined } from '@ant-design/icons';\nimport { ON_DID_RENDER, ON_WILL_DESTROY } from '@chamn/render';\n\nexport type EventPanelProps = {\n  node: CNode | CRootNode | null;\n  pluginCtx: CPluginCtx;\n};\n\nconst INNER_EVENT_LIST_MAP = [\n  {\n    label: '初始化完成后',\n    value: ON_DID_RENDER,\n  },\n  {\n    label: '组件销毁之前',\n    value: ON_WILL_DESTROY,\n  },\n];\n\nexport const EventPanel = (props: EventPanelProps) => {\n  const { node } = props;\n  const [eventList, updateEventList] = useState<\n    {\n      label: string;\n      value: string;\n    }[]\n  >([]);\n\n  const nodeEventList = node?.value.eventListener;\n\n  useEffect(() => {\n    const list =\n      node?.material?.value.events?.map((evt) => {\n        if (typeof evt === 'string') {\n          return {\n            label: evt,\n            value: evt,\n          };\n        }\n        return {\n          label: evt.name || evt.event,\n          value: evt.event,\n        };\n      }) || [];\n    updateEventList([...INNER_EVENT_LIST_MAP, ...list]);\n  }, [node?.id, node?.material?.value.events]);\n  const [currentEvent, updateCurrentEvent] = useState<string>();\n\n  const onChange = (value: string) => {\n    updateCurrentEvent(value);\n  };\n\n  if (!node) {\n    return <></>;\n  }\n\n  return (\n    <div className={styles.eventBox}>\n      <div style={{ display: 'flex' }}>\n        <Select\n          value={currentEvent}\n          showSearch\n          placeholder=\"Select a person\"\n          optionFilterProp=\"label\"\n          onChange={onChange}\n          allowClear\n          style={{ width: '100%' }}\n          options={eventList}\n        />\n        <Button\n          type=\"primary\"\n          style={{ marginLeft: '10px' }}\n          onClick={() => {\n            const newEvent = currentEvent;\n            if (!newEvent) {\n              return;\n            }\n            node.value.eventListener = [\n              ...(node.value.eventListener || []),\n              {\n                name: newEvent,\n                func: {\n                  type: 'ACTION',\n                  handler: [],\n                  // TODO: 待实现\n                  params: [],\n                },\n              },\n            ];\n            node.updateValue();\n            updateCurrentEvent(undefined);\n          }}\n        >\n          Add\n        </Button>\n      </div>\n      <div style={{ marginTop: '16px' }}>\n        {nodeEventList?.map((event: any, index: number) => {\n          const eventLabel = eventList.find((el) => el.value === event.name);\n          return (\n            <div\n              key={index}\n              style={{\n                padding: '8px 12px',\n                borderRadius: '4px',\n                backgroundColor: '#f5f5f5',\n                marginBottom: '8px',\n                display: 'flex',\n                justifyContent: 'space-between',\n                alignItems: 'center',\n                cursor: 'pointer',\n              }}\n            >\n              <div\n                style={{\n                  width: '100%',\n                }}\n              >\n                <ActionFlowSetter\n                  value={event.func}\n                  onValueChange={(val: { handler: any }) => {\n                    event.func.handler = val.handler;\n                    node?.updateValue();\n                  }}\n                  setterContext={{\n                    pluginCtx: props.pluginCtx,\n                    onSetterChange: () => {},\n                    keyPaths: [''],\n                    label: '',\n                    nodeModel: node as any,\n                  }}\n                >\n                  <span>{eventLabel?.label || event.name}</span>\n                </ActionFlowSetter>\n              </div>\n              <Button\n                type=\"text\"\n                size=\"small\"\n                danger\n                icon={<DeleteOutlined />}\n                onClick={(e) => {\n                  e.stopPropagation();\n                  const newEventList = [...(node?.value.eventListener || [])];\n                  newEventList.splice(index, 1);\n                  node!.value.eventListener = newEventList;\n                  node?.updateValue();\n                }}\n              />\n            </div>\n          );\n        })}\n      </div>\n    </div>\n  );\n};\n"
  },
  {
    "path": "packages/engine/src/plugins/EventPanel/style.module.scss",
    "content": ".eventBox {\n  padding: 0 20px;\n  overflow-x: hidden;\n  overflow-y: auto;\n  :global {\n    .ant-input-affix-wrapper .ant-input-prefix {\n      margin-inline-end: 0;\n    }\n  }\n}\n"
  },
  {
    "path": "packages/engine/src/plugins/GlobalStatePanel/index.tsx",
    "content": "/* eslint-disable react-refresh/only-export-components */\nimport { useEffect, useRef } from 'react';\nimport { DatabaseOutlined } from '@ant-design/icons';\nimport { CPlugin, CPluginCtx } from '../../core/pluginManager';\nimport { withTranslation } from 'react-i18next';\nimport localize from './localize';\nimport { MonacoEditor, MonacoEditorInstance } from '../../component/MonacoEditor';\nimport styles from './style.module.scss';\n\nconst PLUGIN_NAME = 'GlobalState';\nconst i18nNamespace = `plugin:${PLUGIN_NAME}`;\n\ntype GlobalStatePanelProps = {\n  pluginCtx: CPluginCtx;\n};\n\nlet triggerChangeBySelf = false;\n\nconst GlobalStatePanel = (props: GlobalStatePanelProps) => {\n  const { pluginCtx } = props;\n  const rootState = pluginCtx.pageModel.value.componentsTree.value.state || {};\n  // 表示是不是自己触发的值更新\n  const editorRef = useRef<MonacoEditorInstance | null>(null);\n  useEffect(() => {\n    editorRef?.current?.setValue(JSON.stringify(rootState, null, 2));\n    // 正常情况下, 只有 reloadPage  才需要同步数据\n    pluginCtx.pageModel.emitter.on('onReloadPage', () => {\n      if (triggerChangeBySelf) {\n        triggerChangeBySelf = false;\n        return;\n      }\n      editorRef.current?.setValue(JSON.stringify(pluginCtx.pageModel.value.componentsTree.value.state, null, 2));\n    });\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n\n  const onValueChange = (newValStr?: string) => {\n    try {\n      const newVal = JSON.parse(newValStr || '{}');\n      pluginCtx.pageModel.value.componentsTree.value.state = newVal;\n      triggerChangeBySelf = true;\n      pluginCtx.pageModel.value.componentsTree.updateValue();\n    } catch (e) {\n      console.warn(e);\n    }\n  };\n\n  return (\n    <div className={styles.box}>\n      <MonacoEditor\n        initialValue={JSON.stringify(pluginCtx.pageModel.value.componentsTree.value.state, null, 2)}\n        language={'json'}\n        options={{\n          automaticLayout: true,\n          tabSize: 2,\n          minimap: { enabled: false },\n          quickSuggestions: false,\n          suggestOnTriggerCharacters: false,\n          folding: false,\n          comments: {},\n        }}\n        onDidMount={(editor) => {\n          editorRef.current = editor;\n        }}\n        beforeMount={() => {\n          // monaco.languages.json.jsonDefaults.setDiagnosticsOptions({\n          //   validate: true,\n          //   allowComments: true, // 启用注释\n          // });\n        }}\n        onChange={onValueChange}\n      />\n    </div>\n  );\n};\n\nexport const GlobalStatePanelPlugin: CPlugin = {\n  name: PLUGIN_NAME,\n  async init(ctx) {\n    const { i18n } = ctx;\n    Object.keys(localize).forEach((lng) => {\n      i18n.addResourceBundle(lng, i18nNamespace, localize[lng], true, true);\n    });\n\n    const GlobalStatePanelWithLocalize = withTranslation(i18nNamespace)(GlobalStatePanel);\n    const Title = withTranslation(i18nNamespace)(({ t }) => <>{t('pluginName')}</>);\n    const workbench = ctx.getWorkbench();\n    workbench.addLeftPanel({\n      title: <Title />,\n      name: PLUGIN_NAME,\n      icon: <DatabaseOutlined />,\n      render: <GlobalStatePanelWithLocalize pluginCtx={ctx} />,\n    });\n  },\n  async destroy() {},\n  export: () => {\n    return {};\n  },\n  meta: {\n    engine: {\n      version: '1.0.0',\n    },\n  },\n};\n\nGlobalStatePanelPlugin.PLUGIN_NAME = PLUGIN_NAME;\n"
  },
  {
    "path": "packages/engine/src/plugins/GlobalStatePanel/localize/en_US/index.ts",
    "content": "export const en_US: Record<string, any> = {\n  pluginName: 'Global State',\n};\n"
  },
  {
    "path": "packages/engine/src/plugins/GlobalStatePanel/localize/index.ts",
    "content": "import { zh_CN } from './zh_CN';\nimport { en_US } from './en_US';\nexport default { zh_CN, en_US } as Record<string, any>;\n"
  },
  {
    "path": "packages/engine/src/plugins/GlobalStatePanel/localize/zh_CN/index.ts",
    "content": "export const zh_CN: Record<string, any> = {\n  pluginName: '全局状态',\n};\n"
  },
  {
    "path": "packages/engine/src/plugins/GlobalStatePanel/style.module.scss",
    "content": ".box {\n  height: 100%;\n  width: 100%;\n  padding-right: 10px;\n  border-top: 1px solid $borderColor;\n  padding-top: 10px;\n  background-color: white;\n}\n"
  },
  {
    "path": "packages/engine/src/plugins/History/index.tsx",
    "content": "import { waitReactUpdate } from '@/utils';\nimport { CPageDataType } from '@chamn/model';\nimport { cloneDeep, debounce } from 'lodash-es';\nimport { CPluginCtx } from '../../core/pluginManager';\nimport { HistoryPluginType } from './type';\n\nconst PLUGIN_NAME = 'History' as const;\n\nexport const HistoryPlugin: HistoryPluginType = (ctx) => {\n  const CTX: CPluginCtx | null = ctx;\n  const dataStore = {\n    historyRecords: [] as CPageDataType[],\n    currentStepIndex: 0,\n  };\n\n  let originalPageRecord: CPageDataType | null = null;\n  const pageSchema = ctx.pageModel.export();\n  originalPageRecord = pageSchema;\n  dataStore.historyRecords.push(pageSchema);\n\n  const loadPage = async (page: CPageDataType) => {\n    if (!CTX) {\n      return;\n    }\n    CTX.pageModel.reloadPage(page);\n    await waitReactUpdate();\n  };\n\n  const resObj = {\n    addStep: () => {\n      const { currentStepIndex, historyRecords } = dataStore;\n      const newPage = ctx.pageModel.export();\n      if (currentStepIndex !== historyRecords.length - 1) {\n        dataStore.historyRecords = historyRecords.slice(0, currentStepIndex + 1);\n      }\n      dataStore.historyRecords.push(newPage);\n      dataStore.currentStepIndex = historyRecords.length - 1;\n    },\n    reset: async () => {\n      const ctx = CTX;\n      if (!ctx) {\n        console.warn('plugin ctx is null, pls check it');\n        return;\n      }\n      if (!originalPageRecord) {\n        return;\n      }\n      dataStore.historyRecords = [];\n      loadPage(originalPageRecord);\n    },\n    preStep: () => {\n      const { currentStepIndex, historyRecords } = dataStore;\n      if (!resObj.canGoPreStep()) {\n        return;\n      }\n      const newIndex = currentStepIndex - 1;\n      dataStore.currentStepIndex = newIndex;\n      const page = cloneDeep(historyRecords[newIndex]);\n      loadPage(page);\n    },\n    nextStep: () => {\n      if (!resObj.canGoNextStep()) {\n        return;\n      }\n      const { currentStepIndex, historyRecords } = dataStore;\n      const newIndex = currentStepIndex + 1;\n      dataStore.currentStepIndex = newIndex;\n      const page = cloneDeep(historyRecords[newIndex]);\n      return loadPage(page);\n    },\n    canGoPreStep: () => {\n      const { currentStepIndex } = dataStore;\n      if (currentStepIndex <= 0) {\n        return false;\n      }\n      return true;\n    },\n    canGoNextStep: () => {\n      const { currentStepIndex, historyRecords } = dataStore;\n      if (currentStepIndex >= historyRecords.length - 1) {\n        return false;\n      }\n      return true;\n    },\n  };\n\n  const debounceAddStep = debounce(() => {\n    resObj.addStep();\n  }, 500);\n\n  return {\n    name: PLUGIN_NAME,\n    PLUGIN_NAME,\n    async init(ctx) {\n      ctx.pageModel.emitter.on('onNodeChange', () => {\n        debounceAddStep();\n      });\n      ctx.pageModel.emitter.on('onPageChange', () => {\n        resObj.addStep();\n      });\n      ctx.pluginReadyOk();\n    },\n    async destroy(ctx) {\n      console.log('destroy', ctx);\n    },\n    export: () => {\n      return resObj;\n    },\n    meta: {\n      engine: {\n        version: '1.0.0',\n      },\n    },\n  };\n};\n\nHistoryPlugin.PLUGIN_NAME = PLUGIN_NAME;\n"
  },
  {
    "path": "packages/engine/src/plugins/History/type.ts",
    "content": "import { CPlugin, PluginInstance } from '@/core/pluginManager';\n\nexport type HistoryExport = {\n  addStep: () => void;\n  reset: () => Promise<void>;\n  preStep: () => void;\n  nextStep: () => Promise<void> | undefined;\n  canGoPreStep: () => boolean;\n  canGoNextStep: () => boolean;\n};\n\nexport type HistoryPluginConfig = any;\nexport type HistoryPluginType = CPlugin<HistoryPluginConfig, HistoryExport>;\nexport type HistoryPluginInstance = PluginInstance<HistoryPluginConfig, HistoryExport>;\n"
  },
  {
    "path": "packages/engine/src/plugins/Hotkeys/action.ts",
    "content": "import { CPluginCtx } from '@/core/pluginManager';\nimport { DesignerPluginInstance } from '../Designer/type';\nimport { CNode } from '@chamn/model';\nimport { HistoryPluginInstance } from '../History/type';\nimport { message } from 'antd';\n\nexport const actionMap = {\n  deleteNode: async (ctx: CPluginCtx) => {\n    const designer = await ctx.pluginManager.get<DesignerPluginInstance>('Designer');\n    const activeNode = ctx.engine.getActiveNode();\n    if (activeNode) {\n      const flag = await designer?.export?.deleteNode(activeNode.id);\n      if (!flag) {\n        message.error('该节点不能删除');\n      }\n      return flag;\n    }\n  },\n  copyNode: async (ctx: CPluginCtx) => {\n    const designer = await ctx.pluginManager.get<DesignerPluginInstance>('Designer');\n    const activeNode = ctx.engine.getActiveNode();\n    if (activeNode) {\n      const flag = await designer?.export?.copyNode(activeNode.id);\n      return flag;\n    }\n  },\n  moveToUp: async (ctx: CPluginCtx) => {\n    const res = await actionMap.moveToSiblingUp(ctx);\n    if (res) {\n      return;\n    }\n    const designer = await ctx.pluginManager.get<DesignerPluginInstance>('Designer');\n    const activeNode = ctx.engine.getActiveNode();\n    const parentNode = activeNode?.parent;\n    if (parentNode && parentNode.nodeType !== 'PAGE') {\n      const flag = await designer?.export?.selectNode(parentNode.id);\n      return flag;\n    }\n  },\n  moveToSiblingUp: async (ctx: CPluginCtx) => {\n    const designer = await ctx.pluginManager.get<DesignerPluginInstance>('Designer');\n    const activeNode = ctx.engine.getActiveNode();\n    const parentNode = activeNode?.parent;\n    if (parentNode?.nodeType === 'PAGE' || !activeNode) {\n      return;\n    }\n    const parentNodeValue = parentNode?.value as any;\n    const children: CNode[] = parentNodeValue.children || parentNodeValue.value;\n    const targetIndex = children.findIndex((el) => el.id === activeNode.id);\n    const nextIndex = targetIndex - 1;\n    if (nextIndex < 0) {\n      return;\n    }\n    const child = children?.[nextIndex] as CNode;\n    if (child && (child as any).id) {\n      return await designer?.export?.selectNode(child.id);\n    }\n  },\n  moveToDown: async (ctx: CPluginCtx) => {\n    const designer = await ctx.pluginManager.get<DesignerPluginInstance>('Designer');\n    const activeNode = ctx.engine.getActiveNode();\n    const children = activeNode?.value.children;\n    const child = children?.[0] as CNode;\n    if (child && (child as any).id) {\n      return await designer?.export?.selectNode(child.id);\n    }\n\n    return await actionMap.moveToSiblingDown(ctx);\n  },\n  moveToSiblingDown: async (ctx: CPluginCtx) => {\n    const designer = await ctx.pluginManager.get<DesignerPluginInstance>('Designer');\n\n    const activeNode = ctx.engine.getActiveNode();\n    const parentNode = activeNode?.parent;\n    if (parentNode?.nodeType === 'PAGE' || !activeNode) {\n      return;\n    }\n    const parentNodeValue = parentNode?.value as any;\n    const children: CNode[] = parentNodeValue.children || parentNodeValue.value;\n    const targetIndex = children.findIndex((el) => el.id === activeNode.id);\n    const nextIndex = targetIndex + 1;\n    if (nextIndex >= children.length) {\n      return;\n    }\n    const child = children?.[nextIndex] as CNode;\n    if (child && (child as any).id) {\n      return await designer?.export?.selectNode(child.id);\n    }\n  },\n  redo: async (ctx: CPluginCtx) => {\n    const historyStack = await ctx.pluginManager.get<HistoryPluginInstance>('History');\n    if (historyStack?.export.canGoNextStep()) {\n      historyStack?.export.nextStep();\n      return true;\n    }\n  },\n  undo: async (ctx: CPluginCtx) => {\n    const historyStack = await ctx.pluginManager.get<HistoryPluginInstance>('History');\n    if (historyStack?.export.canGoPreStep()) {\n      historyStack?.export.preStep();\n      return true;\n    }\n  },\n};\n"
  },
  {
    "path": "packages/engine/src/plugins/Hotkeys/hotKeyManager.ts",
    "content": "import { defaultGetCode, getKeyString } from './keymap';\n\nexport class HotKeysManager {\n  private splitStr = '_';\n  // 按下的键盘按键列表\n  private downKeyCodeList: number[] = [];\n  private elements: HTMLElement[];\n  private disposeEventCbList: (() => void)[] = [];\n\n  /** 是否禁用热键 */\n  private disable: boolean = false;\n\n  /** 快捷操作按键记录 */\n  private hotActionMap: Record<string, () => void> = {};\n\n  constructor(options: { elements: HTMLElement[]; customGetKeyCodeByEvent?: (event: KeyboardEvent) => number }) {\n    this.elements = options.elements;\n    this.init();\n  }\n\n  setDisable(status: boolean) {\n    this.disable = status;\n  }\n\n  init() {\n    // 处理 keyup , 移除按键记录\n    const disposeListCb = this.elements.map((el) => this.registerKeyEvent(el));\n    this.disposeEventCbList = [...disposeListCb];\n  }\n\n  addElement(el: HTMLElement) {\n    const disposeCb = this.registerKeyEvent(el);\n    this.disposeEventCbList.push(disposeCb);\n  }\n\n  registerKeyEvent(el: HTMLElement) {\n    const triggerAction = this.getTriggerHotkeyDebounce();\n    // 收集所有的 keys\n    const keydownCb = (event: KeyboardEvent) => {\n      // 表单控件过滤 默认表单控件不触发快捷键\n      if (this.filterInputElement(event)) return;\n\n      const key = event.keyCode || event.which || event.charCode;\n      if (!this.downKeyCodeList.includes(key)) {\n        this.downKeyCodeList.push(key);\n      }\n      triggerAction();\n    };\n    el?.addEventListener('keydown', keydownCb);\n\n    const keyupCb = (event: KeyboardEvent) => {\n      setTimeout(() => {\n        // 表单控件过滤 默认表单控件不触发快捷键\n        if (this.filterInputElement(event)) return;\n\n        const key = event.keyCode || event.which || event.charCode;\n\n        const findKeyIndex = this.downKeyCodeList.findIndex((el) => el === key);\n        if (findKeyIndex >= 0) {\n          this.downKeyCodeList.splice(findKeyIndex, 1);\n        }\n\n        // 用户释放了所有的按键\n      }, 0);\n    };\n    el?.addEventListener('keyup', keyupCb);\n\n    const clearKeyDownList = () => {\n      this.downKeyCodeList = [];\n    };\n    // 修正某些意外情况下，文档失焦，导致快捷键失效等情况\n    window?.addEventListener('blur', clearKeyDownList);\n\n    return () => {\n      el.removeEventListener('keydown', keydownCb);\n      el.removeEventListener('keyup', keyupCb);\n      window?.removeEventListener('blur', clearKeyDownList);\n    };\n  }\n\n  /** 添加快捷操作 */\n  addHotAction(keys: (number | string)[], cb: () => void) {\n    const newKeysCode = keys.map((el) => {\n      if (typeof el !== 'number') {\n        return this.getKeyCodeByLabel(el);\n      } else {\n        return el;\n      }\n    });\n    this.hotActionMap[newKeysCode.join(this.splitStr)] = () => {\n      // 可以自做一些拦截操作\n      if (this.disable) {\n        return;\n      }\n      cb();\n    };\n  }\n\n  triggerHotKey() {\n    const hotActionId = this.downKeyCodeList.join(this.splitStr);\n    // 本次快捷操作回合已经触发过，跳过触发\n    const cb = this.hotActionMap[hotActionId];\n\n    cb?.();\n  }\n\n  /**\n   * @param time ms\n   * @returns\n   */\n  getTriggerHotkeyDebounce() {\n    return this.triggerHotKey.bind(this);\n  }\n\n  /** 根据可识别的字符串获取对应的键码 */\n  getKeyCodeByLabel(label: string) {\n    return defaultGetCode(label);\n  }\n\n  getKeyString(code: number) {\n    return getKeyString(code);\n  }\n\n  /**\n   * 表单控件控件判断\n   * 如果是编辑控件返回 true\n   * hotkey is effective only when filter return true\n   * @param event\n   * @returns\n   */\n  filterInputElement(event: KeyboardEvent) {\n    const target: any = event.target || event.srcElement;\n    if (!target) {\n      return false;\n    }\n    const { tagName } = target as HTMLInputElement;\n    let flag = false;\n    const isInput =\n      tagName === 'INPUT' &&\n      !['checkbox', 'radio', 'range', 'button', 'file', 'reset', 'submit', 'color'].includes(target.type);\n    // ignore: isContentEditable === 'true', <input> and <textarea> when readOnly state is false, <select>\n    if (\n      (target as HTMLDivElement).isContentEditable ||\n      ((isInput || tagName === 'TEXTAREA' || tagName === 'SELECT') && !target.readOnly)\n    ) {\n      flag = true;\n    }\n    return flag;\n  }\n\n  destroy() {\n    this.disposeEventCbList.forEach((el) => el());\n  }\n}\n"
  },
  {
    "path": "packages/engine/src/plugins/Hotkeys/index.ts",
    "content": "import { DesignerPluginInstance } from '../Designer/type';\nimport { HotKeysManager } from './hotKeyManager';\nimport localize from './localize';\nimport { actionMap } from './action';\nimport { HotKeysPluginType } from './type';\n\nconst PLUGIN_NAME = 'Hotkeys' as const;\nconst i18nNamespace = `plugin:${PLUGIN_NAME}`;\n\nexport const HotkeysPlugin: HotKeysPluginType = {\n  name: PLUGIN_NAME,\n  PLUGIN_NAME,\n  async init(ctx) {\n    const { i18n } = ctx;\n    Object.keys(localize).forEach((lng) => {\n      i18n.addResourceBundle(lng, i18nNamespace, localize[lng], true, true);\n    });\n\n    const designer = await ctx.pluginManager.get<DesignerPluginInstance>('Designer');\n\n    const subWin = designer?.export.getDesignerWindow();\n\n    const hotkeyManager = new HotKeysManager({\n      elements: [window.document.body, subWin!.document.body],\n    });\n\n    ctx.engine.pageModel.emitter.on('onReloadPage', () => {\n      const subWin = designer?.export.getDesignerWindow();\n      setTimeout(() => {\n        hotkeyManager.addElement(subWin!.document.body);\n      }, 1000);\n    });\n\n    hotkeyManager.addHotAction(['ctrl', 'c'], () => {\n      actionMap.copyNode(ctx);\n    });\n\n    hotkeyManager.addHotAction(['ctrl', 'z'], () => {\n      actionMap.undo(ctx);\n    });\n\n    hotkeyManager.addHotAction(['ctrl', 'shift', 'z'], () => {\n      actionMap.redo(ctx);\n    });\n\n    hotkeyManager.addHotAction(['up'], () => {\n      actionMap.moveToUp(ctx);\n    });\n\n    hotkeyManager.addHotAction(['w'], () => {\n      actionMap.moveToUp(ctx);\n    });\n\n    hotkeyManager.addHotAction(['shift', 'w'], () => {\n      actionMap.moveToSiblingUp(ctx);\n    });\n\n    hotkeyManager.addHotAction(['down'], () => {\n      actionMap.moveToDown(ctx);\n    });\n\n    hotkeyManager.addHotAction(['s'], () => {\n      actionMap.moveToDown(ctx);\n    });\n\n    hotkeyManager.addHotAction(['shift', 's'], () => {\n      actionMap.moveToSiblingDown(ctx);\n    });\n\n    hotkeyManager.addHotAction(['backspace'], () => {\n      actionMap.deleteNode(ctx);\n    });\n    (ctx as any).hotkeyManager = hotkeyManager;\n    ctx.pluginReadyOk();\n  },\n  async destroy(ctx) {\n    const hotkeyManager: HotKeysManager = (ctx as any).hotkeyManager;\n    hotkeyManager?.destroy();\n  },\n  export: (ctx) => {\n    return {\n      /** 注册快捷操作 */\n      addHotAction: (actionKey: (string | number)[], cb: () => void) => {\n        const hotkeyManager: HotKeysManager = (ctx as any).hotkeyManager;\n        hotkeyManager.addHotAction(actionKey, cb);\n      },\n      disable: (status: boolean) => {\n        const hotkeyManager: HotKeysManager = (ctx as any).hotkeyManager;\n        hotkeyManager.setDisable(status);\n      },\n    };\n  },\n  meta: {\n    engine: {\n      version: '1.0.0',\n    },\n  },\n};\n\n/** 必须 */\nHotkeysPlugin.PLUGIN_NAME = PLUGIN_NAME;\n"
  },
  {
    "path": "packages/engine/src/plugins/Hotkeys/keymap.ts",
    "content": "export const isff = typeof navigator !== 'undefined' ? navigator.userAgent.toLowerCase().indexOf('firefox') > 0 : false;\n\n// Special Keys\nexport const _keyMap: Record<string, number> = {\n  backspace: 8,\n  '⌫': 8,\n  tab: 9,\n  clear: 12,\n  enter: 13,\n  '↩': 13,\n  return: 13,\n  esc: 27,\n  escape: 27,\n  space: 32,\n  left: 37,\n  up: 38,\n  right: 39,\n  down: 40,\n  del: 46,\n  delete: 46,\n  ins: 45,\n  insert: 45,\n  home: 36,\n  end: 35,\n  pageup: 33,\n  pagedown: 34,\n  capslock: 20,\n  num_0: 96,\n  num_1: 97,\n  num_2: 98,\n  num_3: 99,\n  num_4: 100,\n  num_5: 101,\n  num_6: 102,\n  num_7: 103,\n  num_8: 104,\n  num_9: 105,\n  num_multiply: 106,\n  num_add: 107,\n  num_enter: 108,\n  num_subtract: 109,\n  num_decimal: 110,\n  num_divide: 111,\n  '⇪': 20,\n  ',': 188,\n  '.': 190,\n  '/': 191,\n  '`': 192,\n  '-': isff ? 173 : 189,\n  '=': isff ? 61 : 187,\n  ';': isff ? 59 : 186,\n  \"'\": 222,\n  '[': 219,\n  ']': 221,\n  '\\\\': 220,\n};\n\n// Modifier Keys\nexport const _modifier: any = {\n  // shiftKey\n  '⇧': 16,\n  shift: 16,\n  // altKey\n  '⌥': 18,\n  alt: 18,\n  option: 18,\n  // ctrlKey\n  '⌃': 17,\n  ctrl: 17,\n  control: 17,\n  // metaKey\n  '⌘': 91,\n  cmd: 91,\n  command: 91,\n};\n\nexport const modifierMap = {\n  16: 'shiftKey',\n  18: 'altKey',\n  17: 'ctrlKey',\n  91: 'metaKey',\n\n  shiftKey: 16,\n  ctrlKey: 17,\n  altKey: 18,\n  metaKey: 91,\n};\n\nexport const _mods = {\n  16: false,\n  18: false,\n  17: false,\n  91: false,\n};\n\n// F1~F12 special key\nfor (let k = 1; k < 20; k++) {\n  _keyMap[`f${k}`] = 111 + k;\n}\n\n// 返回键码\nexport const defaultGetCode = (x: string) =>\n  _keyMap[x.toLowerCase()] || _modifier[x.toLowerCase()] || x.toUpperCase().charCodeAt(0);\n\nconst getKey = (x: number) => Object.keys(_keyMap).find((k) => _keyMap[k] === x);\nconst getModifier = (x: number) => Object.keys(_modifier).find((k) => _modifier[k] === x);\n\nexport const getKeyString = (c: number) => {\n  return getKey(c) || getModifier(c) || String.fromCharCode(c);\n};\n"
  },
  {
    "path": "packages/engine/src/plugins/Hotkeys/localize/en_US/index.ts",
    "content": "export const en_US: Record<string, any> = {\n  pluginName: 'Hot Keys',\n};\n"
  },
  {
    "path": "packages/engine/src/plugins/Hotkeys/localize/index.ts",
    "content": "import { zh_CN } from './zh_CN';\nimport { en_US } from './en_US';\nexport default { zh_CN, en_US } as Record<string, any>;\n"
  },
  {
    "path": "packages/engine/src/plugins/Hotkeys/localize/zh_CN/index.ts",
    "content": "export const zh_CN: Record<string, any> = {\n  pluginName: '快捷键',\n};\n"
  },
  {
    "path": "packages/engine/src/plugins/Hotkeys/type.ts",
    "content": "import { CPlugin, PluginInstance } from '@/core/pluginManager';\n\nexport type HotKeysExport = {\n  disable: (status: boolean) => void;\n  addHotAction: (actionKey: (string | number)[], cb: () => void) => void;\n};\n\nexport type HotKeysPluginConfig = object;\nexport type HotKeysPluginType = CPlugin<HotKeysPluginConfig, HotKeysExport>;\nexport type HotKeysPluginInstance = PluginInstance<HotKeysPluginType, HotKeysExport>;\n"
  },
  {
    "path": "packages/engine/src/plugins/Hotkeys/utils.ts",
    "content": "import { _keyMap, _modifier } from './keymap';\n\n// 修饰键转换成对应的键码\nexport function getMods(modifier: string[], key: string[]) {\n  const mods = key.slice(0, key.length - 1);\n  for (let i = 0; i < mods.length; i++) mods[i] = modifier[mods[i].toLowerCase() as any];\n  return mods;\n}\n\n// 处理传的key字符串转换成数组\nexport function getKeys(key: string) {\n  if (typeof key !== 'string') key = '';\n  key = key.replace(/\\s/g, ''); // 匹配任何空白字符,包括空格、制表符、换页符等等\n  const keys = key.split(','); // 同时设置多个快捷键，以','分割\n  let index = keys.lastIndexOf('');\n\n  // 快捷键可能包含','，需特殊处理\n  for (; index >= 0; ) {\n    keys[index - 1] += ',';\n    keys.splice(index, 1);\n    index = keys.lastIndexOf('');\n  }\n\n  return keys;\n}\n\n// 返回键码\nexport const getKeycode = (x: string) => {\n  return _keyMap[x.toLowerCase()] || (_modifier as any)[x.toLowerCase()] || x.toUpperCase().charCodeAt(0);\n};\n"
  },
  {
    "path": "packages/engine/src/plugins/OutlineTree/components/TreeView/context.ts",
    "content": "import { Sensor } from '@chamn/layout';\nimport { CPage } from '@chamn/model';\nimport React from 'react';\nimport { TreeNodeData } from './dataStruct';\nimport { DesignerExport } from '@/plugins/Designer/type';\nexport enum DragState {\n  DRAGGING = 'DRAGGING',\n  NORMAL = 'NORMAL',\n}\nexport type ContextState = {\n  treeData: TreeNodeData[];\n  currentSelectNodeKeys: string[];\n  expandKeys: string[];\n  multiSelect: boolean;\n  dragState: DragState;\n  pageModel: CPage | null;\n};\n\nexport type CTreeContextData = {\n  sensor?: Sensor;\n  state: ContextState;\n  updateState: (state: Partial<ContextState>) => void;\n  onSelectNode: (params: { keys: string[]; node: TreeNodeData }) => Promise<boolean | undefined>;\n  onDeleteNode: (id: string) => Promise<boolean | undefined>;\n  onCopyNode: (id: string) => Promise<boolean | undefined>;\n  getDesignerHandler?: () => Promise<DesignerExport>;\n};\n\nexport const CTreeContext = React.createContext<CTreeContextData>({\n  state: {\n    treeData: [],\n    currentSelectNodeKeys: [],\n    expandKeys: [],\n    multiSelect: false,\n    dragState: DragState.NORMAL,\n    pageModel: null,\n  },\n  updateState: () => {},\n  onSelectNode: async () => {\n    return true;\n  },\n  onDeleteNode: async () => {\n    return true;\n  },\n  onCopyNode: async () => {\n    return true;\n  },\n});\n"
  },
  {
    "path": "packages/engine/src/plugins/OutlineTree/components/TreeView/dataStruct.ts",
    "content": "import React from 'react';\n\nexport type TreeNodeData = {\n  containerRender?: (params: { item: TreeNodeData; treeNodeView: JSX.Element }) => React.ReactElement;\n  titleViewRender?: (params: { item: TreeNodeData; titleView: React.ReactNode }) => React.ReactElement;\n  title: React.ReactNode;\n  icon?: React.ReactNode;\n  key?: string;\n  children?: TreeNodeData[];\n  parent?: TreeNodeData | null;\n  canBeSelected?: boolean;\n  canDrag?: boolean;\n  canDropPos?: boolean | ('before' | 'after' | 'current')[];\n  rootNode?: boolean;\n};\n\nconst x = 10;\nconst y = 3;\nconst z = 2;\n\nconst generateData = (_level: number, _tns: TreeNodeData[], _preKey?: React.Key) => {\n  const preKey = _preKey || '0';\n  const tns = _tns || [];\n\n  const children: React.Key[] = [];\n  for (let i = 0; i < x; i++) {\n    const key = `${preKey}-${i}`;\n    tns.push({ title: key, key });\n    if (i < y) {\n      children.push(key);\n    }\n  }\n  if (_level < 0) {\n    return tns;\n  }\n  const level = _level - 1;\n  children.forEach((key, index) => {\n    tns[index].children = [];\n    return generateData(level, tns[index].children!, key);\n  });\n};\nconst tempData: TreeNodeData[] = [];\ngenerateData(z, tempData);\n\nexport const DemoTreeData: TreeNodeData = {\n  title: 'Page',\n  children: tempData,\n};\n"
  },
  {
    "path": "packages/engine/src/plugins/OutlineTree/components/TreeView/index.tsx",
    "content": "import { LayoutDragAndDropExtraDataType, Sensor } from '@chamn/layout';\nimport { CNode, CRootNode, DropPosType } from '@chamn/model';\nimport React from 'react';\nimport { WithTranslation } from 'react-i18next';\nimport { CPluginCtx } from '../../../../core/pluginManager';\nimport { LOGGER } from '../../../../utils/logger';\nimport { calculateDropPosInfo, getTargetMNodeKeyVal, transformPageSchemaToTreeData, traverseTree } from '../../util';\nimport { ContextState, CTreeContext, DragState } from './context';\nimport { TreeNodeData } from './dataStruct';\nimport styles from './style.module.scss';\nimport { DRAG_ITEM_KEY, TreeNode } from './treeNode';\nimport { DesignerPluginInstance } from '@/plugins/Designer/type';\nimport { message } from 'antd';\n\ninterface TreeViewProps extends WithTranslation {\n  pluginCtx: CPluginCtx;\n  multiSelect?: boolean;\n}\n\nexport class TreeView extends React.Component<\n  TreeViewProps,\n  ContextState & {\n    dropPosInfo: { x: number; y: number } | null;\n  }\n> {\n  domRef: React.RefObject<HTMLDivElement>;\n  disposeCbList: (() => void)[] = [];\n  sensor?: Sensor;\n  constructor(props: TreeViewProps) {\n    super(props);\n    this.domRef = React.createRef<HTMLDivElement>();\n\n    this.state = {\n      treeData: [],\n      currentSelectNodeKeys: [],\n      expandKeys: [],\n      multiSelect: props.multiSelect || false,\n      dropPosInfo: {\n        x: 0,\n        y: 0,\n      },\n      pageModel: props.pluginCtx.pageModel,\n      dragState: DragState.NORMAL,\n    };\n  }\n\n  getDesignerHandler = async () => {\n    const designerPluginInstance = await this.props.pluginCtx.pluginManager.get<DesignerPluginInstance>('Designer');\n    const designerHandler = designerPluginInstance?.export;\n    return designerHandler!;\n  };\n\n  updateTreeDataFromNode = () => {\n    const { pluginCtx } = this.props;\n    const { pageModel } = pluginCtx;\n    const plainTreeData = pageModel.export();\n    const tempTreeData = transformPageSchemaToTreeData(plainTreeData, pageModel);\n    this.setState({\n      treeData: tempTreeData,\n    });\n  };\n\n  getParentKeyPaths = (targetKey: string) => {\n    const { treeData } = this.state;\n    let target: TreeNodeData = null as any;\n    traverseTree(treeData, (node) => {\n      if (node.key === targetKey) {\n        target = node;\n        return true;\n      }\n      return false;\n    });\n    if (target) {\n      let tempNode = target?.parent as TreeNodeData | undefined | null;\n      const res = [];\n      while (tempNode) {\n        if (tempNode.key) {\n          res.push(tempNode.key);\n        }\n        tempNode = tempNode.parent;\n      }\n      return res;\n    } else {\n      return [];\n    }\n  };\n\n  scrollNodeToView = (key: string) => {\n    const dom = document.querySelector(`[${DRAG_ITEM_KEY}=\"${key}\"]`);\n    dom?.scrollIntoView?.({\n      behavior: 'smooth',\n      block: 'center',\n    });\n  };\n\n  async componentDidMount() {\n    this.updateTreeDataFromNode();\n    const { pluginCtx } = this.props;\n    const { pageModel } = pluginCtx;\n\n    pageModel.emitter.on('*', () => {\n      this.updateTreeDataFromNode();\n    });\n\n    pluginCtx.globalEmitter.on('onSelectNodeChange', ({ node }) => {\n      this.toSelectTreeNode(node);\n    });\n\n    const workbench = pluginCtx.getWorkbench();\n\n    workbench.emitter.on('leftPanelVisible', ({ visible, panelName }) => {\n      if (visible && panelName === 'OutlineTree') {\n        console.log('visible, panelName', visible, panelName);\n        const currentSelectNode = pluginCtx.engine.getActiveNode();\n        if (currentSelectNode) {\n          this.toSelectTreeNode(currentSelectNode);\n        }\n      }\n    });\n\n    const currentSelectNode = pluginCtx.engine.getActiveNode();\n    if (currentSelectNode) {\n      this.toSelectTreeNode(currentSelectNode);\n    }\n\n    await this.props.pluginCtx.pluginManager.onPluginReadyOk('Designer');\n    this.registerDragEvent();\n    // 页面重载时重新注册拖拽事件\n    this.props.pluginCtx.engine.pageModel.emitter.on('onReloadPage', () => {\n      this.registerDragEvent();\n    });\n  }\n\n  toSelectTreeNode = (node: CNode | CRootNode | null) => {\n    if (!node) {\n      this.setState({\n        currentSelectNodeKeys: [],\n      });\n      return;\n    }\n    const parentPaths = this.getParentKeyPaths(node.id);\n    LOGGER.debug('onSelectNodeChange parent path', parentPaths, node);\n    const newExpandKeys = Array.from(new Set([...this.state.expandKeys, ...parentPaths]));\n\n    LOGGER.debug('onSelectNodeChange newExpandKeys', newExpandKeys, node);\n\n    this.setState({\n      currentSelectNodeKeys: [node.id],\n      expandKeys: newExpandKeys,\n    });\n\n    setTimeout(() => {\n      this.scrollNodeToView(node.id);\n    }, 16);\n  };\n\n  containNode = (parentNode: TreeNodeData, targetNode: TreeNodeData) => {\n    let res = null;\n    traverseTree(parentNode, (node) => {\n      if (node.key === targetNode.key) {\n        res = node;\n        return true;\n      }\n      return false;\n    });\n    return res;\n  };\n\n  getTreeNodeByKey = (key: string): TreeNodeData | null => {\n    const { treeData } = this.state;\n    let target: TreeNodeData | null = null;\n    traverseTree(treeData, (node) => {\n      if (node.key === key) {\n        target = node;\n        return true;\n      }\n      return false;\n    });\n    return target;\n  };\n\n  registerDragEvent = async () => {\n    if (!this.domRef.current) {\n      return;\n    }\n    const sensor = new Sensor<LayoutDragAndDropExtraDataType>({\n      container: this.domRef.current,\n      name: 'OutlineTree',\n      eventPriority: 999,\n      mainDocument: document,\n    });\n    const { pluginCtx } = this.props;\n\n    const pageModel = pluginCtx.pageModel;\n    const designerExport = await this.getDesignerHandler();\n    const dnd = designerExport!.getDnd()!;\n\n    sensor.setCanDrag(async (eventObj) => {\n      const targetDom = eventObj.event.target as HTMLDivElement;\n      if (!targetDom) {\n        return;\n      }\n      const targetNodeId = getTargetMNodeKeyVal(targetDom, DRAG_ITEM_KEY);\n\n      if (!targetNodeId) {\n        return;\n      }\n\n      const targetNode = pageModel.getNode(targetNodeId);\n      const targetTreeNode = this.getTreeNodeByKey(targetNodeId);\n      // 判断当前节点是否可以在节点树上拖动，比如 jsslot 容器节点不能被拖动，大纲树特有\n      if (targetTreeNode?.canDrag !== undefined && targetTreeNode?.canDrag === false) {\n        return;\n      }\n\n      // 判断节点本身是否可以拖动\n      const designerInstance = designerExport!.getInstance();\n      const nodeCanDragRes = await designerInstance?.customAdvanceHook.canDrag({\n        dragNode: targetNode,\n        eventObj: {\n          extraData: {},\n          ...eventObj,\n          from: eventObj.event,\n          fromSensor: sensor,\n          fromPointer: eventObj.pointer,\n        },\n      });\n      // 节点不能拖动\n      if (nodeCanDragRes === false) {\n        return false;\n      }\n\n      if (!targetNode) {\n        console.log('targetNode not found');\n        return;\n      }\n\n      const canDragResInfo = {\n        ...eventObj,\n        extraData: {\n          dragNode: targetNode,\n          dragNodeUID: targetNode.id,\n        },\n      };\n\n      if (typeof nodeCanDragRes === 'object') {\n        canDragResInfo.extraData.dragNode = nodeCanDragRes.dragNode!;\n        canDragResInfo.extraData.dragNodeUID = nodeCanDragRes.dragNode?.id || '';\n      }\n\n      return canDragResInfo;\n    });\n\n    sensor.setCanDrop(async (eventObj) => {\n      const targetDom = eventObj.event.target as HTMLDivElement;\n\n      if (!targetDom) {\n        LOGGER.debug('drop dom not found');\n        return eventObj;\n      }\n      const targetNodeId = getTargetMNodeKeyVal(targetDom, DRAG_ITEM_KEY);\n\n      if (!targetNodeId) {\n        LOGGER.debug('targetNodeId dom not found', eventObj, targetDom, DRAG_ITEM_KEY);\n        return eventObj;\n      }\n      const targetTreeNode = this.getTreeNodeByKey(targetNodeId);\n      if (targetTreeNode?.canDropPos !== undefined && targetTreeNode.canDropPos === false) {\n        LOGGER.debug('node can not be drop by tree node config');\n        return eventObj;\n      }\n\n      const targetNode = pageModel.getNode(targetNodeId);\n\n      if (!targetNode) {\n        LOGGER.debug('targetNode not found');\n        return eventObj;\n      }\n      const startNode = eventObj.extraData?.dragNode as CNode;\n      if (!startNode) {\n        LOGGER.debug('startNode not found');\n        return eventObj;\n      }\n\n      if (startNode?.id === targetNode.id) {\n        LOGGER.debug('startNode and dropNode is the same');\n        return eventObj;\n      }\n      const hasContain = startNode.contains(targetNode.id);\n\n      if (hasContain) {\n        LOGGER.debug('startNode contain dropNode');\n        return eventObj;\n      }\n\n      const dropInfo = calculateDropPosInfo({\n        point: eventObj.pointer,\n        dom: targetDom,\n      });\n\n      if (Array.isArray(targetTreeNode?.canDropPos) && !targetTreeNode?.canDropPos.includes(dropInfo.pos)) {\n        return false;\n      }\n\n      LOGGER.info('can dropNode', targetNode);\n\n      const res = {\n        ...eventObj,\n        extraData: {\n          ...eventObj.extraData,\n          dropPosInfo: dropInfo,\n          dropNode: targetNode,\n          dropNodeUID: targetNode.id,\n        },\n      };\n\n      // 判断节点本身是否可以拖动\n      const designerInstance = designerExport!.getInstance();\n      const nodeCanDropRes = await designerInstance?.customAdvanceHook.canDrop({\n        dragNode: res.extraData.dragNode,\n        dropNode: targetNode,\n        eventObj: {\n          ...res,\n          from: res.event,\n          fromSensor: sensor,\n          fromPointer: res.pointer,\n        },\n      });\n\n      if (nodeCanDropRes === false) {\n        return false;\n      }\n\n      if (typeof nodeCanDropRes === 'object') {\n        res.extraData = {\n          ...res.extraData,\n          ...nodeCanDropRes,\n        };\n      }\n      return res;\n    });\n\n    dnd.registerSensor(sensor);\n\n    sensor.emitter.on('dragging', (e) => {\n      const dropNode = e.extraData.dropNode as CNode;\n      this.setState({\n        dragState: DragState.DRAGGING,\n      });\n\n      if (!dropNode) {\n        this.setState({\n          dropPosInfo: null,\n        });\n        return;\n      }\n      const dropDom = document.querySelectorAll(`[${DRAG_ITEM_KEY}=\"${dropNode.id}\"]`)?.[0];\n      if (!dropDom) {\n        return;\n      }\n      const dropPosInfo = e.extraData?.dropPosInfo || ({} as DropPosType);\n      const rect = dropDom.getBoundingClientRect();\n      const newDropInfo = { x: 0, y: 0 };\n\n      newDropInfo.x = rect.x;\n      if (dropPosInfo.pos === 'before') {\n        newDropInfo.y = rect.y;\n      } else if (dropPosInfo.pos === 'after') {\n        newDropInfo.y = rect.y + rect.height;\n      } else {\n        newDropInfo.y = rect.y + rect.height;\n        newDropInfo.x = rect.x + 20;\n      }\n      this.setState({\n        dropPosInfo: newDropInfo,\n      });\n    });\n\n    sensor.emitter.on('dragEnd', () => {\n      this.setState({\n        dragState: DragState.NORMAL,\n      });\n    });\n    this.sensor = sensor;\n  };\n\n  render() {\n    const { treeData, dragState, dropPosInfo } = this.state;\n    const { pluginCtx } = this.props;\n\n    return (\n      <CTreeContext.Provider\n        value={{\n          sensor: this.sensor,\n          state: this.state,\n          getDesignerHandler: this.getDesignerHandler,\n          onSelectNode: async ({ keys: sk }) => {\n            const designer = await pluginCtx.pluginManager.get<DesignerPluginInstance>('Designer');\n            if (!designer) {\n              console.warn('Designer is empty');\n              return false;\n            }\n            const nodeId = sk?.[0] || '';\n            const nn = pluginCtx.pageModel.getNode(nodeId);\n            if (!nn) {\n              return false;\n            }\n\n            const designerExport = designer.export;\n            const flag = await designerExport.selectNode(nodeId);\n            return flag;\n          },\n          updateState: (newVal) => {\n            this.setState(newVal as any);\n          },\n          onDeleteNode: async (id) => {\n            const designer = await pluginCtx.pluginManager.get<DesignerPluginInstance>('Designer');\n            if (!designer) {\n              console.warn('Designer is empty');\n              return false;\n            }\n            const designerExport = designer.export;\n            const nodeId = id;\n            const nn = pluginCtx.pageModel.getNode(nodeId);\n            if (!nn) {\n              message.error('该节点不能删除');\n              return false;\n            }\n            const flag = await designerExport.deleteNode(id);\n            if (!flag) {\n              message.error('该节点不能删除');\n            }\n            return flag;\n          },\n          onCopyNode: async (id) => {\n            const designer = await pluginCtx.pluginManager.get<DesignerPluginInstance>('Designer');\n            if (!designer) {\n              console.warn('Designer is empty');\n              return false;\n            }\n            const designerExport = designer.export;\n            return await designerExport.copyNode(id);\n          },\n        }}\n      >\n        <div className={styles.contentBox} ref={this.domRef}>\n          {treeData.map((item, index) => {\n            return <TreeNode item={item} key={item.key + `${index}`} pluginCtx={this.props.pluginCtx}></TreeNode>;\n          })}\n          {dragState === DragState.DRAGGING && dropPosInfo && (\n            <div\n              className={styles.dropAnchorLine}\n              style={{\n                left: `${dropPosInfo.x}px`,\n                top: `${dropPosInfo.y}px`,\n              }}\n            ></div>\n          )}\n        </div>\n      </CTreeContext.Provider>\n    );\n  }\n}\n"
  },
  {
    "path": "packages/engine/src/plugins/OutlineTree/components/TreeView/style.module.scss",
    "content": ".contentBox {\n  background-color: white;\n  height: 100%;\n  border-top: 1px solid $borderColor;\n  overflow: auto;\n  position: relative;\n  * {\n    transition: all 0.1s;\n  }\n}\n.nodeBox {\n  user-select: none;\n  cursor: pointer;\n  .toolbarBox {\n    display: none;\n    padding-right: 10px;\n  }\n  .iconItem {\n    margin-right: 8px;\n  }\n}\n\n.nodeContent:hover {\n  .toolbarBox {\n    display: flex;\n  }\n}\n\n.nodeContent {\n  font-size: 14px;\n  cursor: pointer;\n  display: flex;\n  align-items: center;\n  &.selected {\n    background-color: rgba(221, 221, 221, 0.232);\n  }\n  .nodeArrow {\n    font-size: 12px;\n\n    &.expanded {\n      transform-origin: center;\n      transform: rotate(90deg);\n    }\n  }\n\n  .nodeRenderView {\n    display: flex;\n    align-items: center;\n    height: 26px;\n    flex: 1;\n    flex-wrap: nowrap;\n    overflow: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n  }\n}\n\n.nodeChildren {\n  position: relative;\n\n  &::before {\n    display: block;\n    position: absolute;\n    left: 13px;\n    top: 0;\n    content: '';\n    height: 100%;\n    width: 1px;\n    background-color: $borderColor;\n  }\n\n  &.selected::before {\n    background-color: rgba(0, 0, 255, 0.359);\n  }\n}\n\n.dropAnchorLine {\n  width: 100px;\n  height: 2px;\n  background-color: #1890ff;\n  position: fixed;\n  z-index: 99;\n  pointer-events: none;\n}\n\n.arrowSpan {\n  position: relative;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  &::after {\n    content: '';\n    position: absolute;\n    top: 0px;\n    right: 0;\n    bottom: -0;\n    left: -8px;\n  }\n}\n"
  },
  {
    "path": "packages/engine/src/plugins/OutlineTree/components/TreeView/treeNode.tsx",
    "content": "import { useContext, useEffect, useRef, useState } from 'react';\nimport { CopyOutlined, DeleteOutlined, EyeInvisibleOutlined, EyeOutlined, RightOutlined } from '@ant-design/icons';\nimport clsx from 'clsx';\nimport { CTreeContext, DragState } from './context';\nimport { TreeNodeData } from './dataStruct';\nimport styles from './style.module.scss';\nimport { CNode } from '@chamn/model';\nimport { Input, InputRef } from 'antd';\nimport { CPluginCtx } from '@/core/pluginManager';\nimport { DesignerPluginInstance } from '@/plugins/Designer/type';\n\nexport const DRAG_ITEM_KEY = 'data-drag-key';\n\nexport type TreeNodeProps = {\n  item: TreeNodeData;\n  level?: number;\n  paths?: (string | number)[];\n  pluginCtx: CPluginCtx;\n};\nexport const TreeNode = (props: TreeNodeProps) => {\n  const allStateRef = useRef<{ titleEditable: boolean }>({\n    titleEditable: false,\n  });\n  const { level = 0, item, paths = ['0'] } = props;\n  const [nodeVisible, setNodeVisible] = useState(true);\n  const {\n    state: ctxState,\n    updateState,\n    onSelectNode,\n    onDeleteNode,\n    getDesignerHandler,\n    onCopyNode,\n  } = useContext(CTreeContext);\n\n  const [titleEditable, setTitleEditable] = useState(allStateRef.current?.titleEditable);\n  const [editInputValue, setEditInputValue] = useState('');\n  allStateRef.current.titleEditable = titleEditable;\n  const expanded = ctxState.expandKeys.find((el) => el === item.key);\n  const toggleExpandNode = (open?: boolean) => {\n    let newExpandKeys = ctxState.expandKeys;\n    if (open !== undefined) {\n      if (open === true) {\n        if (!expanded) {\n          newExpandKeys.push(item.key || '');\n        }\n      } else {\n        if (expanded) {\n          newExpandKeys = newExpandKeys.filter((el) => el !== item.key);\n        }\n      }\n    } else {\n      if (expanded) {\n        newExpandKeys = newExpandKeys.filter((el) => el !== item.key);\n      } else {\n        newExpandKeys.push(item.key || '');\n      }\n    }\n\n    updateState({\n      expandKeys: newExpandKeys,\n    });\n  };\n  const selected = ctxState.currentSelectNodeKeys.find((el) => el === item.key);\n  const titleEditInputRef = useRef<InputRef>(null);\n  const toggleSelectNode = async () => {\n    toggleExpandNode(true);\n    if (titleEditable) {\n      titleEditInputRef?.current?.focus();\n      return;\n    }\n    if (item.canBeSelected !== undefined && item.canBeSelected === false) {\n      return;\n    }\n    let newKeys = ctxState.currentSelectNodeKeys;\n    if (!ctxState.multiSelect) {\n      if (item.key) {\n        const flag = await onSelectNode?.({ keys: [item.key], node: item });\n        if (flag) {\n          updateState({\n            currentSelectNodeKeys: [String(item.key)],\n          });\n        }\n      } else {\n        onSelectNode?.({ keys: [], node: item });\n      }\n\n      return;\n    }\n    if (selected) {\n      newKeys = newKeys.filter((el) => el !== item.key);\n    } else {\n      newKeys.push(String(item.key));\n    }\n    onSelectNode?.({ keys: newKeys, node: item });\n    updateState({\n      currentSelectNodeKeys: newKeys,\n    });\n  };\n  const singPadding = 20;\n  const indent = singPadding * level;\n  const canBeSelected = item.canBeSelected ?? true;\n\n  const dragKeyProps = {\n    [DRAG_ITEM_KEY]: item.key,\n  };\n  const updateExpandKeyRef = useRef<(key: string) => void>();\n  const ctxStateRef = useRef<typeof ctxState>();\n  ctxStateRef.current = ctxState;\n  updateExpandKeyRef.current = (key) => {\n    const oldExpandKeys = ctxState.expandKeys;\n    const newExpandKeys = Array.from(new Set([...oldExpandKeys, key]));\n    updateState({\n      expandKeys: newExpandKeys,\n    });\n  };\n  const domRef = useRef<HTMLDivElement>(null);\n  useEffect(() => {\n    // auto expand on dragging\n    let timerHandler = 0;\n    domRef.current?.addEventListener('mouseenter', () => {\n      timerHandler = window.setTimeout(() => {\n        if (ctxStateRef.current?.dragState === DragState.DRAGGING) {\n          updateExpandKeyRef.current?.(item.key || '');\n        }\n      }, 0.8 * 1000);\n    });\n    domRef.current?.addEventListener('mouseleave', () => {\n      clearTimeout(timerHandler);\n    });\n\n    const clickHandle = (e: MouseEvent) => {\n      if (allStateRef.current.titleEditable) {\n        if (e.target === titleEditInputRef.current?.input) {\n          return;\n        }\n        setTitleEditable(false);\n        targetNodeModel.updateValue();\n      }\n    };\n    let sunWin: Window | null;\n    const registerDesignerClick = async () => {\n      const designerPluginInstance = await props.pluginCtx.pluginManager.get<DesignerPluginInstance>('Designer');\n      const designerHandler = designerPluginInstance?.export;\n      const win = designerHandler?.getDesignerWindow();\n      win?.addEventListener('click', clickHandle);\n      sunWin = win || null;\n    };\n\n    registerDesignerClick();\n    document.addEventListener('click', clickHandle);\n\n    return () => {\n      if (timerHandler) {\n        clearTimeout(timerHandler);\n      }\n      document.removeEventListener('click', clickHandle);\n      sunWin?.removeEventListener('click', clickHandle);\n    };\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n\n  const targetNodeModel = ctxState.pageModel?.getNode(item.key || '') as CNode;\n\n  const toggleNodeVisible = () => {\n    const newVisible = !nodeVisible;\n\n    if (!targetNodeModel) {\n      return;\n    }\n    const devState = targetNodeModel.value.configure.devState ?? {};\n    devState.condition = newVisible;\n    targetNodeModel.value.configure.devState = devState;\n    targetNodeModel.updateValue();\n    setNodeVisible(newVisible);\n  };\n\n  let titleView = item.title;\n  if (item.titleViewRender) {\n    titleView = item.titleViewRender({\n      item,\n      titleView: item.title,\n    });\n  }\n\n  const bodyView = (\n    <div className={styles.nodeBox}>\n      <div\n        className={clsx([styles.nodeContent, selected && canBeSelected && styles.selected])}\n        style={{ marginLeft: `${-indent}px`, paddingLeft: `${indent + 8}px` }}\n        onMouseMove={async () => {\n          if (!item.key) {\n            return;\n          }\n          const designerHandler = await getDesignerHandler?.();\n          const compInstances = designerHandler?.getDynamicComponentInstances(item.key);\n          if (typeof compInstances?._CONDITION !== 'undefined') {\n            setNodeVisible(compInstances?._CONDITION);\n          }\n        }}\n      >\n        {item.children?.length ? (\n          <span style={{ paddingRight: '5px' }} className={styles.arrowSpan} onClickCapture={() => toggleExpandNode()}>\n            <RightOutlined className={clsx([styles.nodeArrow, expanded && styles.expanded])} />\n          </span>\n        ) : null}\n        <div\n          className={styles.nodeRenderView}\n          {...dragKeyProps}\n          ref={domRef}\n          onClick={toggleSelectNode}\n          onDoubleClick={() => {\n            // slot 节点，属性节点不能编辑 title\n            if (!targetNodeModel) {\n              return;\n            }\n            const node = targetNodeModel;\n            const nodeMeta = node.materialsModel.findByComponentName(node.value.componentName)?.value.title;\n            const inputValue = node.value.title || nodeMeta || node.value.componentName || '';\n            setEditInputValue(inputValue);\n            setTitleEditable(true);\n            setTimeout(() => {\n              titleEditInputRef.current?.focus();\n            }, 16.66);\n          }}\n        >\n          {!titleEditable && titleView}\n          {titleEditable && (\n            <div\n              style={{\n                paddingRight: '10px',\n              }}\n            >\n              <Input\n                size=\"small\"\n                maxLength={20}\n                style={{}}\n                ref={titleEditInputRef}\n                value={editInputValue}\n                onPressEnter={() => {\n                  setTitleEditable(false);\n                  targetNodeModel.updateValue();\n                }}\n                onChange={(e) => {\n                  targetNodeModel.value.title = e.target.value;\n                  setEditInputValue(e.target.value);\n                }}\n              />\n            </div>\n          )}\n        </div>\n\n        {!titleEditable && !item.rootNode && (\n          <div className={styles.toolbarBox}>\n            <div className={styles.iconItem}>\n              {!nodeVisible && <EyeOutlined onClick={toggleNodeVisible} />}\n              {nodeVisible && <EyeInvisibleOutlined onClick={toggleNodeVisible} />}\n            </div>\n            <div\n              className={styles.iconItem}\n              onClick={() => {\n                onCopyNode(item.key || '');\n              }}\n            >\n              <CopyOutlined />\n            </div>\n            <div\n              className={styles.iconItem}\n              onClick={() => {\n                onDeleteNode(item.key || '');\n              }}\n            >\n              <DeleteOutlined />\n            </div>\n          </div>\n        )}\n      </div>\n      <div\n        className={clsx([styles.nodeChildren, selected && styles.selected])}\n        style={{\n          paddingLeft: `${singPadding}px`,\n          height: expanded ? 'auto' : '0',\n        }}\n      >\n        {expanded &&\n          item.children?.map((el, index) => {\n            const key = `${el.key}-${index}`;\n            return (\n              <TreeNode key={key} item={el} paths={[...paths, index]} level={level + 1} pluginCtx={props.pluginCtx} />\n            );\n          })}\n      </div>\n    </div>\n  );\n  if (item.containerRender) {\n    const containerView = item.containerRender({\n      item: item,\n      treeNodeView: bodyView,\n    });\n    return containerView;\n  }\n  return bodyView;\n};\n"
  },
  {
    "path": "packages/engine/src/plugins/OutlineTree/index.tsx",
    "content": "import { ApartmentOutlined } from '@ant-design/icons';\nimport { CPlugin } from '../../core/pluginManager';\nimport { withTranslation } from 'react-i18next';\nimport { TreeView } from './components/TreeView';\nimport localize from './localize';\n\nconst PLUGIN_NAME = 'OutlineTree';\nconst i18nNamespace = `plugin:${PLUGIN_NAME}`;\n\nexport const OutlineTreePlugin: CPlugin = {\n  name: PLUGIN_NAME,\n  PLUGIN_NAME,\n  async init(ctx) {\n    const { i18n } = ctx;\n    Object.keys(localize).forEach((lng) => {\n      i18n.addResourceBundle(lng, i18nNamespace, localize[lng], true, true);\n    });\n\n    const TreeViewWithLocalize = withTranslation(i18nNamespace)(TreeView);\n    const Title = withTranslation(i18nNamespace)(({ t }) => <>{t('pluginName')}</>);\n    const workbench = ctx.getWorkbench();\n    workbench.addLeftPanel({\n      title: <Title />,\n      name: PLUGIN_NAME,\n      icon: <ApartmentOutlined />,\n      render: <TreeViewWithLocalize pluginCtx={ctx} />,\n    });\n\n    ctx.pluginReadyOk();\n  },\n  async destroy(ctx) {\n    console.log('destroy', ctx);\n  },\n  export: () => {\n    return {};\n  },\n  meta: {\n    engine: {\n      version: '1.0.0',\n    },\n  },\n};\n\nOutlineTreePlugin.PLUGIN_NAME = PLUGIN_NAME;\n"
  },
  {
    "path": "packages/engine/src/plugins/OutlineTree/localize/en_US/index.ts",
    "content": "export const en_US: Record<string, any> = {\n  pluginName: 'Layout Tree',\n};\n"
  },
  {
    "path": "packages/engine/src/plugins/OutlineTree/localize/index.ts",
    "content": "import { zh_CN } from './zh_CN';\nimport { en_US } from './en_US';\nexport default { zh_CN, en_US } as Record<string, any>;\n"
  },
  {
    "path": "packages/engine/src/plugins/OutlineTree/localize/zh_CN/index.ts",
    "content": "export const zh_CN: Record<string, any> = {\n  pluginName: '大纲树',\n};\n"
  },
  {
    "path": "packages/engine/src/plugins/OutlineTree/util.tsx",
    "content": "import {\n  ArraySetterObjType,\n  CNode,\n  CNodeDataType,\n  CPage,\n  CPageDataType,\n  DropPosType,\n  getMTitle,\n  isJSSlotPropNode,\n  isSpecialMaterialPropType,\n  MaterialPropType,\n  MTitle,\n  RenderPropType,\n  SetterObjType,\n  SetterType,\n  ShapeSetterObjType,\n} from '@chamn/model';\nimport { isPlainObject } from 'lodash-es';\nimport { TreeNodeData } from './components/TreeView/dataStruct';\n\nexport const getTargetMNodeKeyVal = (dom: HTMLElement | null, key: string): null | string => {\n  if (!dom) {\n    return null;\n  }\n  const val = dom.getAttribute(key);\n  if (!val) {\n    return getTargetMNodeKeyVal(dom.parentElement, key);\n  } else {\n    return val;\n  }\n};\n\nexport const transformNodeSchemaToTreeData = (\n  nodeSchema: CNodeDataType | CNodeDataType[],\n  parent: TreeNodeData,\n  pageModel: CPage\n): TreeNodeData | TreeNodeData[] => {\n  const tb = (node: CNodeDataType, parent?: TreeNodeData | null): TreeNodeData => {\n    let nodeChild: any[] = node.children || [];\n    if (!Array.isArray(nodeChild)) {\n      // TODO: 暂时不处理字符串的情况\n      nodeChild = [];\n    }\n    // 过滤掉字符串的情况\n    nodeChild = nodeChild.filter((el) => typeof el !== 'string');\n    const nodeMeta = pageModel.materialsModel.findByComponentName(node.componentName)?.value.title;\n    const newCurrentNode: TreeNodeData = {\n      title: node.title || nodeMeta || node.componentName,\n      key: node.id,\n      children: [],\n      parent: parent,\n      canDropPos: pageModel.getNode(node.id || '')?.isContainer() ? undefined : ['after', 'before'],\n    };\n    // 还需要处理 props 中的节点\n    const propsNodeList: TreeNodeData[] = [];\n    const slotNode: TreeNodeData = {\n      title: 'SLOT',\n      key: `${node.id}-SLOT`,\n      children: propsNodeList,\n      canBeSelected: false,\n      canDropPos: false,\n      parent: null,\n      containerRender: ({ treeNodeView }) => {\n        return (\n          <div\n            style={{\n              border: '1px solid #a97cf8',\n              borderTopWidth: '6px',\n              marginRight: '2px',\n              marginTop: '5px',\n            }}\n          >\n            {treeNodeView}\n          </div>\n        );\n      },\n    };\n    const props = node.props || {};\n\n    const processProps = (val: unknown, key: string, keys: string[]) => {\n      const flag = isJSSlotPropNode(val);\n\n      if (flag) {\n        const tempVal = val as RenderPropType;\n        const pageModeNode = pageModel.getNode(node.id!);\n        let propsTitle = '';\n        if (pageModeNode) {\n          const tempTitle = getPropsLabel(pageModeNode as CNode, [...keys, key]);\n          propsTitle = getMTitle(tempTitle);\n        }\n        let plainTitle = key;\n        if (propsTitle) {\n          plainTitle = getMTitle(propsTitle) || key;\n        }\n        const tempNode: TreeNodeData = {\n          title: plainTitle,\n          key: `${node.id}-${keys.join('_')}`,\n          children: [],\n          canBeSelected: false,\n          canDrag: false,\n          canDropPos: ['current'],\n          parent: slotNode,\n        };\n\n        let propValue: CNodeDataType[] = [];\n        if (tempVal.value && !Array.isArray(tempVal.value)) {\n          propValue = [tempVal.value];\n        } else {\n          propValue = tempVal.value;\n        }\n\n        tempNode.children = transformNodeSchemaToTreeData(propValue, tempNode, pageModel) as TreeNodeData[];\n        propsNodeList.push(tempNode);\n        return;\n      }\n      if (isPlainObject(val)) {\n        const tempVal = val as any;\n        Object.keys(tempVal).forEach((newKey) => {\n          processProps(tempVal[newKey], newKey, [...keys, key]);\n        });\n      }\n      if (Array.isArray(val)) {\n        const tempVal = val as any[];\n        tempVal.forEach((item, index) => {\n          processProps(item, String(index), [...keys, key]);\n        });\n      }\n    };\n\n    const getPropsLabel = (nodeModel: CNode, keyPath: string[]) => {\n      const propsLabelMap = getNodePropsLabelMap(nodeModel);\n      const newKeyPath = keyPath.map((el) => {\n        if (String(parseInt(el, 10)) === el) {\n          return '$NUM';\n        } else {\n          return el;\n        }\n      });\n      return propsLabelMap[newKeyPath.join('.')] || '';\n    };\n\n    Object.keys(props).forEach((key) => {\n      processProps(props[key], key, []);\n    });\n\n    if (propsNodeList.length > 0) {\n      newCurrentNode.children?.push(slotNode);\n      slotNode.parent = newCurrentNode;\n    }\n    const childNodeList = nodeChild.map((el) => tb(el, newCurrentNode)) || [];\n    newCurrentNode.children = [...newCurrentNode.children!, ...childNodeList];\n    return newCurrentNode;\n  };\n\n  if (Array.isArray(nodeSchema)) {\n    return nodeSchema.map((el) => {\n      return tb(el, parent);\n    });\n  } else {\n    return tb(nodeSchema, parent);\n  }\n};\n\nexport const transformPageSchemaToTreeData = (pageSchema: CPageDataType, pageModel: CPage): TreeNodeData[] => {\n  const tree = pageSchema.componentsTree;\n  let child = (tree.children || []) as CNodeDataType[];\n  if (!Array.isArray(child)) {\n    child = [];\n  }\n\n  const rootNode: TreeNodeData = {\n    title: 'Page',\n    key: tree.id || 'globalState',\n    rootNode: true,\n    children: [],\n  };\n  rootNode.children = transformNodeSchemaToTreeData(child, rootNode, pageModel) as TreeNodeData[];\n  return [rootNode];\n};\n\nexport const traverseTree = (\n  tree: TreeNodeData | TreeNodeData[],\n  handler: (node: TreeNodeData) => boolean | undefined\n) => {\n  let tempTree: TreeNodeData[] = [];\n  if (Array.isArray(tree)) {\n    tempTree = tree;\n  } else {\n    tempTree = [tree];\n  }\n\n  let stop = false;\n\n  const traverseCb = (node: TreeNodeData, conditionCb: (node: TreeNodeData) => boolean | undefined) => {\n    if (stop) {\n      return;\n    }\n    const res = conditionCb(node);\n    if (res) {\n      stop = true;\n    } else {\n      node.children?.forEach((el) => {\n        traverseCb(el, conditionCb);\n      });\n    }\n  };\n  tempTree.forEach((el) => {\n    traverseCb(el, handler);\n  });\n};\n\nexport function calculateDropPosInfo(params: { point: { x: number; y: number }; dom: HTMLElement }): DropPosType {\n  const { point, dom } = params;\n  let pos: DropPosType['pos'];\n\n  const mousePos = point;\n  const targetRect = dom.getBoundingClientRect();\n  const targetDomH = targetRect.height;\n  const xCenter = targetRect.x + 50;\n  const yCenter = targetRect.y + Math.round(targetDomH / 2);\n\n  if (mousePos.y > yCenter) {\n    pos = 'after';\n  } else {\n    pos = 'before';\n  }\n\n  if (mousePos.x > xCenter && pos === 'after') {\n    pos = 'current';\n  }\n\n  return {\n    pos,\n    direction: 'vertical',\n  };\n}\n\n//TODO:  移动到 model 包中去\nexport const getNodePropsLabelMap = (node: CNode) => {\n  const resMap: Record<string, MTitle> = {};\n  const props = node.material?.value.props;\n  if (!props) {\n    return resMap;\n  }\n  const singleProcessUnit = (val: SetterType, paths: string[]) => {\n    if (isPlainObject(val)) {\n      const newPaths = [...paths];\n      const objSetter = val as SetterObjType;\n      const componentName = objSetter.componentName;\n      if (componentName === 'ArraySetter') {\n        const setter = objSetter as ArraySetterObjType;\n        const arrItemSetter = setter.props?.item.setters || [];\n        const parentTitle = resMap[paths.join('.')];\n        let currentTitle = '';\n        if (typeof parentTitle === 'string') {\n          currentTitle = parentTitle;\n        } else {\n          currentTitle = parentTitle.label;\n        }\n        newPaths.push('$NUM');\n        arrItemSetter.forEach((el) => {\n          currentTitle = `${currentTitle}`;\n          resMap[newPaths.join('.')] = currentTitle;\n          singleProcessUnit(el, newPaths);\n        });\n      } else if (componentName === 'ShapeSetter') {\n        const setter = objSetter as ShapeSetterObjType;\n        const elements = setter.props?.elements || [];\n        elements.forEach((it) => {\n          const newObjPaths = [...newPaths];\n          newObjPaths.push(it.name);\n          resMap[newObjPaths.join('.')] = it.title;\n          const subSetter = it.setters || [];\n          subSetter.forEach((it2) => {\n            singleProcessUnit(it2, [...newObjPaths]);\n          });\n        });\n      }\n    }\n  };\n\n  props.forEach((item) => {\n    let targetItem: MaterialPropType | MaterialPropType[] | null = null;\n    if (isSpecialMaterialPropType(item)) {\n      targetItem = item.content;\n    } else {\n      targetItem = item;\n    }\n    let itemArr: MaterialPropType[] = [];\n    if (Array.isArray(targetItem)) {\n      itemArr = targetItem;\n    } else {\n      itemArr = [targetItem];\n    }\n\n    itemArr.forEach((it) => {\n      const newPaths: string[] = [it.name];\n      resMap[newPaths.join('.')] = it.title;\n      it.setters?.forEach((it2) => {\n        singleProcessUnit(it2, newPaths);\n      });\n    });\n  });\n\n  return resMap;\n};\n"
  },
  {
    "path": "packages/engine/src/plugins/PropertyPanel/index.tsx",
    "content": "import { CRightPanelItem } from '../RightPanel/view';\nimport { PropertyPanel } from './view';\n\nexport const PropertyPanelConfig: CRightPanelItem = {\n  key: 'Property',\n  name: 'Property',\n  view: ({ node, pluginCtx }) => <PropertyPanel node={node} pluginCtx={pluginCtx} />,\n  show: (props) => {\n    return props.node?.material?.value.advanceCustom?.rightPanel?.property !== false;\n  },\n};\n"
  },
  {
    "path": "packages/engine/src/plugins/PropertyPanel/style.module.scss",
    "content": ".CFromRenderBox {\n  padding: 0 15px;\n  :global {\n    .ant-collapse .ant-collapse-content > .ant-collapse-content-box {\n      padding-right: 5px;\n    }\n\n    .ant-collapse > .ant-collapse-item:last-child > .ant-collapse-header {\n      padding-right: 5px;\n    }\n  }\n}\n"
  },
  {
    "path": "packages/engine/src/plugins/PropertyPanel/utils.ts",
    "content": "import { SetterObjType, SetterType } from '@chamn/model';\n\nexport const getSetterList = (setters: SetterType[] = []): SetterObjType[] => {\n  return setters.map((setter) => {\n    if (typeof setter === 'string') {\n      return {\n        componentName: setter as any,\n      };\n    } else {\n      return setter;\n    }\n  });\n};\n"
  },
  {
    "path": "packages/engine/src/plugins/PropertyPanel/view.tsx",
    "content": "import { useEffect, useRef } from 'react';\nimport { CNode, CRootNode } from '@chamn/model';\nimport { CPluginCtx } from '../../core/pluginManager';\nimport { BUILD_IN_ADVANCE_SETTER_MAP } from '@/component/CustomSchemaForm/components/Setters/AdvanceSetterList';\n\nimport styles from './style.module.scss';\nimport { CustomSchemaForm, CustomSchemaFormInstance, CustomSchemaFormProps } from '../../component/CustomSchemaForm';\n\nexport const PropertyPanel = (props: { node: CNode | CRootNode | null; pluginCtx: CPluginCtx }) => {\n  const { node } = props;\n  const properties = node?.material?.value.props || [];\n  const formRef = useRef<CustomSchemaFormInstance>(null);\n\n  useEffect(() => {\n    const handel = () => {\n      const newVal = node?.getPlainProps?.() || {};\n      formRef.current?.setFields(newVal);\n    };\n    handel();\n    node?.emitter.on('onNodeChange', handel);\n    return () => {\n      node?.emitter.off('onNodeChange', handel);\n    };\n  }, [node]);\n\n  const value = node?.getPlainProps?.() || {};\n\n  const onValueChange: CustomSchemaFormProps['onValueChange'] = (val) => {\n    node?.updateValue({\n      props: val,\n    });\n  };\n\n  const onSetterChange: CustomSchemaFormProps['onSetterChange'] = (keyPaths, setterName) => {\n    if (!node) {\n      return;\n    }\n    node.value.configure = node.value.configure || {};\n    node.value.configure.propsSetter = node.value.configure.propsSetter || {};\n    node.value.configure.propsSetter[keyPaths.join('.')] = {\n      name: keyPaths.join('.'),\n      setter: setterName,\n    };\n  };\n\n  const customSetterMap = props.pluginCtx.config?.customPropertySetterMap;\n\n  return (\n    <div className={styles.CFromRenderBox}>\n      <CustomSchemaForm\n        pluginCtx={props.pluginCtx}\n        key={node?.id}\n        nodeId={node?.id}\n        defaultSetterConfig={node?.value.configure.propsSetter || {}}\n        onSetterChange={onSetterChange}\n        properties={properties}\n        initialValue={value}\n        ref={formRef}\n        customSetterMap={{\n          ...BUILD_IN_ADVANCE_SETTER_MAP,\n          ...customSetterMap,\n        }}\n        onValueChange={onValueChange}\n      />\n    </div>\n  );\n};\n"
  },
  {
    "path": "packages/engine/src/plugins/RightPanel/context.ts",
    "content": "import React, { useContext } from 'react';\nimport { CPluginCtx } from '../../core/pluginManager';\nimport { CSetter } from '@/component';\nimport { CNode, CRootNode } from '@chamn/model';\n\nexport type ContextState = Record<string, any>;\n\nexport type CRightPanelData = {\n  /** 存储 field 默认的 setter 类型*/\n  defaultSetterConfig: Record<string, { name: string; setter: string }>;\n  /** schema 中的全局 setter map 配置*/\n  customSetterMap: Record<string, CSetter>;\n  pluginCtx?: CPluginCtx;\n  /** 当前编辑节点的 id */\n  nodeId?: string;\n  nodeModel?: CNode | CRootNode;\n};\n\nexport const CRightPanelContext = React.createContext<CRightPanelData>({\n  defaultSetterConfig: {},\n  customSetterMap: {},\n});\n\nexport const UseCRightPanelContext = () => {\n  return useContext(CRightPanelContext);\n};\n"
  },
  {
    "path": "packages/engine/src/plugins/RightPanel/index.tsx",
    "content": "import React from 'react';\nimport { CPlugin } from '../../core/pluginManager';\nimport { RightPanel } from './view';\nimport { RightPanelConfig } from './type';\n\nconst PLUGIN_NAME = 'RightPanel';\nexport const RightPanelPlugin: CPlugin<RightPanelConfig> = () => {\n  const uiHandle = React.createRef<RightPanel>();\n  return {\n    name: PLUGIN_NAME,\n    PLUGIN_NAME,\n    async init(ctx) {\n      const workbench = ctx.getWorkbench();\n      workbench.replaceRightView(<RightPanel ref={uiHandle} pluginCtx={ctx} />);\n    },\n    async destroy() {},\n    export: () => {\n      return uiHandle?.current;\n    },\n    meta: {\n      engine: {\n        version: '1.0.0',\n      },\n    },\n  };\n};\n\nRightPanelPlugin.PLUGIN_NAME = PLUGIN_NAME;\n"
  },
  {
    "path": "packages/engine/src/plugins/RightPanel/style.module.scss",
    "content": ".rightPanelContainer {\n  height: 100%;\n  width: 100%;\n  overflow: hidden;\n  :global {\n    .monaco-editor .overflowingContentWidgets {\n      overflow: visible !important;\n    }\n    .ant-tabs .ant-tabs-tab + .ant-tabs-tab {\n      margin-left: 0;\n    }\n    .ant-tabs-content.ant-tabs-content-top {\n      height: 100%;\n      overflow-x: hidden;\n      overflow-y: auto;\n    }\n    .ant-tabs .ant-tabs-tabpane {\n      height: 100%;\n    }\n  }\n}\n"
  },
  {
    "path": "packages/engine/src/plugins/RightPanel/type.ts",
    "content": "import { CSetter } from '@/component';\nimport { TLogicRequestAPIItem } from '@chamn/model';\n\nexport type RightPanelConfig = {\n  customPropertySetterMap?: Record<string, CSetter>;\n  requestAPINode?: {\n    customAPIInput: TCustomAPIInput;\n  };\n};\n\nexport type TCustomAPIInput = (props: {\n  value: any;\n  onChange: (value: any) => void;\n  form: {\n    updateFields: (newValue: Partial<TLogicRequestAPIItem>) => void;\n    getFields: () => Pick<TLogicRequestAPIItem, 'apiPath' | 'body' | 'header' | 'query' | 'method' | 'responseVarName'>;\n  };\n}) => React.ReactNode;\n"
  },
  {
    "path": "packages/engine/src/plugins/RightPanel/view.tsx",
    "content": "import { CNode, CRootNode } from '@chamn/model';\nimport { Empty, Tabs } from 'antd';\nimport React from 'react';\nimport { CPluginCtx } from '../../core/pluginManager';\nimport { PropertyPanelConfig } from '../PropertyPanel';\nimport { ComponentStatePanelConfig } from '../ComponentStatePanel';\nimport { AdvancePanelConfig } from '../AdvancePanel';\nimport styles from './style.module.scss';\nimport { VisualPanelPlusConfig } from '../VisualPanelPlus';\nimport { EventPanelConfig } from '../EventPanel';\nimport { CRightPanelContext } from './context';\n\nexport type RightPanelOptions = { node: CNode | CRootNode | null; pluginCtx: CPluginCtx; activeTab: string };\n\nexport type CRightPanelItem = {\n  key: string;\n  name: string | ((props: RightPanelOptions) => React.ReactNode);\n  view: (props: RightPanelOptions) => React.ReactNode;\n  show?: (options: RightPanelOptions) => boolean;\n};\n\ninterface RightPanelProps {\n  pluginCtx: CPluginCtx;\n}\n\ninterface RightPanelState {\n  node: CNode | CRootNode | null;\n  activeKey: string;\n  panels: CRightPanelItem[];\n  displayPanels: CRightPanelItem[];\n}\n\nexport class RightPanel extends React.Component<RightPanelProps, RightPanelState> {\n  constructor(props: RightPanelProps) {\n    super(props);\n    this.state = {\n      node: props.pluginCtx.engine.getActiveNode(),\n      activeKey: 'Visual',\n      panels: [\n        // AdvancePanelConfig,\n        PropertyPanelConfig,\n        VisualPanelPlusConfig,\n        EventPanelConfig,\n        ComponentStatePanelConfig,\n        AdvancePanelConfig,\n      ],\n      displayPanels: [],\n    };\n  }\n\n  addPanel = (panel: CRightPanelItem) => {\n    const newPanels = [...this.state.panels, panel];\n    this.setState({\n      panels: newPanels,\n    });\n    this.updatePanels();\n  };\n\n  removePanel = (panelName: string) => {\n    const newPanels = this.state.panels.filter((el) => el.name !== panelName);\n    this.setState({\n      panels: newPanels,\n    });\n    this.updatePanels();\n  };\n\n  replacePanel = (panelName: string, newPanel: CRightPanelItem) => {\n    const targetIndex = this.state.panels.findIndex((el) => el.name === panelName);\n    const newPanels = [...this.state.panels];\n    if (targetIndex > -1) {\n      newPanels[targetIndex] = newPanel;\n    }\n    this.setState({\n      panels: newPanels,\n    });\n    this.updatePanels();\n  };\n\n  choosePanel = (panelName: string) => {\n    this.setState({\n      activeKey: panelName,\n    });\n    this.updatePanels();\n  };\n\n  /** 更新被展示的 panel, 根据 panel 的 show 方法 */\n  updatePanels = () => {\n    const { pluginCtx } = this.props;\n    const { node, panels } = this.state;\n    const newPanels = panels;\n    let val: {\n      panels: CRightPanelItem[];\n      displayPanels: CRightPanelItem[];\n    } = { panels: [], displayPanels: [] };\n    if (node) {\n      const panelParams: RightPanelOptions = { node: node, pluginCtx, activeTab: this.state.activeKey };\n      const displayPanels = newPanels.filter((panel) => {\n        if (panel.show === undefined) {\n          return true;\n        } else {\n          return panel.show(panelParams);\n        }\n      });\n      // 获取物料中的自定义 panel\n      const customTabs = node.material?.value.advanceCustom?.rightPanel?.customTabs || [];\n      displayPanels.concat(customTabs);\n      val = {\n        panels: newPanels,\n        displayPanels,\n      };\n    } else {\n      val = {\n        panels: newPanels,\n        displayPanels: [],\n      };\n    }\n    this.setState(val);\n    return val;\n  };\n\n  onNodeChange = ({ node }: any) => {\n    const { pluginCtx } = this.props;\n    const { panels, activeKey } = this.state;\n    const panelParams: RightPanelOptions = { node: node, pluginCtx, activeTab: this.state.activeKey };\n    const displayPanels = panels.filter((panel) => {\n      if (panel.show === undefined) {\n        return true;\n      } else {\n        return panel.show(panelParams);\n      }\n    });\n    const firstPanelKey = displayPanels.find((_, index) => index === 0)?.key || '';\n    const isExitsCurrent = displayPanels.find((el) => el.key === activeKey);\n    if (!isExitsCurrent) {\n      this.setState({\n        activeKey: firstPanelKey,\n        node,\n        displayPanels,\n      });\n    } else {\n      this.setState({\n        node,\n        displayPanels,\n      });\n    }\n  };\n\n  componentDidMount(): void {\n    const { pluginCtx } = this.props;\n    pluginCtx.globalEmitter.on('onSelectNodeChange', (args) => {\n      setTimeout(() => {\n        this.onNodeChange(args);\n      }, 10);\n    });\n    pluginCtx.pageModel.emitter.on('*', () => {\n      const currentSelectNode = pluginCtx.engine.getActiveNode();\n      this.onNodeChange({ node: currentSelectNode });\n    });\n    const { displayPanels } = this.updatePanels();\n    const firstPanelKey = displayPanels.find((_, index) => index === 0)?.key || '';\n    const isExitsCurrent = displayPanels.find((el) => el.key === this.state.activeKey);\n\n    if (!isExitsCurrent) {\n      this.setState({\n        activeKey: firstPanelKey,\n      });\n    }\n\n    pluginCtx.pluginReadyOk();\n  }\n\n  render() {\n    const { displayPanels, node, activeKey } = this.state;\n    const { pluginCtx } = this.props;\n    if (!node) {\n      return (\n        <div style={{ overflow: 'hidden' }}>\n          <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={'Please select a node from left view'} />\n        </div>\n      );\n    }\n    const panelParams = { node: node, pluginCtx, activeTab: this.state.activeKey };\n    const customSetterMap = pluginCtx.config?.customPropertySetterMap;\n\n    return (\n      <CRightPanelContext.Provider\n        value={{\n          customSetterMap: { ...customSetterMap },\n          defaultSetterConfig: {},\n          nodeId: this.state.node?.id,\n          nodeModel: this.state.node as any,\n          pluginCtx: this.props.pluginCtx,\n        }}\n      >\n        <div className={styles.rightPanelContainer}>\n          <Tabs\n            activeKey={activeKey}\n            tabPosition=\"top\"\n            style={{\n              flex: 1,\n              height: '100%',\n            }}\n            onChange={(activeKey) => {\n              this.setState({\n                activeKey,\n              });\n            }}\n            items={displayPanels.map((p) => {\n              return {\n                label: (\n                  <div style={{ padding: '0 10px' }}>{typeof p.name === 'string' ? p.name : p.name?.(panelParams)}</div>\n                ),\n                key: p.key,\n                children: p.view(panelParams),\n              };\n            })}\n          />\n        </div>\n      </CRightPanelContext.Provider>\n    );\n  }\n}\n"
  },
  {
    "path": "packages/engine/src/plugins/VisualPanelPlus/index.tsx",
    "content": "/* eslint-disable react-refresh/only-export-components */\nimport { useCallback, useEffect, useMemo, useRef } from 'react';\nimport { CNode, CRootNode } from '@chamn/model';\nimport { CRightPanelItem, RightPanelOptions } from '../RightPanel/view';\n\nimport styles from './style.module.scss';\nimport {\n  CSSPropertiesVariableBindEditor,\n  CSSPropertiesVariableBindEditorRef,\n} from '../../component/CSSPropertiesVariableBindEditor';\nimport { Collapse } from 'antd';\nimport { ClassNameEditor, ClassNameEditorRef } from '@/component/ClassNameEditor';\nimport { CSSEditor, CSSEditorRef, CSSVal } from '@/component/CSSEditor';\nimport {\n  formatStyleProperty,\n  formatCssToNodeVal,\n  formatNodeValToEditor,\n  StyleArr,\n  styleArr2Obj,\n  styleObjToArr,\n} from '@/utils/css';\nimport { StyleUIPanel, StyleUIPanelRef } from '@/component/StylePanel';\n\nexport const VisualPanelPlus = (props: RightPanelOptions) => {\n  const styleVariableRef = useRef<CSSPropertiesVariableBindEditorRef>(null);\n\n  const node = props.node!;\n  const classNameList = useMemo(() => {\n    const tempList = node.value.classNames || [];\n    return tempList;\n  }, [node]);\n  const cssEditorRef = useRef<CSSEditorRef>(null);\n  const cssUIRef = useRef<StyleUIPanelRef>(null);\n  const classNameEditorRef = useRef<ClassNameEditorRef>(null);\n  const formatStyle = useMemo(() => {\n    return formatStyleProperty(node.value.style || []);\n  }, [node.value.style]);\n\n  const lastNode = useRef<CNode | CRootNode>();\n\n  const updatePanelValue = useCallback(() => {\n    lastNode.current = node;\n    const newStyle = node.value.style || [];\n    const { expressionProperty, normalProperty } = formatStyleProperty(newStyle);\n    const fCss = formatNodeValToEditor(node.value.css);\n    styleVariableRef.current?.setValue([...expressionProperty]);\n    cssEditorRef.current?.setValue(fCss);\n    classNameEditorRef.current?.setValue(node.value.classNames || []);\n    cssUIRef.current?.setValue(styleArr2Obj(normalProperty) || {});\n  }, [node]);\n\n  useEffect(() => {\n    updatePanelValue();\n    node.emitter.on('onNodeChange', updatePanelValue);\n    node.emitter.on('onReloadPage', updatePanelValue);\n    return () => {\n      node.emitter.off('onNodeChange', updatePanelValue);\n      node.emitter.off('onReloadPage', updatePanelValue);\n    };\n  }, [node.emitter, node.id, props.activeTab, updatePanelValue]);\n\n  const onUpdateStyleVariable = (styleArr: StyleArr) => {\n    // merge style\n    const newStyleList = [...formatStyle.normalProperty, ...styleArr];\n    node.value.style = newStyleList;\n    node.updateValue();\n  };\n\n  const onUpdateStyle = (styleArr: StyleArr) => {\n    // merge style\n    const newStyleList = [...styleArr, ...formatStyle.expressionProperty];\n    node.value.style = newStyleList;\n    node.updateValue();\n  };\n\n  const onUpdateCss = (val: CSSVal) => {\n    // class name 不能以数字开头，这里使用c_前缀\n    node.value.css = formatCssToNodeVal(`c_${node.id}`, val);\n    node.updateValue();\n  };\n\n  return (\n    <div className={styles.visualPanelBox}>\n      <div\n        style={{\n          marginBottom: '10px',\n        }}\n      >\n        <StyleUIPanel\n          ref={cssUIRef}\n          onValueChange={(newNormaCss) => {\n            onUpdateStyle(styleObjToArr(newNormaCss));\n          }}\n        />\n        <Collapse\n          bordered={false}\n          style={{\n            marginBottom: '10px',\n          }}\n          defaultActiveKey={['origin-css-edit']}\n          onChange={(val) => {\n            if (val.length) {\n              updatePanelValue();\n            }\n          }}\n          items={[\n            {\n              key: 'origin-css-edit',\n              label: <span className={styles.header}>Style Variable</span>,\n              children: (\n                <CSSPropertiesVariableBindEditor\n                  ref={styleVariableRef}\n                  initialValue={formatStyle.expressionProperty}\n                  onValueChange={(val) => {\n                    onUpdateStyleVariable(val);\n                  }}\n                />\n              ),\n            },\n          ]}\n        ></Collapse>\n        <div\n          style={{\n            paddingTop: '10px',\n          }}\n        >\n          <ClassNameEditor\n            nodeModel={props.node as any}\n            initialValue={classNameList}\n            ref={classNameEditorRef}\n            pluginContext={props.pluginCtx}\n            onValueChange={(newVal) => {\n              node.value.classNames = newVal;\n              node.updateValue();\n            }}\n          />\n        </div>\n        <CSSEditor handler={cssEditorRef} onValueChange={onUpdateCss} />\n      </div>\n    </div>\n  );\n};\n\nexport const VisualPanelPlusConfig: CRightPanelItem = {\n  key: 'VisualPanelPlus',\n  name: 'Visual',\n  view: ({ node, pluginCtx, activeTab }) => {\n    if (node) {\n      return <VisualPanelPlus node={node} pluginCtx={pluginCtx} activeTab={activeTab} />;\n    } else {\n      return <></>;\n    }\n  },\n  show: (props) => {\n    return props.node?.material?.value.advanceCustom?.rightPanel?.visual !== false;\n  },\n};\n"
  },
  {
    "path": "packages/engine/src/plugins/VisualPanelPlus/style.module.scss",
    "content": ".visualPanelBox {\n  padding: 0 15px 300px;\n\n  .header {\n    color: $fontColor;\n    display: flex;\n  }\n}\n\n:global {\n  .ant-collapse .ant-collapse-item .ant-collapse-header {\n    padding: 5px 5px 5px 10px;\n    align-items: center;\n    color: $fontColor;\n    font-size: 12px;\n    .ant-collapse-expand-icon {\n      padding-inline-end: 4px;\n    }\n  }\n}\n"
  },
  {
    "path": "packages/engine/src/plugins/index.tsx",
    "content": "/* eslint-disable react-refresh/only-export-components */\nimport { CPlugin } from '../core/pluginManager';\nimport { ComponentLibPlugin } from './ComponentLibrary';\nimport { DesignerPlugin } from './Designer';\nimport { OutlineTreePlugin } from './OutlineTree';\nimport { RightPanelPlugin } from './RightPanel';\nimport { GlobalStatePanelPlugin } from './GlobalStatePanel';\nimport { DisplaySourceSchema } from './DisplaySourceSchema';\nimport { HistoryPlugin } from './History';\nimport { HotkeysPlugin } from './Hotkeys';\n\nexport const DEFAULT_PLUGIN_LIST: CPlugin[] = [\n  DesignerPlugin,\n  OutlineTreePlugin,\n  ComponentLibPlugin,\n  GlobalStatePanelPlugin,\n  RightPanelPlugin,\n  HistoryPlugin,\n  HotkeysPlugin,\n];\n\nexport {\n  DesignerPlugin,\n  ComponentLibPlugin,\n  RightPanelPlugin,\n  OutlineTreePlugin,\n  GlobalStatePanelPlugin,\n  HistoryPlugin,\n  DisplaySourceSchema,\n  HotkeysPlugin,\n};\n\n/** 组件唯一名 */\nexport const DEFAULT_PLUGIN_NAME_MAP = {\n  HotkeysPlugin: HotkeysPlugin['PLUGIN_NAME']!,\n  ComponentLibPlugin: ComponentLibPlugin['PLUGIN_NAME']!,\n  RightPanelPlugin: RightPanelPlugin['PLUGIN_NAME']!,\n  GlobalStatePanelPlugin: GlobalStatePanelPlugin['PLUGIN_NAME']!,\n  HistoryPlugin: HistoryPlugin['PLUGIN_NAME']!,\n  DesignerPlugin: DesignerPlugin['PLUGIN_NAME']!,\n  OutlineTreePlugin: OutlineTreePlugin['PLUGIN_NAME']!,\n};\n\nexport * from './AdvancePanel/index';\nexport * from './ComponentLibrary/index';\nexport * from './ComponentStatePanel/index';\nexport * from './Designer/index';\nexport * from './DisplaySourceSchema/index';\nexport * from './GlobalStatePanel/index';\nexport * from './History';\nexport * from './OutlineTree';\nexport * from './PropertyPanel';\nexport * from './RightPanel';\nexport * from './RightPanel/type';\nexport * from './VisualPanelPlus';\nexport * from './Hotkeys';\n"
  },
  {
    "path": "packages/engine/src/stories/Button.jsx",
    "content": "import React from 'react';\n\nimport PropTypes from 'prop-types';\n\nimport './button.css';\n\n/** Primary UI component for user interaction */\nexport const Button = ({ primary, backgroundColor, size, label, ...props }) => {\n  const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary';\n  return (\n    <button\n      type=\"button\"\n      className={['storybook-button', `storybook-button--${size}`, mode].join(' ')}\n      style={backgroundColor && { backgroundColor }}\n      {...props}\n    >\n      {label}\n    </button>\n  );\n};\n\nButton.propTypes = {\n  /** Is this the principal call to action on the page? */\n  primary: PropTypes.bool,\n  /** What background color to use */\n  backgroundColor: PropTypes.string,\n  /** How large should the button be? */\n  size: PropTypes.oneOf(['small', 'medium', 'large']),\n  /** Button contents */\n  label: PropTypes.string.isRequired,\n  /** Optional click handler */\n  onClick: PropTypes.func,\n};\n\nButton.defaultProps = {\n  backgroundColor: null,\n  primary: false,\n  size: 'medium',\n  onClick: undefined,\n};\n"
  },
  {
    "path": "packages/engine/src/stories/Button.stories.js",
    "content": "import { fn } from '@storybook/test';\n\nimport { Button } from './Button';\n\n// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export\nexport default {\n  title: 'Example/Button',\n  component: Button,\n  parameters: {\n    // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout\n    layout: 'centered',\n  },\n  // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs\n  tags: ['autodocs'],\n  // More on argTypes: https://storybook.js.org/docs/api/argtypes\n  argTypes: {\n    backgroundColor: { control: 'color' },\n  },\n  // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args\n  args: { onClick: fn() },\n};\n\n// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args\nexport const Primary = {\n  args: {\n    primary: true,\n    label: 'Button',\n  },\n};\n\nexport const Secondary = {\n  args: {\n    label: 'Button',\n  },\n};\n\nexport const Large = {\n  args: {\n    size: 'large',\n    label: 'Button',\n  },\n};\n\nexport const Small = {\n  args: {\n    size: 'small',\n    label: 'Button',\n  },\n};\n"
  },
  {
    "path": "packages/engine/src/stories/Configure.mdx",
    "content": "import { Meta } from '@storybook/blocks';\n\nimport Github from './assets/github.svg';\nimport Discord from './assets/discord.svg';\nimport Youtube from './assets/youtube.svg';\nimport Tutorials from './assets/tutorials.svg';\nimport Styling from './assets/styling.png';\nimport Context from './assets/context.png';\nimport Assets from './assets/assets.png';\nimport Docs from './assets/docs.png';\nimport Share from './assets/share.png';\nimport FigmaPlugin from './assets/figma-plugin.png';\nimport Testing from './assets/testing.png';\nimport Accessibility from './assets/accessibility.png';\nimport Theming from './assets/theming.png';\nimport AddonLibrary from './assets/addon-library.png';\n\nexport const RightArrow = () => (\n  <svg\n    viewBox=\"0 0 14 14\"\n    width=\"8px\"\n    height=\"14px\"\n    style={{\n      marginLeft: '4px',\n      display: 'inline-block',\n      shapeRendering: 'inherit',\n      verticalAlign: 'middle',\n      fill: 'currentColor',\n      'path fill': 'currentColor',\n    }}\n  >\n    <path d=\"m11.1 7.35-5.5 5.5a.5.5 0 0 1-.7-.7L10.04 7 4.9 1.85a.5.5 0 1 1 .7-.7l5.5 5.5c.2.2.2.5 0 .7Z\" />\n  </svg>\n);\n\n<Meta title=\"Configure your project\" />\n\n<div className=\"sb-container\">\n  <div className='sb-section-title'>\n    # Configure your project\n\n    Because Storybook works separately from your app, you'll need to configure it for your specific stack and setup. Below, explore guides for configuring Storybook with popular frameworks and tools. If you get stuck, learn how you can ask for help from our community.\n\n  </div>\n  <div className=\"sb-section\">\n    <div className=\"sb-section-item\">\n      <img\n        src={Styling}\n        alt=\"A wall of logos representing different styling technologies\"\n      />\n      <h4 className=\"sb-section-item-heading\">Add styling and CSS</h4>\n      <p className=\"sb-section-item-paragraph\">Like with web applications, there are many ways to include CSS within Storybook. Learn more about setting up styling within Storybook.</p>\n      <a\n        href=\"https://storybook.js.org/docs/configure/styling-and-css/?renderer=react\"\n        target=\"_blank\"\n      >Learn more<RightArrow /></a>\n    </div>\n    <div className=\"sb-section-item\">\n      <img\n        src={Context}\n        alt=\"An abstraction representing the composition of data for a component\"\n      />\n      <h4 className=\"sb-section-item-heading\">Provide context and mocking</h4>\n      <p className=\"sb-section-item-paragraph\">Often when a story doesn't render, it's because your component is expecting a specific environment or context (like a theme provider) to be available.</p>\n      <a\n        href=\"https://storybook.js.org/docs/writing-stories/decorators/?renderer=react#context-for-mocking\"\n        target=\"_blank\"\n      >Learn more<RightArrow /></a>\n    </div>\n    <div className=\"sb-section-item\">\n      <img src={Assets} alt=\"A representation of typography and image assets\" />\n      <div>\n        <h4 className=\"sb-section-item-heading\">Load assets and resources</h4>\n        <p className=\"sb-section-item-paragraph\">To link static files (like fonts) to your projects and stories, use the\n        `staticDirs` configuration option to specify folders to load when\n        starting Storybook.</p>\n        <a\n          href=\"https://storybook.js.org/docs/configure/images-and-assets/?renderer=react\"\n          target=\"_blank\"\n        >Learn more<RightArrow /></a>\n      </div>\n    </div>\n  </div>\n</div>\n<div className=\"sb-container\">\n  <div className='sb-section-title'>\n    # Do more with Storybook\n\n    Now that you know the basics, let's explore other parts of Storybook that will improve your experience. This list is just to get you started. You can customise Storybook in many ways to fit your needs.\n\n  </div>\n\n  <div className=\"sb-section\">\n    <div className=\"sb-features-grid\">\n      <div className=\"sb-grid-item\">\n        <img src={Docs} alt=\"A screenshot showing the autodocs tag being set, pointing a docs page being generated\" />\n        <h4 className=\"sb-section-item-heading\">Autodocs</h4>\n        <p className=\"sb-section-item-paragraph\">Auto-generate living,\n          interactive reference documentation from your components and stories.</p>\n        <a\n          href=\"https://storybook.js.org/docs/writing-docs/autodocs/?renderer=react\"\n          target=\"_blank\"\n        >Learn more<RightArrow /></a>\n      </div>\n      <div className=\"sb-grid-item\">\n        <img src={Share} alt=\"A browser window showing a Storybook being published to a chromatic.com URL\" />\n        <h4 className=\"sb-section-item-heading\">Publish to Chromatic</h4>\n        <p className=\"sb-section-item-paragraph\">Publish your Storybook to review and collaborate with your entire team.</p>\n        <a\n          href=\"https://storybook.js.org/docs/sharing/publish-storybook/?renderer=react#publish-storybook-with-chromatic\"\n          target=\"_blank\"\n        >Learn more<RightArrow /></a>\n      </div>\n      <div className=\"sb-grid-item\">\n        <img src={FigmaPlugin} alt=\"Windows showing the Storybook plugin in Figma\" />\n        <h4 className=\"sb-section-item-heading\">Figma Plugin</h4>\n        <p className=\"sb-section-item-paragraph\">Embed your stories into Figma to cross-reference the design and live\n          implementation in one place.</p>\n        <a\n          href=\"https://storybook.js.org/docs/sharing/design-integrations/?renderer=react#embed-storybook-in-figma-with-the-plugin\"\n          target=\"_blank\"\n        >Learn more<RightArrow /></a>\n      </div>\n      <div className=\"sb-grid-item\">\n        <img src={Testing} alt=\"Screenshot of tests passing and failing\" />\n        <h4 className=\"sb-section-item-heading\">Testing</h4>\n        <p className=\"sb-section-item-paragraph\">Use stories to test a component in all its variations, no matter how\n          complex.</p>\n        <a\n          href=\"https://storybook.js.org/docs/writing-tests/?renderer=react\"\n          target=\"_blank\"\n        >Learn more<RightArrow /></a>\n      </div>\n      <div className=\"sb-grid-item\">\n        <img src={Accessibility} alt=\"Screenshot of accessibility tests passing and failing\" />\n        <h4 className=\"sb-section-item-heading\">Accessibility</h4>\n        <p className=\"sb-section-item-paragraph\">Automatically test your components for a11y issues as you develop.</p>\n        <a\n          href=\"https://storybook.js.org/docs/writing-tests/accessibility-testing/?renderer=react\"\n          target=\"_blank\"\n        >Learn more<RightArrow /></a>\n      </div>\n      <div className=\"sb-grid-item\">\n        <img src={Theming} alt=\"Screenshot of Storybook in light and dark mode\" />\n        <h4 className=\"sb-section-item-heading\">Theming</h4>\n        <p className=\"sb-section-item-paragraph\">Theme Storybook's UI to personalize it to your project.</p>\n        <a\n          href=\"https://storybook.js.org/docs/configure/theming/?renderer=react\"\n          target=\"_blank\"\n        >Learn more<RightArrow /></a>\n      </div>\n    </div>\n  </div>\n</div>\n<div className='sb-addon'>\n  <div className='sb-addon-text'>\n    <h4>Addons</h4>\n    <p className=\"sb-section-item-paragraph\">Integrate your tools with Storybook to connect workflows.</p>\n    <a\n        href=\"https://storybook.js.org/addons/\"\n        target=\"_blank\"\n      >Discover all addons<RightArrow /></a>\n  </div>\n  <div className='sb-addon-img'>\n    <img src={AddonLibrary} alt=\"Integrate your tools with Storybook to connect workflows.\" />\n  </div>\n</div>\n\n<div className=\"sb-section sb-socials\">\n    <div className=\"sb-section-item\">\n      <img src={Github} alt=\"Github logo\" className=\"sb-explore-image\"/>\n      Join our contributors building the future of UI development.\n\n      <a\n        href=\"https://github.com/storybookjs/storybook\"\n        target=\"_blank\"\n      >Star on GitHub<RightArrow /></a>\n    </div>\n    <div className=\"sb-section-item\">\n      <img src={Discord} alt=\"Discord logo\" className=\"sb-explore-image\"/>\n      <div>\n        Get support and chat with frontend developers.\n\n        <a\n          href=\"https://discord.gg/storybook\"\n          target=\"_blank\"\n        >Join Discord server<RightArrow /></a>\n      </div>\n    </div>\n    <div className=\"sb-section-item\">\n      <img src={Youtube} alt=\"Youtube logo\" className=\"sb-explore-image\"/>\n      <div>\n        Watch tutorials, feature previews and interviews.\n\n        <a\n          href=\"https://www.youtube.com/@chromaticui\"\n          target=\"_blank\"\n        >Watch on YouTube<RightArrow /></a>\n      </div>\n    </div>\n    <div className=\"sb-section-item\">\n      <img src={Tutorials} alt=\"A book\" className=\"sb-explore-image\"/>\n      <p>Follow guided walkthroughs on for key workflows.</p>\n\n      <a\n          href=\"https://storybook.js.org/tutorials/\"\n          target=\"_blank\"\n        >Discover tutorials<RightArrow /></a>\n    </div>\n\n</div>\n\n<style>\n  {`\n  .sb-container {\n    margin-bottom: 48px;\n  }\n\n  .sb-section {\n    width: 100%;\n    display: flex;\n    flex-direction: row;\n    gap: 20px;\n  }\n\n  img {\n    object-fit: cover;\n  }\n\n  .sb-section-title {\n    margin-bottom: 32px;\n  }\n\n  .sb-section a:not(h1 a, h2 a, h3 a) {\n    font-size: 14px;\n  }\n\n  .sb-section-item, .sb-grid-item {\n    flex: 1;\n    display: flex;\n    flex-direction: column;\n  }\n\n  .sb-section-item-heading {\n    padding-top: 20px !important;\n    padding-bottom: 5px !important;\n    margin: 0 !important;\n  }\n  .sb-section-item-paragraph {\n    margin: 0;\n    padding-bottom: 10px;\n  }\n\n  .sb-chevron {\n    margin-left: 5px;\n  }\n\n  .sb-features-grid {\n    display: grid;\n    grid-template-columns: repeat(2, 1fr);\n    grid-gap: 32px 20px;\n  }\n\n  .sb-socials {\n    display: grid;\n    grid-template-columns: repeat(4, 1fr);\n  }\n\n  .sb-socials p {\n    margin-bottom: 10px;\n  }\n\n  .sb-explore-image {\n    max-height: 32px;\n    align-self: flex-start;\n  }\n\n  .sb-addon {\n    width: 100%;\n    display: flex;\n    align-items: center;\n    position: relative;\n    background-color: #EEF3F8;\n    border-radius: 5px;\n    border: 1px solid rgba(0, 0, 0, 0.05);\n    background: #EEF3F8;\n    height: 180px;\n    margin-bottom: 48px;\n    overflow: hidden;\n  }\n\n  .sb-addon-text {\n    padding-left: 48px;\n    max-width: 240px;\n  }\n\n  .sb-addon-text h4 {\n    padding-top: 0px;\n  }\n\n  .sb-addon-img {\n    position: absolute;\n    left: 345px;\n    top: 0;\n    height: 100%;\n    width: 200%;\n    overflow: hidden;\n  }\n\n  .sb-addon-img img {\n    width: 650px;\n    transform: rotate(-15deg);\n    margin-left: 40px;\n    margin-top: -72px;\n    box-shadow: 0 0 1px rgba(255, 255, 255, 0);\n    backface-visibility: hidden;\n  }\n\n  @media screen and (max-width: 800px) {\n    .sb-addon-img {\n      left: 300px;\n    }\n  }\n\n  @media screen and (max-width: 600px) {\n    .sb-section {\n      flex-direction: column;\n    }\n\n    .sb-features-grid {\n      grid-template-columns: repeat(1, 1fr);\n    }\n\n    .sb-socials {\n      grid-template-columns: repeat(2, 1fr);\n    }\n\n    .sb-addon {\n      height: 280px;\n      align-items: flex-start;\n      padding-top: 32px;\n      overflow: hidden;\n    }\n\n    .sb-addon-text {\n      padding-left: 24px;\n    }\n\n    .sb-addon-img {\n      right: 0;\n      left: 0;\n      top: 130px;\n      bottom: 0;\n      overflow: hidden;\n      height: auto;\n      width: 124%;\n    }\n\n    .sb-addon-img img {\n      width: 1200px;\n      transform: rotate(-12deg);\n      margin-left: 0;\n      margin-top: 48px;\n      margin-bottom: -40px;\n      margin-left: -24px;\n    }\n  }\n  `}\n</style>\n"
  },
  {
    "path": "packages/engine/src/stories/Header.jsx",
    "content": "import React from 'react';\n\nimport PropTypes from 'prop-types';\n\nimport { Button } from './Button';\nimport './header.css';\n\nexport const Header = ({ user, onLogin, onLogout, onCreateAccount }) => (\n  <header>\n    <div className=\"storybook-header\">\n      <div>\n        <svg width=\"32\" height=\"32\" viewBox=\"0 0 32 32\" xmlns=\"http://www.w3.org/2000/svg\">\n          <g fill=\"none\" fillRule=\"evenodd\">\n            <path d=\"M10 0h12a10 10 0 0110 10v12a10 10 0 01-10 10H10A10 10 0 010 22V10A10 10 0 0110 0z\" fill=\"#FFF\" />\n            <path d=\"M5.3 10.6l10.4 6v11.1l-10.4-6v-11zm11.4-6.2l9.7 5.5-9.7 5.6V4.4z\" fill=\"#555AB9\" />\n            <path d=\"M27.2 10.6v11.2l-10.5 6V16.5l10.5-6zM15.7 4.4v11L6 10l9.7-5.5z\" fill=\"#91BAF8\" />\n          </g>\n        </svg>\n        <h1>Acme</h1>\n      </div>\n      <div>\n        {user ? (\n          <>\n            <span className=\"welcome\">\n              Welcome, <b>{user.name}</b>!\n            </span>\n            <Button size=\"small\" onClick={onLogout} label=\"Log out\" />\n          </>\n        ) : (\n          <>\n            <Button size=\"small\" onClick={onLogin} label=\"Log in\" />\n            <Button primary size=\"small\" onClick={onCreateAccount} label=\"Sign up\" />\n          </>\n        )}\n      </div>\n    </div>\n  </header>\n);\n\nHeader.propTypes = {\n  user: PropTypes.shape({\n    name: PropTypes.string.isRequired,\n  }),\n  onLogin: PropTypes.func.isRequired,\n  onLogout: PropTypes.func.isRequired,\n  onCreateAccount: PropTypes.func.isRequired,\n};\n\nHeader.defaultProps = {\n  user: null,\n};\n"
  },
  {
    "path": "packages/engine/src/stories/Header.stories.js",
    "content": "import { fn } from '@storybook/test';\n\nimport { Header } from './Header';\n\nexport default {\n  title: 'Example/Header',\n  component: Header,\n  // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs\n  tags: ['autodocs'],\n  parameters: {\n    // More on how to position stories at: https://storybook.js.org/docs/configure/story-layout\n    layout: 'fullscreen',\n  },\n  args: {\n    onLogin: fn(),\n    onLogout: fn(),\n    onCreateAccount: fn(),\n  },\n};\n\nexport const LoggedIn = {\n  args: {\n    user: {\n      name: 'Jane Doe',\n    },\n  },\n};\n\nexport const LoggedOut = {};\n"
  },
  {
    "path": "packages/engine/src/stories/Page.jsx",
    "content": "import React from 'react';\n\nimport { Header } from './Header';\nimport './page.css';\n\nexport const Page = () => {\n  const [user, setUser] = React.useState();\n\n  return (\n    <article>\n      <Header\n        user={user}\n        onLogin={() => setUser({ name: 'Jane Doe' })}\n        onLogout={() => setUser(undefined)}\n        onCreateAccount={() => setUser({ name: 'Jane Doe' })}\n      />\n\n      <section className=\"storybook-page\">\n        <h2>Pages in Storybook</h2>\n        <p>\n          We recommend building UIs with a{' '}\n          <a href=\"https://componentdriven.org\" target=\"_blank\" rel=\"noopener noreferrer\">\n            <strong>component-driven</strong>\n          </a>{' '}\n          process starting with atomic components and ending with pages.\n        </p>\n        <p>\n          Render pages with mock data. This makes it easy to build and review page states without needing to navigate to\n          them in your app. Here are some handy patterns for managing page data in Storybook:\n        </p>\n        <ul>\n          <li>\n            Use a higher-level connected component. Storybook helps you compose such data from the args of child\n            component stories\n          </li>\n          <li>\n            Assemble data in the page component from your services. You can mock these services out using Storybook.\n          </li>\n        </ul>\n        <p>\n          Get a guided tutorial on component-driven development at{' '}\n          <a href=\"https://storybook.js.org/tutorials/\" target=\"_blank\" rel=\"noopener noreferrer\">\n            Storybook tutorials\n          </a>\n          . Read more in the{' '}\n          <a href=\"https://storybook.js.org/docs\" target=\"_blank\" rel=\"noopener noreferrer\">\n            docs\n          </a>\n          .\n        </p>\n        <div className=\"tip-wrapper\">\n          <span className=\"tip\">Tip</span> Adjust the width of the canvas with the{' '}\n          <svg width=\"10\" height=\"10\" viewBox=\"0 0 12 12\" xmlns=\"http://www.w3.org/2000/svg\">\n            <g fill=\"none\" fillRule=\"evenodd\">\n              <path\n                d=\"M1.5 5.2h4.8c.3 0 .5.2.5.4v5.1c-.1.2-.3.3-.4.3H1.4a.5.5 0 01-.5-.4V5.7c0-.3.2-.5.5-.5zm0-2.1h6.9c.3 0 .5.2.5.4v7a.5.5 0 01-1 0V4H1.5a.5.5 0 010-1zm0-2.1h9c.3 0 .5.2.5.4v9.1a.5.5 0 01-1 0V2H1.5a.5.5 0 010-1zm4.3 5.2H2V10h3.8V6.2z\"\n                id=\"a\"\n                fill=\"#999\"\n              />\n            </g>\n          </svg>\n          Viewports addon in the toolbar\n        </div>\n      </section>\n    </article>\n  );\n};\n"
  },
  {
    "path": "packages/engine/src/stories/Page.stories.js",
    "content": "import { expect, userEvent, within } from '@storybook/test';\n\nimport { Page } from './Page';\n\nexport default {\n  title: 'Example/Page',\n  component: Page,\n  parameters: {\n    // More on how to position stories at: https://storybook.js.org/docs/configure/story-layout\n    layout: 'fullscreen',\n  },\n};\n\nexport const LoggedOut = {};\n\n// More on interaction testing: https://storybook.js.org/docs/writing-tests/interaction-testing\nexport const LoggedIn = {\n  play: async ({ canvasElement }) => {\n    const canvas = within(canvasElement);\n    const loginButton = canvas.getByRole('button', { name: /Log in/i });\n    await expect(loginButton).toBeInTheDocument();\n    await userEvent.click(loginButton);\n    await expect(loginButton).not.toBeInTheDocument();\n\n    const logoutButton = canvas.getByRole('button', { name: /Log out/i });\n    await expect(logoutButton).toBeInTheDocument();\n  },\n};\n"
  },
  {
    "path": "packages/engine/src/stories/button.css",
    "content": ".storybook-button {\n  display: inline-block;\n  cursor: pointer;\n  border: 0;\n  border-radius: 3em;\n  font-weight: 700;\n  line-height: 1;\n  font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;\n}\n.storybook-button--primary {\n  background-color: #1ea7fd;\n  color: white;\n}\n.storybook-button--secondary {\n  box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset;\n  background-color: transparent;\n  color: #333;\n}\n.storybook-button--small {\n  padding: 10px 16px;\n  font-size: 12px;\n}\n.storybook-button--medium {\n  padding: 11px 20px;\n  font-size: 14px;\n}\n.storybook-button--large {\n  padding: 12px 24px;\n  font-size: 16px;\n}\n"
  },
  {
    "path": "packages/engine/src/stories/components/CustomSchemaForm/components/Setters/actionFlow.stories.tsx",
    "content": "import { ActionFlowSetter } from '@/component/CustomSchemaForm/components/Setters/ActionFlowSetter';\nimport { CNodePropsTypeEnum, CPage } from '@chamn/model';\nimport { SamplePage, Material } from '@chamn/demo-page';\nimport { logicListSchema } from './mock';\nimport { useEffect, useState } from 'react';\nimport { node } from 'globals';\n\n// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export\nexport default {\n  title: 'Setter/ActionFlowSetter',\n  component: ActionFlowSetter,\n  parameters: {\n    layout: 'centered',\n  },\n  tags: ['autodocs'],\n  argTypes: {},\n};\n\nexport const Demo = {\n  args: {},\n  render: function UseRender() {\n    const [value, setValue] = useState<any>();\n    const [ready, setReady] = useState(false);\n\n    useEffect(() => {\n      const val = localStorage.getItem('ActionFlowSetterSchema');\n      if (val) {\n        setValue(JSON.parse(val));\n      } else {\n        setValue({\n          type: CNodePropsTypeEnum.ACTION,\n          handler: logicListSchema,\n        });\n      }\n      setReady(true);\n    }, []);\n\n    return (\n      <div\n        style={{\n          width: '90vw',\n          height: '90vh',\n        }}\n      >\n        {ready && (\n          <ActionFlowSetter\n            value={value}\n            setterContext={{\n              pluginCtx: {\n                pageModel: new CPage(SamplePage, { materials: Material }),\n              } as any,\n              setCollapseHeaderExt: undefined,\n              onSetterChange: function () {},\n              keyPaths: [],\n              label: '',\n              nodeModel: node as any,\n            }}\n            onValueChange={(newValue) => {\n              localStorage.setItem('ActionFlowSetterSchema', JSON.stringify(newValue));\n            }}\n          />\n        )}\n      </div>\n    );\n  },\n};\n"
  },
  {
    "path": "packages/engine/src/stories/components/CustomSchemaForm/components/Setters/mock.ts",
    "content": "import { REACT_FLOW_DRAG_CLASS_NAME } from '@/component/CustomSchemaForm/components/Setters/ActionFlowSetter/config';\nimport { NODE_TYPE } from '@/component/CustomSchemaForm/components/Setters/ActionFlowSetter/node';\nimport {\n  LogicType,\n  DEV_CONFIG_KEY,\n  AssignValueType,\n  TLogicAssignValueItem,\n  TLogicJumpLinkItem,\n  TLogicItemHandlerFlow,\n  getRandomStr,\n} from '@chamn/model';\n\nexport const mockNodeList = [\n  {\n    data: {},\n    position: { x: 0, y: 0 },\n    type: NODE_TYPE.START_NODE,\n    dragHandle: `.${REACT_FLOW_DRAG_CLASS_NAME}`,\n    selectable: false,\n  },\n  {\n    type: NODE_TYPE.RUN_CODE,\n    data: {\n      value: 'console.log(123)',\n    },\n    dragHandle: `.${REACT_FLOW_DRAG_CLASS_NAME}`,\n    position: { x: 0, y: 0 },\n  },\n  {\n    type: LogicType.REQUEST_API,\n    data: {\n      value: 'console.log(123)',\n    },\n    dragHandle: `.${REACT_FLOW_DRAG_CLASS_NAME}`,\n    position: { x: 0, y: 0 },\n  },\n\n  {\n    type: LogicType.ASSIGN_VALUE,\n    data: {\n      ...{\n        type: 'ASSIGN_VALUE',\n        nodeId: 'globalStateText',\n        methodName: 'doAlert',\n        args: ['123', { type: 'EXPRESSION', value: 'q2123' }],\n      },\n      [DEV_CONFIG_KEY]: {\n        defaultSetterMap: { 'args.1': { name: 'args.1', setter: 'ExpressionSetter' } },\n      },\n    },\n    dragHandle: `.${REACT_FLOW_DRAG_CLASS_NAME}`,\n    position: { x: 0, y: 0 },\n  },\n];\n\nexport const logicListSchema: TLogicItemHandlerFlow = [\n  {\n    type: 'JUMP_LINK',\n    link: {\n      type: 'EXPRESSION',\n      value: '$$context.state.link',\n    },\n  } as TLogicJumpLinkItem,\n  {\n    type: 'JUMP_LINK',\n    link: {\n      type: 'FUNCTION',\n      sourceCode: `function () {\n      return $$context.state.link;\n    }`,\n      value: `function () {\n    console.log('jump3');\n      return $$context.state.link;\n    }`,\n    },\n  } as TLogicJumpLinkItem,\n  {\n    type: 'ASSIGN_VALUE',\n    valueType: AssignValueType.STATE,\n    currentValue: 123,\n    targetValueName: 'Asd',\n  } as TLogicAssignValueItem,\n  {\n    type: 'ASSIGN_VALUE',\n    valueType: AssignValueType.STATE,\n    currentValue: {\n      type: 'EXPRESSION',\n      value: '$$context',\n    },\n    targetValueName: {\n      keyPath: 'testName',\n      nodeId: '12323',\n    },\n  } as TLogicAssignValueItem,\n  {\n    type: 'ASSIGN_VALUE',\n    valueType: 'MEMORY',\n    currentValue: {\n      type: 'FUNCTION',\n      value: 'console.log(12444)',\n    },\n    targetValueName: 'Asd',\n  } as TLogicAssignValueItem,\n  {\n    id: getRandomStr(),\n    type: LogicType.REQUEST_API,\n    apiPath: '',\n    method: 'GET',\n    header: {},\n    query: {},\n    responseVarName: `responseData_${getRandomStr()}`,\n    afterSuccessResponse: [\n      {\n        type: 'ASSIGN_VALUE',\n        valueType: 'MEMORY',\n        currentValue: {\n          type: 'FUNCTION',\n          value: 'console.log(12444)',\n        },\n        targetValueName: 'Asd',\n      } as TLogicAssignValueItem,\n    ],\n    afterFailedResponse: [\n      {\n        id: getRandomStr(),\n        type: LogicType.RUN_CODE,\n        value: 'console.error(\"API request failed:\", $$response)',\n      },\n    ],\n  },\n];\n"
  },
  {
    "path": "packages/engine/src/stories/components/inputPlus.stories.tsx",
    "content": "// Button.stories.ts|tsx\n\nimport { Meta, StoryFn } from '@storybook/react';\nimport { CSSSizeInput } from '../../component/CSSSizeInput';\nimport { useState } from 'react';\n\nconst TargetComponent = CSSSizeInput;\n\nexport default {\n  /* 👇 The title prop is optional.\n   * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading\n   * to learn how to generate automatic titles\n   */\n  title: 'CSSSizeInput',\n  component: TargetComponent,\n  decorators: [\n    (Story) => (\n      <div style={{ width: '500px' }}>\n        <Story />\n      </div>\n    ),\n  ],\n} as Meta<typeof TargetComponent>;\n\nconst Template: StoryFn<typeof TargetComponent> = (args) => {\n  const [val, setVal] = useState('100px');\n  return (\n    <>\n      <TargetComponent\n        value={val}\n        onValueChange={(newVal) => setVal(newVal || '')}\n        {...args}\n        min={0}\n        max={100}\n        cumulativeTransform={(data) => {\n          const { cumulativeX } = data;\n          data.cumulativeX = Math.ceil(cumulativeX / 10);\n          return data;\n        }}\n      />\n      <span>value: {val}</span>\n    </>\n  );\n};\n\nexport const Default = () => <Template></Template>;\n"
  },
  {
    "path": "packages/engine/src/stories/header.css",
    "content": ".storybook-header {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  border-bottom: 1px solid rgba(0, 0, 0, 0.1);\n  padding: 15px 20px;\n  font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;\n}\n\n.storybook-header svg {\n  display: inline-block;\n  vertical-align: top;\n}\n\n.storybook-header h1 {\n  display: inline-block;\n  vertical-align: top;\n  margin: 6px 0 6px 10px;\n  font-weight: 700;\n  font-size: 20px;\n  line-height: 1;\n}\n\n.storybook-header button + button {\n  margin-left: 10px;\n}\n\n.storybook-header .welcome {\n  margin-right: 10px;\n  color: #333;\n  font-size: 14px;\n}\n"
  },
  {
    "path": "packages/engine/src/stories/page.css",
    "content": ".storybook-page {\n  margin: 0 auto;\n  padding: 48px 20px;\n  max-width: 600px;\n  color: #333;\n  font-size: 14px;\n  line-height: 24px;\n  font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;\n}\n\n.storybook-page h2 {\n  display: inline-block;\n  vertical-align: top;\n  margin: 0 0 4px;\n  font-weight: 700;\n  font-size: 32px;\n  line-height: 1;\n}\n\n.storybook-page p {\n  margin: 1em 0;\n}\n\n.storybook-page a {\n  color: #1ea7fd;\n  text-decoration: none;\n}\n\n.storybook-page ul {\n  margin: 1em 0;\n  padding-left: 30px;\n}\n\n.storybook-page li {\n  margin-bottom: 8px;\n}\n\n.storybook-page .tip {\n  display: inline-block;\n  vertical-align: top;\n  margin-right: 10px;\n  border-radius: 1em;\n  background: #e7fdd8;\n  padding: 4px 12px;\n  color: #66bf3c;\n  font-weight: 700;\n  font-size: 11px;\n  line-height: 12px;\n}\n\n.storybook-page .tip-wrapper {\n  margin-top: 40px;\n  margin-bottom: 40px;\n  font-size: 13px;\n  line-height: 20px;\n}\n\n.storybook-page .tip-wrapper svg {\n  display: inline-block;\n  vertical-align: top;\n  margin-top: 3px;\n  margin-right: 4px;\n  width: 12px;\n  height: 12px;\n}\n\n.storybook-page .tip-wrapper svg path {\n  fill: #1ea7fd;\n}\n"
  },
  {
    "path": "packages/engine/src/stories/plugins/CSSEditor.stories.tsx",
    "content": "import { Meta, StoryFn } from '@storybook/react';\nimport { VisualPanelPlus } from '../../plugins/VisualPanelPlus';\nimport { CPage } from '@chamn/model';\nimport { PluginManager } from '../../core/pluginManager';\nimport mitt from 'mitt';\nimport customI18n from '../../i18n';\nimport { BasePage } from '@chamn/demo-page';\nimport { CSSEditor } from '../../component/CSSEditor';\nimport { AssetsPackageListManager } from '../../core/assetPackagesListManage';\n\nconst TargetComponent = CSSEditor;\n\nexport default {\n  /* 👇 The title prop is optional.\n   * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading\n   * to learn how to generate automatic titles\n   */\n  title: 'CSSEditor',\n  component: VisualPanelPlus,\n  decorators: [\n    (Story) => (\n      <div style={{ width: '500px' }}>\n        <Story />\n      </div>\n    ),\n  ],\n} as Meta<typeof VisualPanelPlus>;\n\nconst Template: StoryFn<typeof TargetComponent> = () => <TargetComponent />;\n\n// const node = new CNode({\n//   props: {\n//     width: '100px',\n//     height: '100px',\n//     style: {\n//       width: '100%',\n//       'background-repeat': 'no-repeat',\n//       'background-position': 'center',\n//       'background-size': 'cover',\n//       'flex-shrink': '0',\n//       height: '100%',\n//       'Webkit-transform': 'translate3d(0, 0, 0)',\n//       'background-image': {\n//         type: 'EXPRESSION',\n//         value: '`url(\"${$$context.loopData.item}\")`',\n//       },\n//     },\n//     $$attributes: [],\n//   },\n//   componentName: 'CBlock',\n//   id: 'v59d71',\n//   configure: {\n//     propsSetter: {},\n//     advanceSetter: {\n//       'loop.data': {\n//         name: 'loop.data',\n//         setter: 'ExpressionSetter',\n//       },\n//     },\n//   },\n// });\n\nconst pluginManager = new PluginManager({\n  emitter: mitt(),\n  getWorkbench: () => {\n    return {} as any;\n  },\n  i18n: customI18n,\n  pageModel: new CPage(BasePage),\n  engine: {} as any,\n  assetsPackageListManager: new AssetsPackageListManager([]),\n});\n\nconst ctx = pluginManager.createPluginCtx();\nconsole.log('🚀 ~ ctx:', ctx);\nexport const Default = () => <Template></Template>;\n"
  },
  {
    "path": "packages/engine/src/stories/plugins/VisualPanelPlus.stories.tsx",
    "content": "// Button.stories.ts|tsx\n\nimport { VisualPanelPlus } from '@/plugins/VisualPanelPlus';\nimport { CNode, CPage } from '@chamn/model';\nimport { PluginManager } from '../../core/pluginManager';\nimport mitt from 'mitt';\nimport customI18n from '../../i18n';\nimport { BasePage } from '@chamn/demo-page';\nimport { AssetsPackageListManager } from '../../core/assetPackagesListManage';\n\nconst TargetComponent = VisualPanelPlus;\n\nexport default {\n  /* 👇 The title prop is optional.\n   * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading\n   * to learn how to generate automatic titles\n   */\n  title: 'VisualPanelPlus',\n  component: VisualPanelPlus,\n};\n\nconst node = new CNode({\n  props: {\n    width: '100px',\n    height: '100px',\n    style: {\n      width: '100%',\n      'background-repeat': 'no-repeat',\n      'background-position': 'center',\n      'background-size': 'cover',\n      'flex-shrink': '0',\n      height: '100%',\n      'Webkit-transform': 'translate3d(0, 0, 0)',\n      'background-image': {\n        type: 'EXPRESSION',\n        value: '`url(\"${$$context.loopData.item}\")`',\n      },\n    },\n    $$attributes: [],\n  },\n  componentName: 'CBlock',\n  id: 'v59d71',\n  configure: {\n    propsSetter: {},\n    advanceSetter: {\n      'loop.data': {\n        name: 'loop.data',\n        setter: 'ExpressionSetter',\n      },\n    },\n  },\n});\n\nconst pluginManager = new PluginManager({\n  assetsPackageListManager: new AssetsPackageListManager([]),\n  emitter: mitt(),\n  getWorkbench: () => {\n    return {} as any;\n  },\n  i18n: customI18n,\n  pageModel: new CPage(BasePage),\n  engine: {} as any,\n});\n\nconst ctx = pluginManager.createPluginCtx();\nexport const Default = () => <TargetComponent node={node} pluginCtx={ctx} activeTab={''}></TargetComponent>;\n"
  },
  {
    "path": "packages/engine/src/stories/setters/colorSetter.stories.tsx",
    "content": "// Button.stories.ts|tsx\n\nimport { Meta, StoryFn } from '@storybook/react';\nimport { ColorSetter } from '../../component/CustomSchemaForm/components/Setters/ColorSetter';\nimport { useState } from 'react';\n\nconst TargetComponent = ColorSetter;\n\nexport default {\n  /* 👇 The title prop is optional.\n   * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading\n   * to learn how to generate automatic titles\n   */\n  title: 'ColorSetter',\n  component: TargetComponent,\n  decorators: [\n    (Story) => (\n      <div style={{ width: '500px' }}>\n        <Story />\n      </div>\n    ),\n  ],\n} as Meta<typeof TargetComponent>;\n\nconst Template: StoryFn<typeof TargetComponent> = (args) => {\n  const [color, setColor] = useState<string>('white');\n  return <TargetComponent value={color} onValueChange={(newColor: string) => setColor(newColor)} {...args} />;\n};\n\nexport const Default = () => <Template></Template>;\n"
  },
  {
    "path": "packages/engine/src/style.d.ts",
    "content": "declare const classNames: any;\nexport = classNames;\n"
  },
  {
    "path": "packages/engine/src/type.ts",
    "content": "import { CPageDataType, CMaterialType, AssetPackage } from '@chamn/model';\nimport { Engine, WorkbenchPropsType } from '.';\nimport { PluginManager, CPlugin } from './core/pluginManager';\nimport { RenderPropsType } from '@chamn/render';\n\nexport type EnginContext = {\n  pluginManager: PluginManager;\n  engine: Engine;\n};\n\nexport type EngineProps = {\n  plugins: CPlugin[];\n  schema: CPageDataType;\n  material?: CMaterialType[];\n  components?: Record<string, any>;\n  assetPackagesList?: AssetPackage[];\n  beforePluginRun?: (options: { pluginManager: PluginManager }) => void;\n  /** 所有的加插件加载完成 */\n  onReady?: (ctx: EnginContext) => void;\n  onMount?: (ctx: EnginContext) => void;\n  /** 渲染器 umd 格式 js 地址, 默认 ./render.umd.js */\n  renderJSUrl?: string;\n  style?: React.CSSProperties;\n  className?: string;\n  renderProps?: Partial<RenderPropsType>;\n  /** 配置 workbench 的属性，初始化时生效，后续修改不会生效，只能通过 API 变更 */\n  workbenchConfig?: Partial<WorkbenchPropsType>;\n  monacoEditor?: {\n    cndUrl?: string;\n  };\n};\n"
  },
  {
    "path": "packages/engine/src/typing.d.ts",
    "content": "declare module '*.scss' {\n  const content: { [key: string]: any };\n  export = content;\n}\n\ndeclare module '*.worker?worker' {\n  const content: any;\n  export = content;\n}\n"
  },
  {
    "path": "packages/engine/src/utils/css.ts",
    "content": "import { CSSVal } from '@/component/CSSEditor';\nimport { CNodeModelDataType, CSSType, CSSValue, JSExpressionPropType, getRandomStr, isExpression } from '@chamn/model';\nimport Color from 'color';\nimport { isPlainObject } from 'lodash-es';\nimport * as csstree from 'css-tree';\n\nexport type StyleArr = {\n  property: string;\n  value: any;\n}[];\n\nexport const styleArr2Obj = (val: StyleArr) => {\n  const res: Record<string, any> = {};\n  val.forEach((item) => {\n    res[item.property] = item.value;\n  });\n  return res;\n};\n\nexport const styleObjToArr = (obj: Record<string, any> = {}) => {\n  const res: StyleArr = Object.keys(obj).reduce((tempRes, key) => {\n    return [\n      ...tempRes,\n      {\n        property: key,\n        value: obj[key],\n      },\n    ];\n  }, [] as StyleArr);\n\n  return res;\n};\n\nexport const styleList2Text = (val: StyleArr) => {\n  let res = '';\n  val.forEach((item) => {\n    res += `${item.property}:${item.value};`;\n  });\n  return res;\n};\n\nexport const formatStyleProperty = (styleList: CNodeModelDataType['style'] = []) => {\n  // 遗留数据结构兼容, 正式版本会移除\n  if (isPlainObject(styleList)) {\n    styleList = Object.keys(styleList).map((key) => {\n      return {\n        property: key,\n        value: (styleList as any)[key],\n      };\n    });\n  }\n  const normalProperty: { property: string; value: string }[] = [];\n  const expressionProperty: { property: string; value: JSExpressionPropType }[] = [];\n  const allProperty: { property: string; value: any }[] = [];\n  styleList.forEach((it) => {\n    const val = it.value;\n    const obj = {\n      property: it.property,\n      value: val,\n    };\n\n    allProperty.push(obj);\n    if (isExpression(val)) {\n      expressionProperty.push({\n        ...obj,\n        value: val as unknown as JSExpressionPropType,\n      });\n    } else {\n      normalProperty.push({\n        ...obj,\n        value: val as unknown as string,\n      });\n    }\n  });\n  const res = {\n    normalProperty,\n    expressionProperty,\n    allProperty,\n  };\n  return res;\n};\n\nexport const formatCSSTextProperty = (cssText: string) => {\n  // 解析 CSS 代码\n  const ast = csstree.parse(`.node {${cssText}}`);\n\n  const cssList: { id: string; property: string; value: string }[] = [];\n\n  // 遍历 AST 获取属性\n  csstree.walk(ast, function (node) {\n    if (node.type === 'Declaration') {\n      cssList.push({\n        id: getRandomStr(),\n        property: node.property,\n        value: csstree.generate(node.value),\n      });\n    }\n  });\n\n  return cssList;\n};\n\nexport const formatCssToNodeVal = (className: string, val: CSSVal): CSSType => {\n  type StateType = keyof CSSVal;\n  const res: CSSType = {\n    class: className,\n    value: [],\n  };\n  const cssList: CSSValue[] = [];\n  Object.keys(val).forEach((state) => {\n    const currentStateCss = val[state as StateType];\n    const tempVal: CSSValue = {\n      state: state,\n      media: [],\n      text: currentStateCss?.['normal'] || '',\n    };\n    Object.keys(currentStateCss || ({} as any)).forEach((mediaKey) => {\n      tempVal.media = tempVal.media || [];\n      if (mediaKey !== 'normal') {\n        tempVal.media.push({\n          type: 'max-width',\n          value: mediaKey,\n          text: currentStateCss?.[mediaKey] || '',\n        });\n      }\n    });\n    cssList.push(tempVal);\n  });\n  res.value = cssList;\n  return res;\n};\n\nexport const formatNodeValToEditor = (val?: CSSType): CSSVal => {\n  if (!val) {\n    return {};\n  }\n  const list = val.value;\n  const res: CSSVal = {};\n\n  list.forEach((el) => {\n    const tempStyle = (el as any).style;\n    // 遗留数据兼容，正式版本会移除\n    if (!el.text && tempStyle) {\n      el.text = Object.keys(tempStyle).reduce((cssText, key) => {\n        cssText += `${key}: ${tempStyle[key]};`;\n        return cssText;\n      }, '');\n    }\n\n    const currentStateCss: Record<string, string> = {\n      normal: el.text || '',\n    };\n    el.media?.forEach((it) => {\n      currentStateCss[it.value] = it.text || '';\n    });\n    res[el.state as keyof CSSVal] = currentStateCss;\n  });\n\n  return res;\n};\n\nexport const getColorFromStr = (str: string) => {\n  // 匹配16进制颜色（包括#号）\n  const hexColorPattern = /#(?:[0-9a-fA-F]{3}){1,2}\\b/g;\n\n  // 匹配RGBA颜色\n  const rgbaColorPattern = /rgba?\\(\\s*\\d+\\s*,\\s*\\d+\\s*,\\s*\\d+\\s*,\\s*(?:\\d*\\.\\d+|\\d+\\.\\d*|\\d+)\\s*\\)/g;\n\n  // 匹配RGB颜色\n  const rgbColorPattern = /rgb?\\(\\s*\\d+\\s*,\\s*\\d+\\s*,\\s*\\d+\\s*\\)/g;\n\n  // 匹配HSB颜色\n  const hsbColorPattern = /hsb?\\(\\s*\\d+\\s*,\\s*\\d+%\\s*,\\s*\\d+%\\s*\\)/g;\n  const hexColors = str.match(hexColorPattern) || [];\n  const rgbaColors = str.match(rgbaColorPattern) || [];\n  const rgbColors = str.match(rgbColorPattern) || [];\n  const hsbColors = str.match(hsbColorPattern) || [];\n\n  return [...hexColors, ...rgbaColors, ...rgbColors, ...hsbColors];\n};\n\nexport type BoxShadowObjType = {\n  offsetX: string | undefined;\n  offsetY: string | undefined;\n  blur: string | undefined;\n  spread: string | undefined;\n  color: string | undefined;\n  type?: 'inset' | '';\n};\n\nexport const parseBoxShadowString = (str: string) => {\n  // 先匹配所有的 rgb（a）, 转换为对应的 16 进制,\n  const colorsList = getColorFromStr(str);\n  const color16Map = colorsList.reduce((res, c) => {\n    res[c] = Color(c).hexa();\n    return res;\n  }, {} as any);\n  // 替换字符串中所有的颜色为 16 进制，避免逗号影响分隔\n  let tempStr = str;\n  colorsList.forEach((c) => {\n    tempStr = tempStr.replaceAll(c, color16Map[c]);\n  });\n\n  // 分割逗号\n  const res = tempStr.split(',').map((el) => {\n    const shadowItem: BoxShadowObjType = {\n      offsetX: undefined,\n      offsetY: undefined,\n      blur: undefined,\n      spread: undefined,\n      color: undefined,\n      type: undefined,\n    };\n    let newStr = el;\n    if (newStr.includes('inset')) {\n      shadowItem.type = 'inset';\n      newStr = newStr.replace('inset', '').trim();\n    }\n\n    const color = getColorFromStr(newStr);\n    if (color.length) {\n      shadowItem.color = color[0];\n      newStr = newStr.replace(shadowItem.color, '').trim();\n    }\n\n    const newStrArr = newStr.split(' ');\n    const sizeArr = [];\n    for (let i = 0; i < newStrArr.length; i++) {\n      if (!isNaN(parseInt(newStrArr[i]))) {\n        sizeArr.push(newStrArr[i]);\n      } else {\n        break;\n      }\n    }\n    shadowItem.offsetX = sizeArr[0];\n    shadowItem.offsetY = sizeArr[1];\n    shadowItem.blur = sizeArr[2] || '';\n    shadowItem.spread = sizeArr[3] || '';\n    // 获取单词颜色\n    if (!shadowItem.color) {\n      shadowItem.color = newStrArr[sizeArr.length];\n    }\n    return shadowItem;\n  });\n\n  return res;\n};\n"
  },
  {
    "path": "packages/engine/src/utils/defaultEngineConfig.tsx",
    "content": "import { getUniqueAssetsList } from '@/core/assetPackagesListManage';\nimport { LayoutPropsType } from '@chamn/layout';\nimport { collectVariable, flatObject, getThirdLibs, RenderPropsType } from '@chamn/render';\n\n/** 默认使用 react 18 模式渲染 */\nexport const beforeInitRender: LayoutPropsType['beforeInitRender'] = async ({ iframe }) => {\n  const subWin = iframe.getWindow();\n  if (!subWin) {\n    return;\n  }\n  (subWin as any).React = window.React;\n  (subWin as any).ReactDOM = window.ReactDOM;\n  (subWin as any).ReactDOMClient = (window as any).ReactDOMClient;\n};\n\n/** 默认使用 react 18 模式渲染 */\nexport const getDefaultRender = (options: {\n  components: Record<string, any>;\n  renderProps: Partial<RenderPropsType>;\n}) => {\n  const defaultRender: LayoutPropsType['customRender'] = async ({\n    iframe: iframeContainer,\n    assets,\n    page,\n    pageModel,\n    ready,\n    renderJSUrl,\n  }) => {\n    await iframeContainer.injectJS(renderJSUrl || '');\n    const iframeWindow = iframeContainer.getWindow()!;\n    const iframeDoc = iframeContainer.getDocument()!;\n    const IframeReact = iframeWindow.React!;\n    const IframeReactDOM = iframeWindow.ReactDOMClient!;\n    const CRender = iframeWindow.CRender!;\n\n    // 从子窗口获取物料对象\n    const pageInfo = page || pageModel?.export();\n    if (!pageInfo) {\n      console.log('page schema is empty');\n      return;\n    }\n    const allAssets = getUniqueAssetsList([...assets, ...(pageInfo.assets || [])]);\n    // 注入组件物料资源\n    const assetLoader = new CRender.AssetLoader(allAssets, {\n      window: iframeContainer.getWindow()!,\n    });\n    assetLoader\n      .onSuccess(() => {\n        const allLibs = collectVariable(allAssets, iframeWindow);\n        const componentsLibs = flatObject(allLibs);\n        const thirdLibs = getThirdLibs(allLibs, pageInfo.thirdLibs || []);\n        const App = IframeReact?.createElement(CRender.DesignRender, {\n          adapter: CRender?.ReactAdapter,\n          page: page,\n          pageModel: pageModel,\n          components: { ...componentsLibs, ...(options.components || {}) },\n          $$context: {\n            thirdLibs,\n          },\n          onMount: (designRenderInstance) => {\n            ready(designRenderInstance);\n          },\n          ...((options?.renderProps as any) || {}),\n        });\n        IframeReactDOM.createRoot(iframeDoc.getElementById('app')!).render(App);\n      })\n      .onError(() => {\n        console.log('资源加载出错');\n      })\n      .load();\n  };\n\n  return defaultRender;\n};\n"
  },
  {
    "path": "packages/engine/src/utils/index.ts",
    "content": "/**\n *\n * @param time 毫秒\n * @returns\n */\nexport const waitReactUpdate = ({ time = 1000 / 60, cb }: { time?: number; cb?: (...args: any[]) => void } = {}) => {\n  return new Promise((resolve) => {\n    setTimeout(() => {\n      resolve('ok');\n      cb?.();\n    }, time);\n  });\n};\n/** 确保 key 一定存在 */\nexport const ensureKeyExist = (obj: any, key: string, defaultValue: any) => {\n  if (obj[key] !== undefined) {\n    return;\n  }\n  obj[key] = defaultValue;\n};\n\nexport const sageJSONParse = function (jsonStr: string, errorValue: any) {\n  try {\n    const res = JSON.parse(jsonStr);\n    return res;\n  } catch (_e) {\n    return errorValue ?? null;\n  }\n};\n\nexport * from './css';\nexport * from './defaultEngineConfig';\nexport * from './logger';\n"
  },
  {
    "path": "packages/engine/src/utils/logger.ts",
    "content": "import { consola } from 'consola';\nexport const LOGGER = consola.create({\n  level: 1,\n});\n"
  },
  {
    "path": "packages/engine/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ESNext\",\n    \"useDefineForClassFields\": true,\n    \"lib\": [\n      \"DOM\",\n      \"DOM.Iterable\",\n      \"ESNext\"\n    ],\n    \"allowJs\": false,\n    \"skipLibCheck\": true,\n    \"esModuleInterop\": false,\n    \"allowSyntheticDefaultImports\": true,\n    \"strict\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": false,\n    \"noEmit\": true,\n    \"jsx\": \"react-jsx\",\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"@/*\": [\n        \"src/*\"\n      ],\n      \"mitt\": [\n        \"./node_modules/mitt\"\n      ]\n    },\n  },\n  \"include\": [\n    \"src\"\n  ],\n}"
  },
  {
    "path": "packages/engine-website-app/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n\nstats.html"
  },
  {
    "path": "packages/engine-website-app/.npmignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\nexample\n\nnode_modules\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n\nstats.html"
  },
  {
    "path": "packages/engine-website-app/CHANGELOG.md",
    "content": "# Change Log\n\nAll notable changes to this project will be documented in this file.\nSee [Conventional Commits](https://conventionalcommits.org) for commit guidelines.\n\n## [0.10.4](https://github.com/ByteCrazy/chameleon/compare/v0.10.3...v0.10.4) (2026-01-17)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, engine-website-app:** 🐛 fixed pkg deps ([22b0000](https://github.com/ByteCrazy/chameleon/commit/22b0000a921ed3bd63cdf3366687184d51ee9dc5))\n\n## [0.10.3](https://github.com/ByteCrazy/chameleon/compare/v0.10.2...v0.10.3) (2026-01-17)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.10.2](https://github.com/ByteCrazy/chameleon/compare/v0.10.1...v0.10.2) (2026-01-17)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.10.1](https://github.com/ByteCrazy/chameleon/compare/v0.10.0...v0.10.1) (2026-01-11)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.10.0](https://github.com/hlerenow/chameleon/compare/v0.9.3...v0.10.0) (2025-12-25)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* 优化构建配置，修复 ES 模块外部化问题 ([866f0ae](https://github.com/hlerenow/chameleon/commit/866f0ae39472625137d73d143625d88d873f6c00))\n\n### 📝 Documentation | 文档\n\n* **build-script:** 添加完整的 README 使用文档 ([fcd6172](https://github.com/hlerenow/chameleon/commit/fcd6172e381364f010986864593264b160380342))\n\n## [0.9.3](https://github.com/ByteCrazy/chameleon/compare/v0.9.2...v0.9.3) (2025-07-20)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.9.2](https://github.com/ByteCrazy/chameleon/compare/v0.9.1...v0.9.2) (2025-07-13)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.9.1](https://github.com/ByteCrazy/chameleon/compare/v0.9.0...v0.9.1) (2025-07-13)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.9.0](https://github.com/ByteCrazy/chameleon/compare/v0.8.6...v0.9.0) (2025-07-13)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.8.6](https://github.com/ByteCrazy/chameleon/compare/v0.8.5...v0.8.6) (2025-06-21)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.8.5](https://github.com/ByteCrazy/chameleon/compare/v0.8.4...v0.8.5) (2025-04-13)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.8.4](https://github.com/ByteCrazy/chameleon/compare/v0.8.3...v0.8.4) (2025-04-13)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.8.3](https://github.com/ByteCrazy/chameleon/compare/v0.8.2...v0.8.3) (2025-04-12)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.8.2](https://github.com/ByteCrazy/chameleon/compare/v0.8.1...v0.8.2) (2025-04-11)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.8.1](https://github.com/ByteCrazy/chameleon/compare/v0.8.0...v0.8.1) (2025-04-10)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.8.0](https://github.com/ByteCrazy/chameleon/compare/v0.7.0...v0.8.0) (2025-04-10)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.7.0](https://github.com/ByteCrazy/chameleon/compare/v0.6.0...v0.7.0) (2025-04-06)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.6.0](https://github.com/ByteCrazy/chameleon/compare/v0.5.2...v0.6.0) (2025-03-30)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.5.2](https://github.com/ByteCrazy/chameleon/compare/v0.5.1...v0.5.2) (2025-03-30)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.5.1](https://github.com/ByteCrazy/chameleon/compare/v0.5.0...v0.5.1) (2025-03-30)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.5.0](https://github.com/ByteCrazy/chameleon/compare/v0.4.0...v0.5.0) (2025-03-30)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.4.0](https://github.com/ByteCrazy/chameleon/compare/v0.3.21...v0.4.0) (2025-03-29)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.3.21](https://github.com/ByteCrazy/chameleon/compare/v0.3.20...v0.3.21) (2025-03-26)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.3.20](https://github.com/ByteCrazy/chameleon/compare/v0.3.19...v0.3.20) (2025-03-26)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.3.19](https://github.com/ByteCrazy/chameleon/compare/v0.3.18...v0.3.19) (2025-03-26)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.3.18](https://github.com/ByteCrazy/chameleon/compare/v0.3.17...v0.3.18) (2025-03-26)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.3.17](https://github.com/ByteCrazy/chameleon/compare/v0.3.16...v0.3.17) (2025-03-25)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.3.16](https://github.com/ByteCrazy/chameleon/compare/v0.3.15...v0.3.16) (2025-03-24)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.3.15](https://github.com/ByteCrazy/chameleon/compare/v0.3.14...v0.3.15) (2025-03-23)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.3.14](https://github.com/ByteCrazy/chameleon/compare/v0.3.13...v0.3.14) (2025-03-23)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.3.13](https://github.com/ByteCrazy/chameleon/compare/v0.3.12...v0.3.13) (2025-03-23)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.3.12](https://github.com/ByteCrazy/chameleon/compare/v0.3.11...v0.3.12) (2025-03-23)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.3.11](https://github.com/ByteCrazy/chameleon/compare/v0.3.10...v0.3.11) (2025-03-22)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.3.10](https://github.com/ByteCrazy/chameleon/compare/v0.3.9...v0.3.10) (2025-03-22)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.3.9](https://github.com/ByteCrazy/chameleon/compare/v0.3.8...v0.3.9) (2025-03-22)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.3.8](https://github.com/ByteCrazy/chameleon/compare/v0.3.7...v0.3.8) (2025-03-16)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.3.7](https://github.com/ByteCrazy/chameleon/compare/v0.3.6...v0.3.7) (2025-03-16)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.3.6](https://github.com/ByteCrazy/chameleon/compare/v0.3.5...v0.3.6) (2025-03-09)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.3.5](https://github.com/ByteCrazy/chameleon/compare/v0.3.4...v0.3.5) (2025-03-09)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.3.4](https://github.com/ByteCrazy/chameleon/compare/v0.3.3...v0.3.4) (2025-02-16)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.3.3](https://github.com/ByteCrazy/chameleon/compare/v0.3.2...v0.3.3) (2025-02-16)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.3.2](https://github.com/ByteCrazy/chameleon/compare/v0.3.1...v0.3.2) (2025-02-16)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.3.1](https://github.com/ByteCrazy/chameleon/compare/v0.3.0...v0.3.1) (2025-02-16)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.3.0](https://github.com/ByteCrazy/chameleon/compare/v0.2.4...v0.3.0) (2025-02-15)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, engine-website-app, material, model, render:** 🐛 fixed GRL hidden offsetY loop add size ([3df132b](https://github.com/ByteCrazy/chameleon/commit/3df132b6493026b42435d4868d77915b9f7316b2))\n\n### ✨ Features | 新功能\n\n* **docs-app, engine, engine-website-app, layout, material, model, render:** 🎸 do ActionFlowSetter 50% ([6399466](https://github.com/ByteCrazy/chameleon/commit/6399466c7253436591e071df25828a357bb7089e))\n* **engine, engine-website-app, render:** 🎸 RequestAPINode support custom select ([5b72385](https://github.com/ByteCrazy/chameleon/commit/5b72385673bb646aff3ad2c5a9d8fffa142733dd))\n\n## [0.2.4](https://github.com/ByteCrazy/chameleon/compare/v0.2.3...v0.2.4) (2024-12-08)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.2.3](https://github.com/ByteCrazy/chameleon/compare/v0.2.2...v0.2.3) (2024-12-08)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.2.2](https://github.com/ByteCrazy/chameleon/compare/v0.2.1...v0.2.2) (2024-12-07)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.2.1](https://github.com/ByteCrazy/chameleon/compare/v0.2.0...v0.2.1) (2024-12-07)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, engine-website-app, material, model, render:** 🎸 fixed RGL component and optimize project struct ([99f0679](https://github.com/ByteCrazy/chameleon/commit/99f0679d93a2fd696034ebed8f1abcd9d9e601d4))\n\n## [0.2.0](https://github.com/ByteCrazy/chameleon/compare/v0.1.1...v0.2.0) (2024-12-07)\n\n### ✨ Features | 新功能\n\n* **build-script, demo-page, engine, engine-website-app, layout, material, model, render:** 🎸 upgrade vite to 6.0 ([bcac2b1](https://github.com/ByteCrazy/chameleon/commit/bcac2b15b83b41a7042ca37368c1b45302ad81d5))\n\n## [0.1.1](https://github.com/ByteCrazy/chameleon/compare/v0.1.0...v0.1.1) (2024-11-11)\n\n**Note:** Version bump only for package @chamn/engine-website-app\n\n## [0.1.0](https://github.com/ByteCrazy/chameleon/compare/v0.0.46...v0.1.0) (2024-09-07)\n\n### 👷 Continuous Integration | CI 配置\n\n* **engine, engine-website-app, layout, model:** 🎡 update github ci ([259e9db](https://github.com/ByteCrazy/chameleon/commit/259e9db229576cd09c5aae1f28a2c228927011c7))\n\n### ✨ Features | 新功能\n\n* **demo-page, engine, engine-website-app, material:** 🎸 add designerSizer and fixed GridItem bug ([4665aed](https://github.com/ByteCrazy/chameleon/commit/4665aed300d54c77be4abcb9a8cc0f1710ac2145))\n* **engine-website-app, material:** 🎸 optimize RGL ([dd04999](https://github.com/ByteCrazy/chameleon/commit/dd04999a1509e29d6d738ea3a4250a146bfc295e))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **demo-page, docs-app, engine, engine-website-app, layout, material:** 🐛 fixed material meta not correct ([55b755c](https://github.com/ByteCrazy/chameleon/commit/55b755ce0c833e594b46447b2d6608cf56f7a593))\n* **engine-website-app:** 🐛 fixed demo-app assets path ([4cc071e](https://github.com/ByteCrazy/chameleon/commit/4cc071ecfe77e93b46645b5e4a7d806acf78d896))\n* **engine, engine-website-app, material:** 🐛 fixed ReactGridLayout edit mode lable not correct ([9801839](https://github.com/ByteCrazy/chameleon/commit/9801839d9ff6a6548b0e23f1e31850cd56e7fff0))\n\n### 📝 Documentation | 文档\n\n* **demo-page, engine-website-app:** ✏️ demo app add switch page ([c098997](https://github.com/ByteCrazy/chameleon/commit/c098997e2c53bbe135ce263fcd1912ddc53146fe))\n* **docs-app, engine-website-app:** ✏️ fixed doc url path ([181aa3f](https://github.com/ByteCrazy/chameleon/commit/181aa3f0fe1f329d8c2e8bd83759781468edcf5d))\n\n## [0.0.46](https://github.com/ByteCrazy/chameleon/compare/v0.0.45...v0.0.46) (2024-06-30)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed registerCustomSetter method not valid ([9bdaa3b](https://github.com/ByteCrazy/chameleon/commit/9bdaa3b9feb1610a754b9aaa0925530f474dd3c3))\n\n## [0.0.45](https://github.com/ByteCrazy/chameleon/compare/v0.0.44...v0.0.45) (2024-06-30)\n\n**Note:** Version bump only for package @chamn/engine\n\n## [0.0.44](https://github.com/ByteCrazy/chameleon/compare/v0.0.43...v0.0.44) (2024-06-30)\n\n### ✨ Features | 新功能\n\n* **engine, model, render:** 🎸 optimize addMaterials and fixed inner mat schema ([aa77803](https://github.com/ByteCrazy/chameleon/commit/aa77803c75b203ee2a098fbee143f4a9581e15fa))\n\n## [0.0.43](https://github.com/ByteCrazy/chameleon/compare/v0.0.42...v0.0.43) (2024-06-29)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, layout:** 🎸 remove scss dts generate ([9ba7af3](https://github.com/ByteCrazy/chameleon/commit/9ba7af30601804f94e90a0408745fd48de38d8b5))\n\n## [0.0.42](https://github.com/ByteCrazy/chameleon/compare/v0.0.41...v0.0.42) (2024-06-01)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, layout:** 🐛 fixed drag and drop problem on chrome ([5c655c3](https://github.com/ByteCrazy/chameleon/commit/5c655c3a2f288bd90b70212f0f114adf3c23f8a1))\n\n## [0.0.41](https://github.com/ByteCrazy/chameleon/compare/v0.0.40...v0.0.41) (2024-05-26)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, layout, material, model, render:** 🎸 optimize build script ([0a60cc4](https://github.com/ByteCrazy/chameleon/commit/0a60cc4f5e5635d9e544b5141eaed5b8433901a5))\n\n## [0.0.40](https://github.com/ByteCrazy/chameleon/compare/v0.0.39...v0.0.40) (2024-04-28)\n\n**Note:** Version bump only for package @chamn/engine\n\n## [0.0.39](https://github.com/ByteCrazy/chameleon/compare/v0.0.38...v0.0.39) (2024-04-27)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **build-script, demo-page, engine, layout, material, model, render:** 🐛 fix flatObject collectVariable refeer pos ([1c4163a](https://github.com/ByteCrazy/chameleon/commit/1c4163aeeb89176a1ff5b50f76930889df7751fe))\n\n## [0.0.38](https://github.com/ByteCrazy/chameleon/compare/v0.0.37...v0.0.38) (2024-04-27)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, layout:** 🐛 fixed assets load iuess when relaod ([3eccc60](https://github.com/ByteCrazy/chameleon/commit/3eccc60bb7be5a469704a9c4f769161e525481f4))\n\n## [0.0.37](https://github.com/ByteCrazy/chameleon/compare/v0.0.36...v0.0.37) (2024-04-27)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed updateMaterials components map problem ([b868e88](https://github.com/ByteCrazy/chameleon/commit/b868e884a9b65021effdee8872a462fd8539c9c4))\n\n## [0.0.36](https://github.com/ByteCrazy/chameleon/compare/v0.0.35...v0.0.36) (2024-04-27)\n\n**Note:** Version bump only for package @chamn/engine\n\n## [0.0.35](https://github.com/ByteCrazy/chameleon/compare/v0.0.34...v0.0.35) (2024-04-27)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed onPluginReadyOk return value ([8550591](https://github.com/ByteCrazy/chameleon/commit/85505913af29459882caef5b83d17c2a8aca45b3))\n\n## [0.0.34](https://github.com/ByteCrazy/chameleon/compare/v0.0.33...v0.0.34) (2024-04-27)\n\n### ✨ Features | 新功能\n\n* **docs-website, engine, model:** 🎸 add update assetsPackageList logic ([2bce2f4](https://github.com/ByteCrazy/chameleon/commit/2bce2f4758b203695ec119d6e201e3186d7fab84))\n\n## [0.0.33](https://github.com/ByteCrazy/chameleon/compare/v0.0.32...v0.0.33) (2024-04-27)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fix css editor initial value not correct problem ([96c3e58](https://github.com/ByteCrazy/chameleon/commit/96c3e58755ed4fdfa3e200f3049b5024cb9c6719))\n\n### ✨ Features | 新功能\n\n* **engine, layout, render:** 🎸 optimize history step save ([dca964a](https://github.com/ByteCrazy/chameleon/commit/dca964a990a3cc0c5fd853d853a55a4957999644))\n* **engine, render:** 🎸 designer plugin add  updateComponent method ([f161177](https://github.com/ByteCrazy/chameleon/commit/f16117793402681a1eda8f8cba9c06e2ee5247ad))\n\n## [0.0.32](https://github.com/ByteCrazy/chameleon/compare/v0.0.31...v0.0.32) (2024-01-30)\n\n### ✨ Features | 新功能\n\n* **docs-website, engine, model, render:** 🎸 add updateMaterials、updatePage methods ([39ed2b6](https://github.com/ByteCrazy/chameleon/commit/39ed2b692a8a7379a79b96cb3fd8cdb76a4f01f2))\n\n## [0.0.31](https://github.com/ByteCrazy/chameleon/compare/v0.0.30...v0.0.31) (2023-10-17)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed arraySetter delete bug ([1b53538](https://github.com/ByteCrazy/chameleon/commit/1b53538a67ff35a6cfedffe661126f6912e78bbf))\n\n## [0.0.30](https://github.com/ByteCrazy/chameleon/compare/v0.0.29...v0.0.30) (2023-10-17)\n\n**Note:** Version bump only for package @chamn/engine\n\n## [0.0.29](https://github.com/ByteCrazy/chameleon/compare/v0.0.28...v0.0.29) (2023-10-17)\n\n### ✨ Features | 新功能\n\n* **build-script, engine:** 🎸 optimize css editor ([66c0541](https://github.com/ByteCrazy/chameleon/commit/66c0541eaee12b2eb0ceb4fb3a7748e1bf69768d))\n* **demo-page, engine, model:** 🎸 add ant color setter ([830de46](https://github.com/ByteCrazy/chameleon/commit/830de46babdf40a740248c37d8e6dc2043db1434))\n* **demo-page, engine, model:** 🎸 add ColorSetter ([e72b7f1](https://github.com/ByteCrazy/chameleon/commit/e72b7f15dd8bba498b776226d5debd07c8fd4234))\n* **demo-page, engine, model:** 🎸 add radio setter ([e6f9b1d](https://github.com/ByteCrazy/chameleon/commit/e6f9b1d9dba7cd3a9a82116bf5a1068c06a5a1d6))\n* **demo-page, engine:** 🎸 add cssSize setter ([74bf136](https://github.com/ByteCrazy/chameleon/commit/74bf136047000a67795e1360f2a923902366a513))\n* **demo-page, engine:** 🎸 add slider setter ([0fe1d29](https://github.com/ByteCrazy/chameleon/commit/0fe1d29257f214e69ca9fed6e5e4c11ccccb56bb))\n* **engine, model, render:** 🎸 cssEditor support cssText ([3dc74b4](https://github.com/ByteCrazy/chameleon/commit/3dc74b4895d414a718c00911a39d8491fafcfaee))\n* **engine, model:** 🎸 optimize style editor ([9c660ce](https://github.com/ByteCrazy/chameleon/commit/9c660ce694a32059450f19c824bcde9889049db8))\n* **engine, model:** 🎸 style varible、c s scss editor support styles text ([c988fcf](https://github.com/ByteCrazy/chameleon/commit/c988fcf17a2dbbc486e809a03c642726f02cf547))\n* **engine:** 🎸 add border input ([ae7d3c3](https://github.com/ByteCrazy/chameleon/commit/ae7d3c3e1c07bf68cfa74ef15ab8f499c9b7afc3))\n* **engine:** 🎸 add CSSSizeInput componrnt ([cd70933](https://github.com/ByteCrazy/chameleon/commit/cd70933fbb044058b65c402465a69139da4d8a4a))\n* **engine:** 🎸 add DimensionInput、FontInput、MarginInput、PaddingInput ([08aa4e5](https://github.com/ByteCrazy/chameleon/commit/08aa4e5a959ace882312b77a15ab5c973d2015b8))\n* **engine:** 🎸 CSSUI editor sync value ([549fa7e](https://github.com/ByteCrazy/chameleon/commit/549fa7ea676d9f31d10e3b78888ec2318df2cdc2))\n* **engine:** 🎸 optimize style setter ([eba569c](https://github.com/ByteCrazy/chameleon/commit/eba569cb5871f6cf8cce459c69d3d3beb7e0459a))\n* **engine:** 🎸 style UI finish ([a38fb84](https://github.com/ByteCrazy/chameleon/commit/a38fb846475631caa1a6253af7de11fd30027c48))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **build-script, engine:** 🐛 recocer build-script bin file ([5a2ca69](https://github.com/ByteCrazy/chameleon/commit/5a2ca69c4e5c48b3b0686478f6e5c40cd21c08ad))\n* **engine, render:** 🐛 fixed build error and add child cache for render ([9026474](https://github.com/ByteCrazy/chameleon/commit/90264746fe99b8cdd4d055f2d778e67aae38af87))\n* **engine:** 🐛 fixed MonacoEditor not trigger value change event ([0f6c3ee](https://github.com/ByteCrazy/chameleon/commit/0f6c3ee82a31b1a23388486f4cb12f9d7424ae66))\n\n## [0.0.28](https://github.com/ByteCrazy/chameleon/compare/v0.0.27...v0.0.28) (2023-08-29)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed click can't add component into canvas ([b8e8c2c](https://github.com/ByteCrazy/chameleon/commit/b8e8c2c703f20c2bc2836dc2e1f91b02b994e5ca))\n\n## [0.0.27](https://github.com/ByteCrazy/chameleon/compare/v0.0.26...v0.0.27) (2023-08-28)\n\n### ✨ Features | 新功能\n\n* **engine:** 🎸 plugin system support add addCustomView method ([ce6db8f](https://github.com/ByteCrazy/chameleon/commit/ce6db8fcf1b87e6e3e14ed7464e7615c0ecc852b))\n\n## [0.0.26](https://github.com/ByteCrazy/chameleon/compare/v0.0.25...v0.0.26) (2023-08-27)\n\n**Note:** Version bump only for package @chamn/engine\n\n## [0.0.25](https://github.com/ByteCrazy/chameleon/compare/v0.0.24...v0.0.25) (2023-08-27)\n\n**Note:** Version bump only for package @chamn/engine\n\n## [0.0.24](https://github.com/ByteCrazy/chameleon/compare/v0.0.23...v0.0.24) (2023-08-27)\n\n### ✨ Features | 新功能\n\n* **engine, model, render:** 🎸 fixed node maybe is null on rightPanel ([aee551e](https://github.com/ByteCrazy/chameleon/commit/aee551e77454f61900318003f9e3a3ffb1ef9427))\n\n## [0.0.23](https://github.com/ByteCrazy/chameleon/compare/v0.0.22...v0.0.23) (2023-08-26)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, render:** 🐛 fixed CSSPropertiesVariableBindEditor value not update ([9084c32](https://github.com/ByteCrazy/chameleon/commit/9084c32bb919c2e6191c29b871f9d1537d139050))\n\n### ✨ Features | 新功能\n\n* **engine, model:** 🎸 support custom rightPanel ([803d731](https://github.com/ByteCrazy/chameleon/commit/803d731819faa03430b2a17a154d3ff1e0daca28))\n* **engine, model:** 🎸 support inject custom setter ([a49ec4a](https://github.com/ByteCrazy/chameleon/commit/a49ec4ae4a98b42cf1cfb768990b64c981539881))\n\n## [0.0.22](https://github.com/ByteCrazy/chameleon/compare/v0.0.21...v0.0.22) (2023-08-24)\n\n### ✨ Features | 新功能\n\n* **demo-page, engine, layout, model:** 🎸 add disableEditorDragDom config ([1779f44](https://github.com/ByteCrazy/chameleon/commit/1779f44aff20370ea3336e72352032c6416f7dd3))\n\n## [0.0.21](https://github.com/ByteCrazy/chameleon/compare/v0.0.20...v0.0.21) (2023-08-19)\n\n### ✨ Features | 新功能\n\n* **engine, layout, model, render:** 🎸 optimize wrapComponent config ([d5916a7](https://github.com/ByteCrazy/chameleon/commit/d5916a7e6ee3cf79a32d4d23663b6873d86fe671))\n\n## [0.0.20](https://github.com/ByteCrazy/chameleon/compare/v0.0.19...v0.0.20) (2023-08-19)\n\n**Note:** Version bump only for package @chamn/engine\n\n## [0.0.19](https://github.com/ByteCrazy/chameleon/compare/v0.0.18...v0.0.19) (2023-08-17)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, render:** 🐛 fixed if assets is empty, loader will not success ([169d9ad](https://github.com/ByteCrazy/chameleon/commit/169d9ad9e5791353ad3a68dd0212c6ecfda64a29))\n\n### ✨ Features | 新功能\n\n* **engine:** 🎸 add HistoryPlugin type definition ([13a66fb](https://github.com/ByteCrazy/chameleon/commit/13a66fb5141b308af191394b6c377a0d387ea628))\n* **engine:** 🎸 use @monaco-editor/react replace monaco-editor ([848ee87](https://github.com/ByteCrazy/chameleon/commit/848ee87dedbccc71d3a7366320ad03122ed15d38))\n\n## [0.0.18](https://github.com/ByteCrazy/chameleon/compare/v0.0.17...v0.0.18) (2023-08-14)\n\n### ✨ Features | 新功能\n\n* **engine:** 🎸 add className and style for engine ([c2b7ef3](https://github.com/ByteCrazy/chameleon/commit/c2b7ef3c1c68708963dece239528889701eb0fd7))\n* **engine:** 🎸 add onMount for engine ([17c80ae](https://github.com/ByteCrazy/chameleon/commit/17c80aecc09f13da5f33d7a59b5849d73d99d12a))\n\n## [0.0.17](https://github.com/ByteCrazy/chameleon/compare/v0.0.16...v0.0.17) (2023-08-06)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed types error ([1b68777](https://github.com/ByteCrazy/chameleon/commit/1b68777eb58225d750f40b16a7d263acb385477a))\n\n## [0.0.16](https://github.com/ByteCrazy/chameleon/compare/v0.0.15...v0.0.16) (2023-08-06)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, layout:** 🐛 fixed some node not exits querySelector method ([2ddc186](https://github.com/ByteCrazy/chameleon/commit/2ddc1863e6eb207ae2e2b705a9fef4d23bece37c))\n\n### ✨ Features | 新功能\n\n* **build-script-example, demo-page, engine, layout, material, model, render:** 🎸 support direct import render from package on dev mode ([6bb38e2](https://github.com/ByteCrazy/chameleon/commit/6bb38e29678a8a4d08a7fcf9548fa548399259b0))\n* **demo-page, engine, layout, model:** 🎸 support  advanceCustom drag and drop hooks ([fb21e15](https://github.com/ByteCrazy/chameleon/commit/fb21e1501f6829e4f13a35ea7be942ff72b0be91))\n* **demo-page, engine, layout, model:** 🎸 support toolbarViewRender and ghostViewRedner ([43ced37](https://github.com/ByteCrazy/chameleon/commit/43ced371cfb7dbb58a96fc15e1bf635092307fa8))\n* **demo-page, engine, layout:** 🎸 support DropViewRender ([1c025a6](https://github.com/ByteCrazy/chameleon/commit/1c025a6d1f57f450b2de262a787375f3f4891008))\n* **demo-page, engine, model:** 🎸 support onDelete onCopy ([b6a76bb](https://github.com/ByteCrazy/chameleon/commit/b6a76bb244bbe2c050de17157bda97cb7ad21abc))\n* **engine, layout, model:** 🎸 add customDropViewRender prop ([cea7f1e](https://github.com/ByteCrazy/chameleon/commit/cea7f1e3c2b0a8a13aef7cdcb0191593414615aa))\n* **engine, layout, model:** 🎸 finish layout transform ([73ead35](https://github.com/ByteCrazy/chameleon/commit/73ead357b9aded2b5ee2b545da6f2b3677ce8393))\n* **engine:** 🎸 optimze plugin type definition ([da9c107](https://github.com/ByteCrazy/chameleon/commit/da9c107b20646714834642ad09b2cbdfcb6eb7cd))\n\n## [0.0.15](https://github.com/ByteCrazy/chameleon/compare/v0.0.14...v0.0.15) (2023-07-01)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, layout:** 🐛 fixed component will be add repeatedly after reload page ([1507549](https://github.com/ByteCrazy/chameleon/commit/1507549632c3c6e09d8555fd0286fcc72855c358))\n\n### ✨ Features | 新功能\n\n* **demo-page, docs-website, engine, model, render:** 🎸 optimize assets load ([3ee6d58](https://github.com/ByteCrazy/chameleon/commit/3ee6d58a88e5af3fc631723240783d5c97a273b0))\n* **demo-page, engine:** 🎸 setter support initialValue ([794d807](https://github.com/ByteCrazy/chameleon/commit/794d8072518cb3b1a897b04a2e96f5ca53fdb365))\n* **docs-website, engine, layout, material, render:** 🎸 support inject thridLib on $context ([ca2f074](https://github.com/ByteCrazy/chameleon/commit/ca2f07492b713c32e5a41d1f250f7888763cb665))\n* **engine, render:** 🎸 change internal api ([0c0c7c3](https://github.com/ByteCrazy/chameleon/commit/0c0c7c3fa8f86c08ab0cefb0396107ed8c52dcd5))\n\n## [0.0.14](https://github.com/ByteCrazy/chameleon/compare/v0.0.13...v0.0.14) (2023-05-10)\n\n### ✨ Features | 新功能\n\n* **engine:** 🎸 add node id in advance panel ([d457203](https://github.com/ByteCrazy/chameleon/commit/d45720316ab95f75f6f67a5dbf31bb0030ceb3f1))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, layout:** 🐛 fixed new component can not be add into canvas ([4057fa9](https://github.com/ByteCrazy/chameleon/commit/4057fa925c18bf06325de04b270be5f6c23351c8))\n* **engine, layout:** 🐛 fixed type problem ([751a392](https://github.com/ByteCrazy/chameleon/commit/751a39244228f74138acf7f567d23e888b8ff687))\n* **engine, render:** 🐛 fixed removeMediaCSS method run failed ([9127ec3](https://github.com/ByteCrazy/chameleon/commit/9127ec3d13f43a1c8763b2350bc0224d683da85c))\n\n## [0.0.13](https://github.com/ByteCrazy/chameleon/compare/v0.0.12...v0.0.13) (2023-05-03)\n\n### ✨ Features | 新功能\n\n* add-nearby-component ([85a301c](https://github.com/ByteCrazy/chameleon/commit/85a301c3d95e785c116ab38c0b3a452ceb33742f))\n* **build-script, build-script-example, demo-page, engine, layout, material, model, render:** 🎸 config code style config ([2325504](https://github.com/ByteCrazy/chameleon/commit/23255048fa4a3d4fc8f5cfa1312db5abd6cac70d))\n* **build-script, demo-page, engine, layout, model, render:** 🎸 update meterial schema ([14f4e5c](https://github.com/ByteCrazy/chameleon/commit/14f4e5caf0a4f6eca131e139d0c34ea21969dc0b))\n* **demo-page, docs-website, engine, layout, model, render:** 🎸 node support methods config ([e080b20](https://github.com/ByteCrazy/chameleon/commit/e080b20cea3cea27b95def196e078c1c225d3a83))\n* **demo-page, engine, layout, model:** 🎸 finish customEvent config ([84640e3](https://github.com/ByteCrazy/chameleon/commit/84640e3d06b79858590b9fe92ef4764fbe3f4f7b))\n* **demo-page, engine, layout:** 🎸 outline support cancel drag by node material ([cd428d3](https://github.com/ByteCrazy/chameleon/commit/cd428d362a8bfe7d5b6e58ed2b6290e19c00672b))\n* **demo-page, engine, layout:** 🎸 support cancel drag and custom node drag event ([825717e](https://github.com/ByteCrazy/chameleon/commit/825717e063846b72f95b0e41d8cba279ef5270c8))\n* **demo-page, engine:** 🎸 complete advanceCustom material feature ([98cf273](https://github.com/ByteCrazy/chameleon/commit/98cf273543f2fd534c254990d61052534a6649da))\n* **demo-page, engine:** 🎸 supprot onSelectNode ([f496a82](https://github.com/ByteCrazy/chameleon/commit/f496a82920cab7ae8704335b2ecd02a7887de3b2))\n* **engine, layout, model:** 🎸 support supportDispatchNativeEvent field ([489fd05](https://github.com/ByteCrazy/chameleon/commit/489fd05b588e85ed4cea81cc33ab27739a1bac59))\n* **engine, layout:** 🎸 optimize layout event system ([56f35ef](https://github.com/ByteCrazy/chameleon/commit/56f35ef83cc7e1658bfeba9081f997ff457cf09e))\n* **engine, layout:** 🎸 support isSupportDispatchNativeEvent filed ([4f830b4](https://github.com/ByteCrazy/chameleon/commit/4f830b42b6e84d74e9d462c3b3e090de89686e18))\n* **engine, render:** 🎸 html native tag component support container filed ([475fdbd](https://github.com/ByteCrazy/chameleon/commit/475fdbd5a34581ab7cafe67b5c9d3b94102b3a16))\n* remove self-executing function ([e224524](https://github.com/ByteCrazy/chameleon/commit/e224524198aa3f1339afd3bc9f1bacb72bc4d20e))\n* select element after in add node ([0c0d116](https://github.com/ByteCrazy/chameleon/commit/0c0d1164783420da306062292674fbccb0a8ffb0))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* adjust find node logic ([5f9f073](https://github.com/ByteCrazy/chameleon/commit/5f9f07320888b3a83a4a24b04cfbd2e9e82aa4b1))\n* **engine, layout, model:** 🐛 fixed type error ([d11e81f](https://github.com/ByteCrazy/chameleon/commit/d11e81f607ef6a41a2dfda0b4ac657a9a87e948c))\n* **engine:** 🐛 fixed insert node logic ([7fd4866](https://github.com/ByteCrazy/chameleon/commit/7fd4866d7daaece1c8c3b2a846b55da27a1e0936))\n\n## [0.0.12](https://github.com/ByteCrazy/chameleon/compare/v0.0.11...v0.0.12) (2023-04-23)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **build-script, build-script-example, engine, layout, material, model, render:** 🐛 fix can not find vite/client.d.ts problem ([8a53540](https://github.com/ByteCrazy/chameleon/commit/8a53540f75737707f69f93d2f701203dbf88084a))\n\n### ✨ Features | 新功能\n\n* **demo-page, docs-website, engine, model:** 🎸 update schema and change err to warn when schema check ([e753862](https://github.com/ByteCrazy/chameleon/commit/e7538626bad6681e4d488ac835fef61a603c0853))\n* **demo-page, engine, layout, model, render:** 🎸 node support config self isContainer property ([d94f1f9](https://github.com/ByteCrazy/chameleon/commit/d94f1f9203f2c273856fd4aa489a1ed9cbb0f0ec))\n\n## [0.0.11](https://github.com/ByteCrazy/chameleon/compare/v0.0.10...v0.0.11) (2023-04-21)\n\n### ✨ Features | 新功能\n\n*  add reload method of plugin ([3b2cb2a](https://github.com/ByteCrazy/chameleon/commit/3b2cb2aa3804e250ed8ce37027168282f63db879))\n* add replaceSubTopBarView method ([da32d9c](https://github.com/ByteCrazy/chameleon/commit/da32d9c67a6fff1a562886c027c1604a54e19b7e))\n* **build-script, build-script-example, demo-page, engine, layout, material, model, render:** 🎸 optimize build-script output file format ([eda44d2](https://github.com/ByteCrazy/chameleon/commit/eda44d255bd3c5af19013bdd61342e1f0817413a))\n* **build-script, docs-website, engine:** 🎸 add material develop doc ([0aacca0](https://github.com/ByteCrazy/chameleon/commit/0aacca0f726bc13606b814f8890b4e8ff8982142))\n* **engine, model:** 🎸 support custom setter ([5236c54](https://github.com/ByteCrazy/chameleon/commit/5236c543559aac517e833741ffa8046bc1c7f1a3))\n* RightPanel add replacePanel、removePanel methods ([ee6c2e1](https://github.com/ByteCrazy/chameleon/commit/ee6c2e1b45bf93075342cee060fe3038ec7b8e53))\n\n### 📝 Documentation | 文档\n\n* **docs-website, engine:** ✏️ add plugin、setter develop doc ([288edc4](https://github.com/ByteCrazy/chameleon/commit/288edc4999f732ff2f27b1c6e50900b9b0094700))\n\n## [0.0.10](https://github.com/ByteCrazy/chameleon/compare/v0.0.9...v0.0.10) (2023-04-17)\n\n**Note:** Version bump only for package @chamn/engine\n\n## [0.0.9](https://github.com/ByteCrazy/chameleon/compare/v0.0.8...v0.0.9) (2023-04-17)\n\n### ✨ Features | 新功能\n\n* **docs-website, engine, render:** 🎸 render add collectVariable flatObject util method ([ae25160](https://github.com/ByteCrazy/chameleon/commit/ae25160b568c267041b3827e836c95f60ecaee59))\n\n## [0.0.8](https://github.com/ByteCrazy/chameleon/compare/v0.0.7...v0.0.8) (2023-04-16)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, layout, model, render:** 🎸 optimize pack package ([78d99c5](https://github.com/ByteCrazy/chameleon/commit/78d99c507ad65ca39101f0239467737d2b445c87))\n* **docs-website, engine, layout:** 🎸 add defaultRender for engine, add some docs ([91f4257](https://github.com/ByteCrazy/chameleon/commit/91f4257e9f9b9391267e4b8d64e6ed811912381f))\n* **docs-website, engine:** 🎸 add docs ([0e6f605](https://github.com/ByteCrazy/chameleon/commit/0e6f6053fa3cd5e13b6a7a8258c27518c30470c5))\n\n### 📝 Documentation | 文档\n\n* **docs-website, engine:** ✏️ add doc ([2fb5cf5](https://github.com/ByteCrazy/chameleon/commit/2fb5cf5fd4dcb3859ecbdc8d42adf787d9e0255d))\n\n## [0.0.7](https://github.com/ByteCrazy/chameleon/compare/v0.0.6...v0.0.7) (2023-03-29)\n\n### ✨ Features | 新功能\n\n* **build-script, build-script-example, demo-page, engine, layout, material, model, render:** 🎸 external monaco eitor and remove style panel ([494d898](https://github.com/ByteCrazy/chameleon/commit/494d898fd75dabe84d867ff45e84ae63f9b59c5e))\n\n## [0.0.6](https://github.com/ByteCrazy/chameleon/compare/v0.0.5...v0.0.6) (2023-03-28)\n\n**Note:** Version bump only for package @chamn/engine\n\n## [0.0.5](https://github.com/ByteCrazy/chameleon/compare/v0.0.4...v0.0.5) (2023-03-28)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, layout, material, model, render:** 🎸 complete package info ([86b095d](https://github.com/ByteCrazy/chameleon/commit/86b095da6c955946300591b1775f59c1a141c065))\n\n## [0.0.4](https://github.com/ByteCrazy/chameleon/compare/v0.0.3...v0.0.4) (2023-03-27)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fixed engine d.ts path not correct problem ([fb20ca6](https://github.com/ByteCrazy/chameleon/commit/fb20ca604fff4e85b0264eff6383154ecfefd437))\n\n## [0.0.2](https://github.com/ByteCrazy/chameleon/compare/v0.0.1...v0.0.2) (2023-03-27)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine:** 🐛 fix engine not incluce dist folder ([fc2c39e](https://github.com/ByteCrazy/chameleon/commit/fc2c39e88aa2eeb82ba5e3989e5bdf244d112dfb))\n"
  },
  {
    "path": "packages/engine-website-app/LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "packages/engine-website-app/README.md",
    "content": "# chameleon\n\n> Chameleon is ever-changing\n\nA web visual programming engine, help to build a web page with 5 minutes. every people can use it easy.\n\n[Docs](https://hlerenow.github.io/chameleon/documents/) | [Demo](https://hlerenow.github.io/chameleon/)\n\n## Install\n\n```shell\nnpm i @chamn/engine @chamn/model @chamn/render\n```\n\n## Usage\n\n[Example Project](https://github.com/ByteCrazy/chameleon-demo)\n\n### ScreenSnapshoot\n\n![image](https://user-images.githubusercontent.com/13299648/218920616-302a9eb6-a71a-4f4b-8e77-d892972eee2f.png)\n\n![image](https://user-images.githubusercontent.com/13299648/218920783-0d1cc275-a238-4d80-a717-dbbbf54b4713.png)\n\n![image](https://user-images.githubusercontent.com/13299648/218920845-0c4c549d-df56-4b0a-9b72-95dd0c0fcaf5.png)\n\n![image](https://user-images.githubusercontent.com/13299648/218921002-a25cfdd6-f27a-4b19-83fe-a6a264e4e4b5.png)\n\n![image](https://user-images.githubusercontent.com/13299648/218920640-9be3b1ba-1dc2-42c5-922f-f3c5f97a9d96.png)\n"
  },
  {
    "path": "packages/engine-website-app/build.common.config.js",
    "content": "/* eslint-disable @typescript-eslint/no-var-requires */\nimport path from 'path';\n\nexport default {\n  resolve: {\n    alias: [{ find: '@', replacement: path.resolve(__dirname, 'src') }],\n  },\n  css: {\n    preprocessorOptions: {\n      scss: {\n        additionalData: '@import \"@/assets/styles/mixin.scss\";',\n      },\n    },\n  },\n};\n"
  },
  {
    "path": "packages/engine-website-app/build.config.js",
    "content": "/* eslint-disable no-undef */\nimport { viteStaticCopy } from 'vite-plugin-static-copy';\nimport pkg from './package.json';\nimport commonConfig from './build.common.config';\n// 开发模式默认读取 index.html 作为开发模式入口\n// entry 作为打包库入口\nconst plugins = [];\n\nplugins.push(\n  viteStaticCopy({\n    targets: [\n      {\n        src: './node_modules/@chamn/render/dist/index.umd.js',\n        dest: './',\n        rename: 'render.umd.js',\n      },\n    ],\n  })\n);\n\nconst mainConfig = {\n  libMode: false,\n  entry: './src/index.tsx',\n  fileName: 'index',\n  external: [],\n  vite: {\n    base: '/chameleon/',\n    build: {\n      outDir: './dist',\n      copyPublicDir: true,\n    },\n    plugins: plugins,\n    ...commonConfig,\n    define: {\n      global: 'globalThis',\n      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),\n      __RUN_MODE__: JSON.stringify(process.env.BUILD_TYPE),\n      __PACKAGE_VERSION__: JSON.stringify(pkg.version),\n      __BUILD_VERSION__: JSON.stringify(Date.now()),\n    },\n  },\n};\n\nconst config = mainConfig;\nexport default config;\n"
  },
  {
    "path": "packages/engine-website-app/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <link rel=\"icon\" type=\"image/svg+xml\" href=\"/vite.svg\" />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n  <title>Chameleon Low-Code Engine</title>\n</head>\n\n<body>\n  <div id=\"root\"></div>\n  <script type=\"module\" src=\"/src/index.tsx\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "packages/engine-website-app/jest.config.js",
    "content": "/* eslint-disable no-undef */\n/*\n * For a detailed explanation regarding each configuration property, visit:\n * https://jestjs.io/docs/configuration\n */\n\nmodule.exports = {\n  // All imported modules in your tests should be mocked automatically\n  // automock: false,\n\n  // Stop running tests after `n` failures\n  // bail: 0,\n\n  // The directory where Jest should store its cached dependency information\n  // cacheDirectory: \"/private/var/folders/l9/th_r5d_12wxdj16859_mctjw0000gn/T/jest_dx\",\n\n  // Automatically clear mock calls, instances, contexts and results before every test\n  // clearMocks: false,\n\n  // Indicates whether the coverage information should be collected while executing the test\n  // collectCoverage: false,\n\n  // An array of glob patterns indicating a set of files for which coverage information should be collected\n  // collectCoverageFrom: undefined,\n\n  // The directory where Jest should output its coverage files\n  // coverageDirectory: undefined,\n\n  // An array of regexp pattern strings used to skip coverage collection\n  // coveragePathIgnorePatterns: [\n  //   \"/node_modules/\"\n  // ],\n\n  // Indicates which provider should be used to instrument code for coverage\n  // coverageProvider: \"babel\",\n\n  // A list of reporter names that Jest uses when writing coverage reports\n  // coverageReporters: [\n  //   \"json\",\n  //   \"text\",\n  //   \"lcov\",\n  //   \"clover\"\n  // ],\n\n  // An object that configures minimum threshold enforcement for coverage results\n  // coverageThreshold: undefined,\n\n  // A path to a custom dependency extractor\n  // dependencyExtractor: undefined,\n\n  // Make calling deprecated APIs throw helpful error messages\n  // errorOnDeprecated: false,\n\n  // The default configuration for fake timers\n  // fakeTimers: {\n  //   \"enableGlobally\": false\n  // },\n\n  // Force coverage collection from ignored files using an array of glob patterns\n  // forceCoverageMatch: [],\n\n  // A path to a module which exports an async function that is triggered once before all test suites\n  // globalSetup: undefined,\n\n  // A path to a module which exports an async function that is triggered once after all test suites\n  // globalTeardown: undefined,\n\n  // A set of global variables that need to be available in all test environments\n  // globals: {},\n\n  // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.\n  // maxWorkers: \"50%\",\n\n  // An array of directory names to be searched recursively up from the requiring module's location\n  // moduleDirectories: [\n  //   \"node_modules\"\n  // ],\n\n  // An array of file extensions your modules use\n  // moduleFileExtensions: [\n  //   \"js\",\n  //   \"mjs\",\n  //   \"cjs\",\n  //   \"jsx\",\n  //   \"ts\",\n  //   \"tsx\",\n  //   \"json\",\n  //   \"node\"\n  // ],\n\n  // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module\n  // moduleNameMapper: {},\n\n  // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader\n  // modulePathIgnorePatterns: [],\n\n  // Activates notifications for test results\n  // notify: false,\n\n  // An enum that specifies notification mode. Requires { notify: true }\n  // notifyMode: \"failure-change\",\n\n  // A preset that is used as a base for Jest's configuration\n  preset: 'ts-jest',\n\n  // Run tests from one or more projects\n  // projects: undefined,\n\n  // Use this configuration option to add custom reporters to Jest\n  // reporters: undefined,\n\n  // Automatically reset mock state before every test\n  // resetMocks: false,\n\n  // Reset the module registry before running each individual test\n  // resetModules: false,\n\n  // A path to a custom resolver\n  // resolver: undefined,\n\n  // Automatically restore mock state and implementation before every test\n  // restoreMocks: false,\n\n  // The root directory that Jest should scan for tests and modules within\n  // rootDir: undefined,\n\n  // A list of paths to directories that Jest should use to search for files in\n  // roots: [\n  //   \"<rootDir>\"\n  // ],\n\n  // Allows you to use a custom runner instead of Jest's default test runner\n  // runner: \"jest-runner\",\n\n  // The paths to modules that run some code to configure or set up the testing environment before each test\n  // setupFiles: [],\n\n  // A list of paths to modules that run some code to configure or set up the testing framework before each test\n  // setupFilesAfterEnv: [],\n\n  // The number of seconds after which a test is considered as slow and reported as such in the results.\n  // slowTestThreshold: 5,\n\n  // A list of paths to snapshot serializer modules Jest should use for snapshot testing\n  // snapshotSerializers: [],\n\n  // The test environment that will be used for testing\n  testEnvironment: 'jsdom',\n\n  // Options that will be passed to the testEnvironment\n  // testEnvironmentOptions: {},\n\n  // Adds a location field to test results\n  // testLocationInResults: false,\n\n  // The glob patterns Jest uses to detect test files\n  // testMatch: [\n  //   \"**/__tests__/**/*.[jt]s?(x)\",\n  //   \"**/?(*.)+(spec|test).[tj]s?(x)\"\n  // ],\n\n  // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped\n  // testPathIgnorePatterns: [\n  //   \"/node_modules/\"\n  // ],\n\n  // The regexp pattern or array of patterns that Jest uses to detect test files\n  // testRegex: [],\n\n  // This option allows the use of a custom results processor\n  // testResultsProcessor: undefined,\n\n  // This option allows use of a custom test runner\n  // testRunner: \"jest-circus/runner\",\n\n  // A map from regular expressions to paths to transformers\n  // transform: undefined,\n\n  // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation\n  // transformIgnorePatterns: [\n  //   \"/node_modules/\",\n  //   \"\\\\.pnp\\\\.[^\\\\/]+$\"\n  // ],\n\n  // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them\n  // unmockedModulePathPatterns: undefined,\n\n  // Indicates whether each individual test should be reported during the run\n  // verbose: undefined,\n\n  // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode\n  // watchPathIgnorePatterns: [],\n\n  // Whether to use watchman for file crawling\n  // watchman: true,\n};\n"
  },
  {
    "path": "packages/engine-website-app/package.json",
    "content": "{\n  \"name\": \"@chamn/engine-website-app\",\n  \"private\": true,\n  \"version\": \"0.10.4\",\n  \"homepage\": \"https://github.com/hlerenow/chameleon\",\n  \"main\": \"./src/index.tsx\",\n  \"keywords\": [\n    \"engine\",\n    \"lowcode\",\n    \"react\",\n    \"web-editor\",\n    \"visual-editor\",\n    \"vite\"\n  ],\n  \"scripts\": {\n    \"start\": \"build-script\",\n    \"build\": \"export NODE_OPTIONS=--max-old-space-size=32768 && build-script --build\",\n    \"build:analyze\": \"build-script --build --analyze\",\n    \"lint\": \"eslint ./src\",\n    \"prettier\": \"prettier --write ./src\",\n    \"test\": \"jest\",\n    \"storybook\": \"storybook dev -p 6006\",\n    \"build-storybook\": \"storybook build\"\n  },\n  \"peerDependencies\": {\n    \"react\": \">=16.9.0\",\n    \"react-dom\": \">=16.9.0\"\n  },\n  \"dependencies\": {\n    \"@ant-design/icons\": \"^5.4.0\",\n    \"@chamn/engine\": \"workspace:*\",\n    \"@chamn/layout\": \"workspace:*\",\n    \"@chamn/material\": \"workspace:*\",\n    \"@chamn/model\": \"workspace:*\",\n    \"@chamn/render\": \"workspace:*\",\n    \"ahooks\": \"^3.7.4\",\n    \"antd\": \"^5.23.2\",\n    \"color\": \"^4.2.3\",\n    \"i18next\": \"^22.1.5\",\n    \"loadjs\": \"^4.3.0\",\n    \"lodash-es\": \"^4.17.21\",\n    \"mitt\": \"^3.0.0\",\n    \"re-resizable\": \"^6.9.9\",\n    \"react-color\": \"^2.19.3\",\n    \"vite-plugin-static-copy\": \"^0.17.0\"\n  },\n  \"devDependencies\": {\n    \"@babel/core\": \"^7.21.0\",\n    \"@chamn/build-script\": \"workspace:*\",\n    \"@chamn/demo-page\": \"workspace:*\",\n    \"@types/color\": \"^3.0.4\",\n    \"@types/css\": \"^0.0.34\",\n    \"@types/loadjs\": \"^4.0.4\",\n    \"@types/lodash-es\": \"^4.17.6\",\n    \"@types/react\": \"^18.2.0\",\n    \"@types/react-color\": \"^3.0.6\",\n    \"@types/react-dom\": \"^18.0.6\",\n    \"babel-loader\": \"^8.3.0\",\n    \"cross-env\": \"^7.0.3\",\n    \"prop-types\": \"^15.8.1\",\n    \"rc-select\": \"^14.4.0\",\n    \"react\": \"^18.2.0\",\n    \"react-contenteditable\": \"^3.3.7\",\n    \"react-dom\": \"^18.2.0\",\n    \"react-i18next\": \"^12.1.1\",\n    \"react-router-dom\": \"^6.7.0\",\n    \"sass\": \"^1.54.0\",\n    \"storybook\": \"^7.4.0\"\n  },\n  \"config\": {}\n}"
  },
  {
    "path": "packages/engine-website-app/src/build-script-env.d.ts",
    "content": "/// <reference types=\"@chamn/build-script/client\" />\n\n/** 包版本号 */\ndeclare const __PACKAGE_VERSION__: string;\n/** 构建版本号 */\ndeclare const __BUILD_VERSION__: string;\n/** 运行模式 */\ndeclare const __RUN_MODE__: string;\n"
  },
  {
    "path": "packages/engine-website-app/src/index.css",
    "content": "html,\nbody,\n#root {\n  width: 100%;\n  height: 100%;\n  overflow: hidden;\n}\n\nbody {\n  margin: 0;\n  padding: 0;\n}\n\n.logo {\n  height: 100%;\n  font-size: 20px;\n  display: flex;\n  align-items: center;\n  margin-left: 20px;\n  font-weight: bolder;\n  margin-right: auto;\n}\n"
  },
  {
    "path": "packages/engine-website-app/src/index.tsx",
    "content": "import ReactDOMClient from 'react-dom/client';\nimport { RouterProvider } from 'react-router-dom';\nimport { router } from './router';\n\nReactDOMClient.createRoot(document.getElementById('root') as HTMLElement).render(<RouterProvider router={router} />);\n"
  },
  {
    "path": "packages/engine-website-app/src/lib/index.tsx",
    "content": "import { Resizable } from 're-resizable';\nimport { useEffect, useRef, useState } from 'react';\nimport ContentEditable from 'react-contenteditable';\n\nexport const CLayout = () => {\n  const text = useRef('123123123');\n  const contentEditableRef = useRef(null);\n\n  const handleChange = (evt: any) => {\n    text.current = evt.target.value;\n  };\n\n  const handleBlur = () => {\n    console.log(text.current);\n  };\n\n  useEffect(() => {\n    document.addEventListener('click', (e) => {\n      if (e.target && contentEditableRef.current) {\n        if (!(contentEditableRef.current as HTMLDivElement).contains(e.target as any)) {\n          (contentEditableRef.current as any)?.blur();\n          setCanEdit(false);\n        }\n      }\n    });\n  }, []);\n\n  const [canEdit, setCanEdit] = useState(false);\n\n  return (\n    <div>\n      CLayout 布局样例\n      <span\n        onDoubleClick={() => {\n          setCanEdit(true);\n          (contentEditableRef.current as any)?.focus();\n        }}\n      >\n        <ContentEditable\n          style={{\n            pointerEvents: canEdit ? 'auto' : 'none',\n          }}\n          id=\"a4455\"\n          className=\"as44556\"\n          onMouseDown={() => {\n            console.log('mouse down');\n          }}\n          innerRef={contentEditableRef}\n          html={text.current}\n          onBlur={handleBlur}\n          onChange={handleChange}\n        />\n      </span>\n      <Resizable\n        style={{\n          backgroundColor: 'red',\n        }}\n        handleClasses={{\n          left: 'resize-handle',\n          right: 'resize-handle',\n          top: 'resize-handle',\n          bottom: 'resize-handle',\n          topLeft: 'resize-handle',\n          topRight: 'resize-handle',\n          bottomLeft: 'resize-handle',\n          bottomRight: 'resize-handle',\n        }}\n        defaultSize={{\n          width: 320,\n          height: 200,\n        }}\n      >\n        Sample with default size\n      </Resizable>\n    </div>\n  );\n};\n"
  },
  {
    "path": "packages/engine-website-app/src/page/Editor/index.tsx",
    "content": "import { BasePage, LayoutPage } from '@chamn/demo-page';\nimport { Button, Dropdown, message, Modal, Select } from 'antd';\nimport React, { useCallback, useEffect, useRef, useState } from 'react';\nimport ReactDOM from 'react-dom';\nimport ReactDOMClient from 'react-dom/client';\nimport '../../index.css';\nimport { ArrowUpOutlined, RollbackOutlined } from '@ant-design/icons';\n\nimport commonMeta from '@chamn/material/dist/meta';\n\nimport commonComponentUrl from '@chamn/material/dist/index.umd.js?url';\nimport commonComponentCSS from '@chamn/material/dist/style.css?url';\nimport {\n  DEFAULT_PLUGIN_LIST,\n  DesignerSizer,\n  DisplaySourceSchema,\n  EnginContext,\n  Engine,\n  InnerComponentMeta,\n  RightPanelConfig,\n} from '@chamn/engine';\nimport '@chamn/engine/dist/style.css';\nimport { DesignerPluginInstance } from '@chamn/engine/dist/plugins/Designer/type';\nimport { PluginInstance } from '@chamn/engine/dist/core/pluginManager';\n\nconst win = window as any;\nwin.React = React;\nwin.ReactDOM = ReactDOM;\nwin.ReactDOMClient = ReactDOMClient;\n\nconst buildVersion = `t_${__BUILD_VERSION__}`;\n\nconst demoPageMap = {\n  LayoutPage,\n  BasePage,\n};\n\nconst assetPackagesList = [\n  {\n    package: commonMeta.package,\n    globalName: commonMeta.globalName,\n    resources: [\n      {\n        src: commonComponentUrl,\n      },\n      {\n        src: commonComponentCSS,\n      },\n    ],\n  },\n];\n\nexport const App = () => {\n  const [ready, setReady] = useState(false);\n  const [page, setPage] = useState(BasePage);\n  const [lang, setLang] = useState(() => {\n    const lang = localStorage.getItem('lang') || 'zh_CN';\n    return lang;\n  });\n\n  const engineRef = useRef<EnginContext>();\n\n  useEffect(() => {\n    // check 本地版本号，如果不一致直接覆盖本地所有的\n    const localBuildVersion = localStorage.getItem('build_version');\n    if (localBuildVersion !== buildVersion && !import.meta.env.DEV) {\n      // 清理 schema, 因为可能 协议不兼容，demo 可以这样粗暴处理\n      localStorage.setItem('pageSchema', '');\n      localStorage.setItem('build_version', buildVersion);\n    }\n    const localPage = localStorage.getItem('pageSchema');\n    if (localPage) {\n      setPage(JSON.parse(localPage));\n    }\n    setReady(true);\n  }, []);\n  const onReady = useCallback(\n    async (ctx: EnginContext) => {\n      engineRef.current = ctx;\n      engineRef.current?.engine.getI18n()?.changeLanguage(lang);\n\n      const designer = await ctx.pluginManager.get<DesignerPluginInstance>('Designer');\n\n      const reloadPage = async () => {\n        setTimeout(() => {\n          const designerExport = designer?.export;\n          console.log('to reload');\n          designerExport?.reload();\n        }, 0);\n      };\n\n      const workbench = ctx.engine.getWorkbench();\n\n      workbench?.replaceTopBarView(\n        <div\n          style={{\n            width: '100%',\n            height: '100%',\n            display: 'flex',\n            alignItems: 'center',\n            justifyContent: 'flex-end',\n            paddingRight: '10px',\n          }}\n        >\n          <div className=\"logo\">Chameleon EG</div>\n\n          <Select\n            defaultValue={'BasePage'}\n            style={{ width: 200, marginRight: '10px' }}\n            onChange={(val) => {\n              const newPage = (demoPageMap as any)[val];\n              if (newPage) {\n                setPage(newPage);\n                ctx.engine.pageModel.reloadPage(newPage);\n              }\n            }}\n            options={[\n              {\n                value: 'BasePage',\n                label: 'Base Page',\n              },\n              {\n                value: 'LayoutPage',\n                label: 'Advance Layout Page',\n              },\n            ]}\n          />\n          <div\n            style={{\n              height: '100%',\n              display: 'flex',\n              alignItems: 'center',\n              justifyContent: 'center',\n              marginRight: '10px',\n            }}\n          >\n            {ctx && <DesignerSizer ctx={ctx} />}\n          </div>\n          <Select\n            defaultValue={lang}\n            style={{ width: 100, marginRight: '10px' }}\n            onChange={(val) => {\n              setLang(val);\n              engineRef.current?.engine.getI18n()?.changeLanguage(val);\n            }}\n            options={[\n              {\n                value: 'zh_CN',\n                label: 'Chinese',\n              },\n              {\n                value: 'en_US',\n                label: 'English',\n              },\n            ]}\n          />\n          <a target=\"_blank\" href=\"https://hlerenow.github.io/chameleon/documents/\" rel=\"noreferrer\">\n            <Button style={{ marginRight: '10px' }}>Documents </Button>\n          </a>\n          <a target=\"_blank\" href=\"https://github.com/hlerenow/chameleon\" rel=\"noreferrer\">\n            <Button style={{ marginRight: '10px' }}>Github </Button>\n          </a>\n\n          <Button\n            style={{ marginRight: '10px' }}\n            onClick={async () => {\n              const res = await ctx.pluginManager.get<any>('History');\n              res?.export.preStep();\n            }}\n          >\n            <RollbackOutlined />\n          </Button>\n          <Button\n            style={{ marginRight: '10px' }}\n            onClick={async () => {\n              const res = await ctx.pluginManager.get<any>('History');\n              res?.export.nextStep();\n            }}\n          >\n            <RollbackOutlined\n              style={{\n                transform: 'rotateY(180deg)',\n              }}\n            />\n          </Button>\n\n          <DisplaySourceSchema pageModel={ctx.engine.pageModel} engineCtx={ctx}>\n            <Button style={{ marginRight: '10px' }}>Source Code</Button>\n          </DisplaySourceSchema>\n\n          <Button\n            style={{ marginRight: '10px' }}\n            onClick={() => {\n              reloadPage();\n            }}\n          >\n            Refresh Page\n          </Button>\n\n          <Dropdown.Button\n            style={{ marginRight: '10px', width: '110px' }}\n            menu={{\n              items: [],\n            }}\n            buttonsRender={() => {\n              return [\n                <Button\n                  onClick={() => {\n                    let src = '/#/preview';\n                    if (location.href.includes('hlerenow')) {\n                      src = '/chameleon/#/preview';\n                    }\n\n                    Modal.info({\n                      closable: true,\n                      icon: null,\n                      width: 'calc(100vw - 100px)',\n                      centered: true,\n                      title: (\n                        <div>\n                          Preview\n                          <Button\n                            size=\"small\"\n                            style={{\n                              float: 'right',\n                              marginRight: '30px',\n                            }}\n                            onClick={() => {\n                              window.open(src);\n                            }}\n                          >\n                            Open in new window\n                          </Button>\n                        </div>\n                      ),\n                      content: (\n                        <div\n                          style={{\n                            width: '100%',\n                            height: 'calc(100vh - 200px)',\n                          }}\n                        >\n                          <iframe\n                            style={{\n                              border: '1px solid #e7e7e7',\n                              width: '100%',\n                              height: '100%',\n                              borderRadius: '4px',\n                              overflow: 'hidden',\n                            }}\n                            src={src}\n                          />\n                        </div>\n                      ),\n                      footer: <></>,\n                    });\n                  }}\n                  key={1}\n                >\n                  Preview\n                </Button>,\n                <Button\n                  key={2}\n                  style={{\n                    padding: '0 8px',\n                  }}\n                  onClick={() => {\n                    let src = '/#/preview';\n                    if (location.href.includes('hlerenow')) {\n                      src = '/chameleon/#/preview';\n                    }\n                    window.open(src);\n                  }}\n                >\n                  <ArrowUpOutlined\n                    style={{\n                      fontSize: '12px',\n                      transform: 'rotate(30deg)',\n                    }}\n                  />\n                </Button>,\n              ];\n            }}\n          >\n            Preview\n          </Dropdown.Button>\n\n          <Button\n            type=\"primary\"\n            onClick={() => {\n              const newPage = ctx.engine.pageModel.export();\n              localStorage.setItem('pageSchema', JSON.stringify(newPage));\n              message.success('Save successfully');\n            }}\n          >\n            Save\n          </Button>\n        </div>\n      );\n    },\n    [lang]\n  );\n\n  if (!ready) {\n    return <>loading...</>;\n  }\n\n  return (\n    <Engine\n      plugins={DEFAULT_PLUGIN_LIST}\n      schema={page}\n      onMount={(ctx) => {\n        setTimeout(async () => {\n          await ctx.engine.updateMaterials(\n            [],\n            [\n              {\n                package: 'lodash',\n                globalName: 'lodash',\n                resources: [\n                  {\n                    src: 'https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js',\n                  },\n                ],\n              },\n              {\n                package: 'dayjs',\n                globalName: 'dayjs',\n                resources: [\n                  {\n                    src: 'https://cdn.jsdelivr.net/npm/dayjs@1.11.12/dayjs.min.js',\n                  },\n                ],\n              },\n            ]\n          );\n          console.log('add material successfully');\n        }, 2 * 1000);\n      }}\n      // 传入组件物料\n      material={[...InnerComponentMeta, ...commonMeta.meta]}\n      // 组件物料对应的 js 运行库，只能使用 umd 模式的 js\n      assetPackagesList={assetPackagesList}\n      beforePluginRun={({ pluginManager }) => {\n        pluginManager.customPlugin('RightPanel', (pluginInstance: PluginInstance<RightPanelConfig>) => {\n          pluginInstance.ctx.config.customPropertySetterMap = {\n            TestSetter: (props: any) => {\n              useEffect(() => {\n                console.log(props);\n                const currentNode = props.setterContext.pluginCtx.engine.getActiveNode();\n                currentNode.value.configure.isContainer = false;\n                currentNode.value.children = [];\n                currentNode.updateValue();\n              }, [props]);\n              return <div>123</div>;\n            },\n          };\n\n          return pluginInstance;\n        });\n\n        pluginManager.customPlugin('Designer', (pluginInstance) => {\n          return pluginInstance;\n        });\n      }}\n      onReady={onReady}\n    />\n  );\n};\n"
  },
  {
    "path": "packages/engine-website-app/src/page/Preview/index.tsx",
    "content": "import { useEffect, useState } from 'react';\nimport {\n  ReactAdapter,\n  Render,\n  useRender,\n  AssetLoader,\n  collectVariable,\n  flatObject,\n  getComponentsLibs,\n  getThirdLibs,\n} from '@chamn/render';\nimport { AssetPackage, CPageDataType } from '@chamn/model';\n\nconst loadAssets = async (assets: AssetPackage[]) => {\n  // 注入组件物料资源\n  const assetLoader = new AssetLoader(assets);\n  try {\n    await assetLoader.load();\n    // 从子窗口获取物料对象\n    const componentCollection = collectVariable(assets, window);\n    return componentCollection;\n  } catch {\n    return null;\n  }\n};\n\nexport const Preview = () => {\n  const [page, setPage] = useState<CPageDataType>();\n  const renderHandle = useRender();\n  const [loading, setLoading] = useState(true);\n  const [pageComponents, setPageComponents] = useState({});\n  const [renderContext, setRenderContext] = useState({});\n  // 需要区分 那些 UI 组件那些第三方库的对象，分别注入\n  const loadPageAssets = async (pageInfo: CPageDataType) => {\n    const assets = pageInfo.assets || [];\n    const allLibs = (await loadAssets(assets)) || {};\n    const componentsLibs = getComponentsLibs(flatObject(allLibs), pageInfo.componentsMeta);\n    const thirdLibs = getThirdLibs(allLibs, pageInfo.thirdLibs || []);\n    if (componentsLibs) {\n      setPageComponents(componentsLibs);\n      setRenderContext({ thirdLibs });\n      setLoading(false);\n    }\n  };\n  useEffect(() => {\n    const localPage = localStorage.getItem('pageSchema');\n    if (localPage) {\n      const page: CPageDataType = JSON.parse(localPage);\n      setPage(page);\n      loadPageAssets(page);\n    }\n  }, []);\n\n  if (loading) {\n    return <>Not find page info on local, please ensure you save it on editor</>;\n  }\n  return (\n    <div className=\"App\" style={{ overflow: 'auto', height: '100%' }}>\n      <Render\n        page={page}\n        components={{\n          ...pageComponents,\n        }}\n        render={renderHandle}\n        adapter={ReactAdapter}\n        $$context={renderContext as any}\n      />\n    </div>\n  );\n};\n"
  },
  {
    "path": "packages/engine-website-app/src/render.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <link rel=\"icon\" type=\"image/svg+xml\" href=\"/vite.svg\" />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n  <title>Low-code engine</title>\n  <style>\n    * {\n      margin: 0;\n      padding: 0;\n    }\n\n    html,\n    body,\n    #root {\n      height: 100%;\n      width: 100%;\n    }\n  </style>\n  <script>\n    window.React = window.parent.React;\n    window.ReactDOM = window.parent.ReactDOM;\n    window.ReactDOMClient = window.parent.ReactDOMClient;\n  </script>\n</head>\n\n<body>\n  <div id=\"app\"></div>\n  <script type=\"module\" src=\"./render.tsx\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "packages/engine-website-app/src/render.tsx",
    "content": "import renderAsURL from '@chamn/render/dist/index.umd.js?url';\nimport loadjs from 'loadjs';\n\nloadjs([renderAsURL], () => {\n  console.log('load render.umd.js success');\n});\n"
  },
  {
    "path": "packages/engine-website-app/src/router.tsx",
    "content": "import { createHashRouter } from 'react-router-dom';\nimport { App } from './page/Editor';\nimport { Preview } from './page/Preview';\n\nexport const router: any = createHashRouter([\n  {\n    path: '/',\n    element: <App />,\n  },\n  {\n    path: '/preview',\n    element: <Preview />,\n  },\n]);\n"
  },
  {
    "path": "packages/engine-website-app/src/typing.d.ts",
    "content": "declare module '*.scss' {\n  const content: { [key: string]: any };\n  export = content;\n}\n\ndeclare module '*.worker?worker' {\n  const content: any;\n  export = content;\n}\n"
  },
  {
    "path": "packages/engine-website-app/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ESNext\",\n    \"useDefineForClassFields\": true,\n    \"lib\": [\n      \"DOM\",\n      \"DOM.Iterable\",\n      \"ESNext\"\n    ],\n    \"allowJs\": false,\n    \"skipLibCheck\": true,\n    \"esModuleInterop\": false,\n    \"allowSyntheticDefaultImports\": true,\n    \"strict\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": false,\n    \"noEmit\": true,\n    \"jsx\": \"react-jsx\",\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"@/*\": [\n        \"src/*\"\n      ],\n      \"mitt\": [\n        \"./node_modules/mitt\"\n      ]\n    },\n  },\n  \"include\": [\n    \"src\"\n  ],\n}"
  },
  {
    "path": "packages/layout/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "packages/layout/CHANGELOG.md",
    "content": "# Change Log\n\nAll notable changes to this project will be documented in this file.\nSee [Conventional Commits](https://conventionalcommits.org) for commit guidelines.\n\n## [0.10.4](https://github.com/ByteCrazy/chameleon/compare/v0.10.3...v0.10.4) (2026-01-17)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.10.3](https://github.com/ByteCrazy/chameleon/compare/v0.10.2...v0.10.3) (2026-01-17)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.10.2](https://github.com/ByteCrazy/chameleon/compare/v0.10.1...v0.10.2) (2026-01-17)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.10.1](https://github.com/ByteCrazy/chameleon/compare/v0.10.0...v0.10.1) (2026-01-11)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.10.0](https://github.com/hlerenow/chameleon/compare/v0.9.3...v0.10.0) (2025-12-25)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* 优化构建配置，修复 ES 模块外部化问题 ([866f0ae](https://github.com/hlerenow/chameleon/commit/866f0ae39472625137d73d143625d88d873f6c00))\n\n## [0.9.3](https://github.com/ByteCrazy/chameleon/compare/v0.9.2...v0.9.3) (2025-07-20)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.9.2](https://github.com/ByteCrazy/chameleon/compare/v0.9.1...v0.9.2) (2025-07-13)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.9.1](https://github.com/ByteCrazy/chameleon/compare/v0.9.0...v0.9.1) (2025-07-13)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.9.0](https://github.com/ByteCrazy/chameleon/compare/v0.8.6...v0.9.0) (2025-07-13)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.8.6](https://github.com/ByteCrazy/chameleon/compare/v0.8.5...v0.8.6) (2025-06-21)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.8.5](https://github.com/ByteCrazy/chameleon/compare/v0.8.4...v0.8.5) (2025-04-13)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.8.4](https://github.com/ByteCrazy/chameleon/compare/v0.8.3...v0.8.4) (2025-04-13)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.8.3](https://github.com/ByteCrazy/chameleon/compare/v0.8.2...v0.8.3) (2025-04-12)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.8.2](https://github.com/ByteCrazy/chameleon/compare/v0.8.1...v0.8.2) (2025-04-11)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.8.1](https://github.com/ByteCrazy/chameleon/compare/v0.8.0...v0.8.1) (2025-04-10)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.8.0](https://github.com/ByteCrazy/chameleon/compare/v0.7.0...v0.8.0) (2025-04-10)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.7.0](https://github.com/ByteCrazy/chameleon/compare/v0.6.0...v0.7.0) (2025-04-06)\n\n### ✨ Features | 新功能\n\n* **engine, layout:** 🎸 optimize select node interactive time ([41d105c](https://github.com/ByteCrazy/chameleon/commit/41d105c2408458b38cda0c9fac601dd89fd744aa))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, layout:** 🐛 optimize select active box ([b1a6041](https://github.com/ByteCrazy/chameleon/commit/b1a604140652f1c0871a463bc32020c39a07846c))\n\n## [0.6.0](https://github.com/ByteCrazy/chameleon/compare/v0.5.2...v0.6.0) (2025-03-30)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.5.2](https://github.com/ByteCrazy/chameleon/compare/v0.5.1...v0.5.2) (2025-03-30)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.5.1](https://github.com/ByteCrazy/chameleon/compare/v0.5.0...v0.5.1) (2025-03-30)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.5.0](https://github.com/ByteCrazy/chameleon/compare/v0.4.0...v0.5.0) (2025-03-30)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.4.0](https://github.com/ByteCrazy/chameleon/compare/v0.3.21...v0.4.0) (2025-03-29)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.3.21](https://github.com/ByteCrazy/chameleon/compare/v0.3.20...v0.3.21) (2025-03-26)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.3.20](https://github.com/ByteCrazy/chameleon/compare/v0.3.19...v0.3.20) (2025-03-26)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.3.19](https://github.com/ByteCrazy/chameleon/compare/v0.3.18...v0.3.19) (2025-03-26)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.3.18](https://github.com/ByteCrazy/chameleon/compare/v0.3.17...v0.3.18) (2025-03-26)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.3.17](https://github.com/ByteCrazy/chameleon/compare/v0.3.16...v0.3.17) (2025-03-25)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, material, model, render:** 🐛 fixed node update value material is undefined ([969174d](https://github.com/ByteCrazy/chameleon/commit/969174da968aec4a1ee1fec7ca44a5e459be56bc))\n\n## [0.3.16](https://github.com/ByteCrazy/chameleon/compare/v0.3.15...v0.3.16) (2025-03-24)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.3.15](https://github.com/ByteCrazy/chameleon/compare/v0.3.14...v0.3.15) (2025-03-23)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **layout:** 🐛 fixed canvas default css ([41d0ebd](https://github.com/ByteCrazy/chameleon/commit/41d0ebd91b0d9a9fa2d1b7892ec1ec82ecd44a33))\n\n## [0.3.14](https://github.com/ByteCrazy/chameleon/compare/v0.3.13...v0.3.14) (2025-03-23)\n\n### ✨ Features | 新功能\n\n* **layout:** 🎸 ban drag img audio viewo el at editor canvas ([0f2624b](https://github.com/ByteCrazy/chameleon/commit/0f2624bd45eada0290610f7bb52e1683c0c84189))\n\n## [0.3.13](https://github.com/ByteCrazy/chameleon/compare/v0.3.12...v0.3.13) (2025-03-23)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.3.12](https://github.com/ByteCrazy/chameleon/compare/v0.3.11...v0.3.12) (2025-03-23)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, layout:** 🐛 fixed drag interactive and fixed accetpNode action ([c15d03c](https://github.com/ByteCrazy/chameleon/commit/c15d03cc0406533e5eab55e27ce5eb1223d91c72))\n\n## [0.3.11](https://github.com/ByteCrazy/chameleon/compare/v0.3.10...v0.3.11) (2025-03-22)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.3.10](https://github.com/ByteCrazy/chameleon/compare/v0.3.9...v0.3.10) (2025-03-22)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **layout:** 🐛 fixed placeholder UI ([7134583](https://github.com/ByteCrazy/chameleon/commit/71345836c5575399f2fadcd4be5773c593efb95f))\n\n## [0.3.9](https://github.com/ByteCrazy/chameleon/compare/v0.3.8...v0.3.9) (2025-03-22)\n\n### ✨ Features | 新功能\n\n* **engine, layout:** 🎸 optimize drag interactive ([f512f14](https://github.com/ByteCrazy/chameleon/commit/f512f14ecf3caaa148279543999a74d35ae44701))\n\n## [0.3.8](https://github.com/ByteCrazy/chameleon/compare/v0.3.7...v0.3.8) (2025-03-16)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.3.7](https://github.com/ByteCrazy/chameleon/compare/v0.3.6...v0.3.7) (2025-03-16)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.3.6](https://github.com/ByteCrazy/chameleon/compare/v0.3.5...v0.3.6) (2025-03-09)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.3.5](https://github.com/ByteCrazy/chameleon/compare/v0.3.4...v0.3.5) (2025-03-09)\n\n### ✨ Features | 新功能\n\n* **engine, layout:** 🎸 support controll preview mode ([97b83e5](https://github.com/ByteCrazy/chameleon/commit/97b83e58f0f22c76da51e5a3d22db2c82a2f70d2))\n\n## [0.3.4](https://github.com/ByteCrazy/chameleon/compare/v0.3.3...v0.3.4) (2025-02-16)\n\n### ✨ Features | 新功能\n\n* **engine, layout, render:** 🎸 action node express support $response ([9bf1570](https://github.com/ByteCrazy/chameleon/commit/9bf1570c3b80404be78a5d3ca2c401464e7645d3))\n\n## [0.3.3](https://github.com/ByteCrazy/chameleon/compare/v0.3.2...v0.3.3) (2025-02-16)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.3.2](https://github.com/ByteCrazy/chameleon/compare/v0.3.1...v0.3.2) (2025-02-16)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.3.1](https://github.com/ByteCrazy/chameleon/compare/v0.3.0...v0.3.1) (2025-02-16)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.3.0](https://github.com/ByteCrazy/chameleon/compare/v0.2.4...v0.3.0) (2025-02-15)\n\n### ✨ Features | 新功能\n\n* **docs-app, engine, engine-website-app, layout, material, model, render:** 🎸 do ActionFlowSetter 50% ([6399466](https://github.com/ByteCrazy/chameleon/commit/6399466c7253436591e071df25828a357bb7089e))\n\n## [0.2.4](https://github.com/ByteCrazy/chameleon/compare/v0.2.3...v0.2.4) (2024-12-08)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.2.3](https://github.com/ByteCrazy/chameleon/compare/v0.2.2...v0.2.3) (2024-12-08)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.2.2](https://github.com/ByteCrazy/chameleon/compare/v0.2.1...v0.2.2) (2024-12-07)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.2.1](https://github.com/ByteCrazy/chameleon/compare/v0.2.0...v0.2.1) (2024-12-07)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.2.0](https://github.com/ByteCrazy/chameleon/compare/v0.1.1...v0.2.0) (2024-12-07)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **layout:** 🐛 fixed canvas scroll ([9f4c5b9](https://github.com/ByteCrazy/chameleon/commit/9f4c5b9afaf5e3370a72256f2484b9390354f5a3))\n\n### ✨ Features | 新功能\n\n* **build-script, demo-page, engine, engine-website-app, layout, material, model, render:** 🎸 upgrade vite to 6.0 ([bcac2b1](https://github.com/ByteCrazy/chameleon/commit/bcac2b15b83b41a7042ca37368c1b45302ad81d5))\n* **engine, layout, render:** 🎸 replace findDOMNode API ([af2531a](https://github.com/ByteCrazy/chameleon/commit/af2531a095124ac55d4f6dc6896d430f52f5da82))\n\n## [0.1.1](https://github.com/ByteCrazy/chameleon/compare/v0.1.0...v0.1.1) (2024-11-11)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.1.0](https://github.com/ByteCrazy/chameleon/compare/v0.0.46...v0.1.0) (2024-09-07)\n\n### 👷 Continuous Integration | CI 配置\n\n* **engine, engine-website-app, layout, model:** 🎡 update github ci ([259e9db](https://github.com/ByteCrazy/chameleon/commit/259e9db229576cd09c5aae1f28a2c228927011c7))\n\n### ✨ Features | 新功能\n\n* **build-script, demo-page, engine, layout, render:** 🎸 add lang switch ([29da65e](https://github.com/ByteCrazy/chameleon/commit/29da65ee1aa09550d910ddfbbcb9d8b4db983373))\n* **docs-app, engine, layout, material, model, render:** 🎸 optimize GL layout ([02274b4](https://github.com/ByteCrazy/chameleon/commit/02274b432903dc247c5613873f14715dd806decd))\n* **engine, layout:** 🎸 add set canvas width method ([a18369f](https://github.com/ByteCrazy/chameleon/commit/a18369f0d4bbb4bcf04ce2695be313d767d4bbe5))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **demo-page, docs-app, engine, engine-website-app, layout, material:** 🐛 fixed material meta not correct ([55b755c](https://github.com/ByteCrazy/chameleon/commit/55b755ce0c833e594b46447b2d6608cf56f7a593))\n* **docs-website, engine, layout:** 🐛 fixed higligh toolbar pos ([0356109](https://github.com/ByteCrazy/chameleon/commit/0356109e8a11a5a85ab90d1610ef7ef8549db0a9))\n* **engine, layout:** 🐛 fixed toolbox width not correct ([4dc159a](https://github.com/ByteCrazy/chameleon/commit/4dc159a1931a853ba4cdb664638f27ba71b1ecf2))\n\n## [0.0.46](https://github.com/ByteCrazy/chameleon/compare/v0.0.45...v0.0.46) (2024-06-30)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.0.45](https://github.com/ByteCrazy/chameleon/compare/v0.0.44...v0.0.45) (2024-06-30)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.0.44](https://github.com/ByteCrazy/chameleon/compare/v0.0.43...v0.0.44) (2024-06-30)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.0.43](https://github.com/ByteCrazy/chameleon/compare/v0.0.42...v0.0.43) (2024-06-29)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, layout:** 🎸 remove scss dts generate ([9ba7af3](https://github.com/ByteCrazy/chameleon/commit/9ba7af30601804f94e90a0408745fd48de38d8b5))\n\n## [0.0.42](https://github.com/ByteCrazy/chameleon/compare/v0.0.41...v0.0.42) (2024-06-01)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, layout:** 🐛 fixed drag and drop problem on chrome ([5c655c3](https://github.com/ByteCrazy/chameleon/commit/5c655c3a2f288bd90b70212f0f114adf3c23f8a1))\n\n## [0.0.41](https://github.com/ByteCrazy/chameleon/compare/v0.0.40...v0.0.41) (2024-05-26)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, layout, material, model, render:** 🎸 optimize build script ([0a60cc4](https://github.com/ByteCrazy/chameleon/commit/0a60cc4f5e5635d9e544b5141eaed5b8433901a5))\n\n## [0.0.40](https://github.com/ByteCrazy/chameleon/compare/v0.0.39...v0.0.40) (2024-04-28)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.0.39](https://github.com/ByteCrazy/chameleon/compare/v0.0.38...v0.0.39) (2024-04-27)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **build-script, demo-page, engine, layout, material, model, render:** 🐛 fix flatObject collectVariable refeer pos ([1c4163a](https://github.com/ByteCrazy/chameleon/commit/1c4163aeeb89176a1ff5b50f76930889df7751fe))\n\n## [0.0.38](https://github.com/ByteCrazy/chameleon/compare/v0.0.37...v0.0.38) (2024-04-27)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, layout:** 🐛 fixed assets load iuess when relaod ([3eccc60](https://github.com/ByteCrazy/chameleon/commit/3eccc60bb7be5a469704a9c4f769161e525481f4))\n\n## [0.0.37](https://github.com/ByteCrazy/chameleon/compare/v0.0.36...v0.0.37) (2024-04-27)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.0.36](https://github.com/ByteCrazy/chameleon/compare/v0.0.35...v0.0.36) (2024-04-27)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.0.35](https://github.com/ByteCrazy/chameleon/compare/v0.0.34...v0.0.35) (2024-04-27)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.0.34](https://github.com/ByteCrazy/chameleon/compare/v0.0.33...v0.0.34) (2024-04-27)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.0.33](https://github.com/ByteCrazy/chameleon/compare/v0.0.32...v0.0.33) (2024-04-27)\n\n### ✨ Features | 新功能\n\n* **engine, layout, render:** 🎸 optimize history step save ([dca964a](https://github.com/ByteCrazy/chameleon/commit/dca964a990a3cc0c5fd853d853a55a4957999644))\n\n## [0.0.32](https://github.com/ByteCrazy/chameleon/compare/v0.0.31...v0.0.32) (2024-01-30)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.0.31](https://github.com/ByteCrazy/chameleon/compare/v0.0.30...v0.0.31) (2023-10-17)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.0.30](https://github.com/ByteCrazy/chameleon/compare/v0.0.29...v0.0.30) (2023-10-17)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.0.29](https://github.com/ByteCrazy/chameleon/compare/v0.0.28...v0.0.29) (2023-10-17)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.0.28](https://github.com/ByteCrazy/chameleon/compare/v0.0.27...v0.0.28) (2023-08-29)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **layout:** 🐛 fixed not auto inert component when drag on a empty aera ([45d9ae3](https://github.com/ByteCrazy/chameleon/commit/45d9ae3b0b874a225e068f378f6be07584bb2f87))\n\n## [0.0.27](https://github.com/ByteCrazy/chameleon/compare/v0.0.26...v0.0.27) (2023-08-28)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.0.26](https://github.com/ByteCrazy/chameleon/compare/v0.0.25...v0.0.26) (2023-08-27)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.0.25](https://github.com/ByteCrazy/chameleon/compare/v0.0.24...v0.0.25) (2023-08-27)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.0.24](https://github.com/ByteCrazy/chameleon/compare/v0.0.23...v0.0.24) (2023-08-27)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.0.23](https://github.com/ByteCrazy/chameleon/compare/v0.0.22...v0.0.23) (2023-08-26)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.0.22](https://github.com/ByteCrazy/chameleon/compare/v0.0.21...v0.0.22) (2023-08-24)\n\n### ✨ Features | 新功能\n\n* **demo-page, engine, layout, model:** 🎸 add disableEditorDragDom config ([1779f44](https://github.com/ByteCrazy/chameleon/commit/1779f44aff20370ea3336e72352032c6416f7dd3))\n\n## [0.0.21](https://github.com/ByteCrazy/chameleon/compare/v0.0.20...v0.0.21) (2023-08-19)\n\n### ✨ Features | 新功能\n\n* **engine, layout, model, render:** 🎸 optimize wrapComponent config ([d5916a7](https://github.com/ByteCrazy/chameleon/commit/d5916a7e6ee3cf79a32d4d23663b6873d86fe671))\n\n## [0.0.20](https://github.com/ByteCrazy/chameleon/compare/v0.0.19...v0.0.20) (2023-08-19)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.0.19](https://github.com/ByteCrazy/chameleon/compare/v0.0.18...v0.0.19) (2023-08-17)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.0.18](https://github.com/ByteCrazy/chameleon/compare/v0.0.17...v0.0.18) (2023-08-14)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.0.17](https://github.com/ByteCrazy/chameleon/compare/v0.0.16...v0.0.17) (2023-08-06)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **layout:** 🐛 fixed Cannot read properties of undefined (reading 'id') ([008655c](https://github.com/ByteCrazy/chameleon/commit/008655c35bb73a93c48f35bbbd5eac0a515d7163))\n\n## [0.0.16](https://github.com/ByteCrazy/chameleon/compare/v0.0.15...v0.0.16) (2023-08-06)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, layout:** 🐛 fixed some node not exits querySelector method ([2ddc186](https://github.com/ByteCrazy/chameleon/commit/2ddc1863e6eb207ae2e2b705a9fef4d23bece37c))\n\n### ✨ Features | 新功能\n\n* **build-script-example, demo-page, engine, layout, material, model, render:** 🎸 support direct import render from package on dev mode ([6bb38e2](https://github.com/ByteCrazy/chameleon/commit/6bb38e29678a8a4d08a7fcf9548fa548399259b0))\n* **demo-page, engine, layout, model:** 🎸 support  advanceCustom drag and drop hooks ([fb21e15](https://github.com/ByteCrazy/chameleon/commit/fb21e1501f6829e4f13a35ea7be942ff72b0be91))\n* **demo-page, engine, layout, model:** 🎸 support toolbarViewRender and ghostViewRedner ([43ced37](https://github.com/ByteCrazy/chameleon/commit/43ced371cfb7dbb58a96fc15e1bf635092307fa8))\n* **demo-page, engine, layout:** 🎸 support DropViewRender ([1c025a6](https://github.com/ByteCrazy/chameleon/commit/1c025a6d1f57f450b2de262a787375f3f4891008))\n* **engine, layout, model:** 🎸 add customDropViewRender prop ([cea7f1e](https://github.com/ByteCrazy/chameleon/commit/cea7f1e3c2b0a8a13aef7cdcb0191593414615aa))\n* **engine, layout, model:** 🎸 finish layout transform ([73ead35](https://github.com/ByteCrazy/chameleon/commit/73ead357b9aded2b5ee2b545da6f2b3677ce8393))\n* **layout, model:** 🎸 optimize advanceCustom type definition ([0f529d1](https://github.com/ByteCrazy/chameleon/commit/0f529d1a8d8adbfd0970ba13a75a116a26e4d2f8))\n* **layout, model:** 🎸 optimize advanceCustom type definition ([8cd11aa](https://github.com/ByteCrazy/chameleon/commit/8cd11aa9fb0dca0b605d9ed8504c2e827109dc8f))\n* **layout, model:** 🎸 update meterial types definition ([fcf4e82](https://github.com/ByteCrazy/chameleon/commit/fcf4e827942afe4df8ad4210a0a3134138a5b10e))\n* **layout:** 🎸 select hightlight use outline ([00654d0](https://github.com/ByteCrazy/chameleon/commit/00654d082c2823a6b01f109571f25a18bc51f5c5))\n* **layout:** 🎸 setCanDrag add hook ([04ec3b6](https://github.com/ByteCrazy/chameleon/commit/04ec3b687310f689bbaaaa4a037b25d9ae5f8b9a))\n\n## [0.0.15](https://github.com/ByteCrazy/chameleon/compare/v0.0.14...v0.0.15) (2023-07-01)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, layout:** 🐛 fixed component will be add repeatedly after reload page ([1507549](https://github.com/ByteCrazy/chameleon/commit/1507549632c3c6e09d8555fd0286fcc72855c358))\n\n### ✨ Features | 新功能\n\n* auto detect toolbox dir ([#13](https://github.com/ByteCrazy/chameleon/issues/13)) ([4047408](https://github.com/ByteCrazy/chameleon/commit/404740870a658fa1740abe29ca50feac4a4028e4))\n* **docs-website, engine, layout, material, render:** 🎸 support inject thridLib on $context ([ca2f074](https://github.com/ByteCrazy/chameleon/commit/ca2f07492b713c32e5a41d1f250f7888763cb665))\n\n## [0.0.14](https://github.com/ByteCrazy/chameleon/compare/v0.0.13...v0.0.14) (2023-05-10)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, layout:** 🐛 fixed new component can not be add into canvas ([4057fa9](https://github.com/ByteCrazy/chameleon/commit/4057fa925c18bf06325de04b270be5f6c23351c8))\n* **engine, layout:** 🐛 fixed type problem ([751a392](https://github.com/ByteCrazy/chameleon/commit/751a39244228f74138acf7f567d23e888b8ff687))\n\n## [0.0.13](https://github.com/ByteCrazy/chameleon/compare/v0.0.12...v0.0.13) (2023-05-03)\n\n### ✨ Features | 新功能\n\n* **build-script, build-script-example, demo-page, engine, layout, material, model, render:** 🎸 config code style config ([2325504](https://github.com/ByteCrazy/chameleon/commit/23255048fa4a3d4fc8f5cfa1312db5abd6cac70d))\n* **build-script, demo-page, engine, layout, model, render:** 🎸 update meterial schema ([14f4e5c](https://github.com/ByteCrazy/chameleon/commit/14f4e5caf0a4f6eca131e139d0c34ea21969dc0b))\n* **demo-page, docs-website, engine, layout, model, render:** 🎸 node support methods config ([e080b20](https://github.com/ByteCrazy/chameleon/commit/e080b20cea3cea27b95def196e078c1c225d3a83))\n* **demo-page, engine, layout, model:** 🎸 finish customEvent config ([84640e3](https://github.com/ByteCrazy/chameleon/commit/84640e3d06b79858590b9fe92ef4764fbe3f4f7b))\n* **demo-page, engine, layout:** 🎸 outline support cancel drag by node material ([cd428d3](https://github.com/ByteCrazy/chameleon/commit/cd428d362a8bfe7d5b6e58ed2b6290e19c00672b))\n* **demo-page, engine, layout:** 🎸 support cancel drag and custom node drag event ([825717e](https://github.com/ByteCrazy/chameleon/commit/825717e063846b72f95b0e41d8cba279ef5270c8))\n* **engine, layout, model:** 🎸 support supportDispatchNativeEvent field ([489fd05](https://github.com/ByteCrazy/chameleon/commit/489fd05b588e85ed4cea81cc33ab27739a1bac59))\n* **engine, layout:** 🎸 optimize layout event system ([56f35ef](https://github.com/ByteCrazy/chameleon/commit/56f35ef83cc7e1658bfeba9081f997ff457cf09e))\n* **engine, layout:** 🎸 support isSupportDispatchNativeEvent filed ([4f830b4](https://github.com/ByteCrazy/chameleon/commit/4f830b42b6e84d74e9d462c3b3e090de89686e18))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, layout, model:** 🐛 fixed type error ([d11e81f](https://github.com/ByteCrazy/chameleon/commit/d11e81f607ef6a41a2dfda0b4ac657a9a87e948c))\n\n## [0.0.12](https://github.com/ByteCrazy/chameleon/compare/v0.0.11...v0.0.12) (2023-04-23)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **build-script, build-script-example, engine, layout, material, model, render:** 🐛 fix can not find vite/client.d.ts problem ([8a53540](https://github.com/ByteCrazy/chameleon/commit/8a53540f75737707f69f93d2f701203dbf88084a))\n\n### ✨ Features | 新功能\n\n* **demo-page, engine, layout, model, render:** 🎸 node support config self isContainer property ([d94f1f9](https://github.com/ByteCrazy/chameleon/commit/d94f1f9203f2c273856fd4aa489a1ed9cbb0f0ec))\n\n## [0.0.11](https://github.com/ByteCrazy/chameleon/compare/v0.0.10...v0.0.11) (2023-04-21)\n\n### ✨ Features | 新功能\n\n* **build-script, build-script-example, demo-page, engine, layout, material, model, render:** 🎸 optimize build-script output file format ([eda44d2](https://github.com/ByteCrazy/chameleon/commit/eda44d255bd3c5af19013bdd61342e1f0817413a))\n\n## [0.0.10](https://github.com/ByteCrazy/chameleon/compare/v0.0.9...v0.0.10) (2023-04-17)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.0.9](https://github.com/ByteCrazy/chameleon/compare/v0.0.8...v0.0.9) (2023-04-17)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.0.8](https://github.com/ByteCrazy/chameleon/compare/v0.0.7...v0.0.8) (2023-04-16)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, layout, model, render:** 🎸 optimize pack package ([78d99c5](https://github.com/ByteCrazy/chameleon/commit/78d99c507ad65ca39101f0239467737d2b445c87))\n* **docs-website, engine, layout:** 🎸 add defaultRender for engine, add some docs ([91f4257](https://github.com/ByteCrazy/chameleon/commit/91f4257e9f9b9391267e4b8d64e6ed811912381f))\n\n## [0.0.7](https://github.com/ByteCrazy/chameleon/compare/v0.0.6...v0.0.7) (2023-03-29)\n\n### ✨ Features | 新功能\n\n* **build-script, build-script-example, demo-page, engine, layout, material, model, render:** 🎸 external monaco eitor and remove style panel ([494d898](https://github.com/ByteCrazy/chameleon/commit/494d898fd75dabe84d867ff45e84ae63f9b59c5e))\n\n## [0.0.6](https://github.com/ByteCrazy/chameleon/compare/v0.0.5...v0.0.6) (2023-03-28)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.0.5](https://github.com/ByteCrazy/chameleon/compare/v0.0.4...v0.0.5) (2023-03-28)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, layout, material, model, render:** 🎸 complete package info ([86b095d](https://github.com/ByteCrazy/chameleon/commit/86b095da6c955946300591b1775f59c1a141c065))\n\n## [0.0.4](https://github.com/ByteCrazy/chameleon/compare/v0.0.3...v0.0.4) (2023-03-27)\n\n**Note:** Version bump only for package @chamn/layout\n\n## [0.0.2](https://github.com/ByteCrazy/chameleon/compare/v0.0.1...v0.0.2) (2023-03-27)\n\n**Note:** Version bump only for package @chamn/layout\n"
  },
  {
    "path": "packages/layout/LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "packages/layout/__tests__/demo.test.ts",
    "content": "test('adds 1 + 2 to equal 3', () => {\n  expect(3).toBe(3);\n});\n"
  },
  {
    "path": "packages/layout/build.config.js",
    "content": "const mainConfig = {\n  entry: './src/index.tsx',\n  libName: 'CLayout',\n  fileName: 'index',\n  external: ['react', 'react-dom'],\n  global: {\n    react: 'React',\n    'react-dom': 'ReactDOM',\n  },\n  // 额外的 vite 配置\n  vite: {\n    build: {\n      copyPublicDir: false,\n    },\n    plugins: [],\n  },\n};\n\nconst config = mainConfig;\n\nexport default config;\n"
  },
  {
    "path": "packages/layout/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <link rel=\"icon\" type=\"image/svg+xml\" href=\"/vite.svg\" />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n  <title>Layout-Chameleon</title>\n</head>\n\n<body>\n  <div id=\"root\"></div>\n  <script type=\"module\" src=\"/src/_dev_/dev.tsx\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "packages/layout/jest.config.js",
    "content": "/* eslint-disable no-undef */\n/*\n * For a detailed explanation regarding each configuration property, visit:\n * https://jestjs.io/docs/configuration\n */\n\nmodule.exports = {\n  // All imported modules in your tests should be mocked automatically\n  // automock: false,\n\n  // Stop running tests after `n` failures\n  // bail: 0,\n\n  // The directory where Jest should store its cached dependency information\n  // cacheDirectory: \"/private/var/folders/l9/th_r5d_12wxdj16859_mctjw0000gn/T/jest_dx\",\n\n  // Automatically clear mock calls, instances, contexts and results before every test\n  // clearMocks: false,\n\n  // Indicates whether the coverage information should be collected while executing the test\n  // collectCoverage: false,\n\n  // An array of glob patterns indicating a set of files for which coverage information should be collected\n  // collectCoverageFrom: undefined,\n\n  // The directory where Jest should output its coverage files\n  // coverageDirectory: undefined,\n\n  // An array of regexp pattern strings used to skip coverage collection\n  // coveragePathIgnorePatterns: [\n  //   \"/node_modules/\"\n  // ],\n\n  // Indicates which provider should be used to instrument code for coverage\n  // coverageProvider: \"babel\",\n\n  // A list of reporter names that Jest uses when writing coverage reports\n  // coverageReporters: [\n  //   \"json\",\n  //   \"text\",\n  //   \"lcov\",\n  //   \"clover\"\n  // ],\n\n  // An object that configures minimum threshold enforcement for coverage results\n  // coverageThreshold: undefined,\n\n  // A path to a custom dependency extractor\n  // dependencyExtractor: undefined,\n\n  // Make calling deprecated APIs throw helpful error messages\n  // errorOnDeprecated: false,\n\n  // The default configuration for fake timers\n  // fakeTimers: {\n  //   \"enableGlobally\": false\n  // },\n\n  // Force coverage collection from ignored files using an array of glob patterns\n  // forceCoverageMatch: [],\n\n  // A path to a module which exports an async function that is triggered once before all test suites\n  // globalSetup: undefined,\n\n  // A path to a module which exports an async function that is triggered once after all test suites\n  // globalTeardown: undefined,\n\n  // A set of global variables that need to be available in all test environments\n  // globals: {},\n\n  // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.\n  // maxWorkers: \"50%\",\n\n  // An array of directory names to be searched recursively up from the requiring module's location\n  // moduleDirectories: [\n  //   \"node_modules\"\n  // ],\n\n  // An array of file extensions your modules use\n  // moduleFileExtensions: [\n  //   \"js\",\n  //   \"mjs\",\n  //   \"cjs\",\n  //   \"jsx\",\n  //   \"ts\",\n  //   \"tsx\",\n  //   \"json\",\n  //   \"node\"\n  // ],\n\n  // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module\n  // moduleNameMapper: {},\n\n  // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader\n  // modulePathIgnorePatterns: [],\n\n  // Activates notifications for test results\n  // notify: false,\n\n  // An enum that specifies notification mode. Requires { notify: true }\n  // notifyMode: \"failure-change\",\n\n  // A preset that is used as a base for Jest's configuration\n  preset: 'ts-jest',\n\n  // Run tests from one or more projects\n  // projects: undefined,\n\n  // Use this configuration option to add custom reporters to Jest\n  // reporters: undefined,\n\n  // Automatically reset mock state before every test\n  // resetMocks: false,\n\n  // Reset the module registry before running each individual test\n  // resetModules: false,\n\n  // A path to a custom resolver\n  // resolver: undefined,\n\n  // Automatically restore mock state and implementation before every test\n  // restoreMocks: false,\n\n  // The root directory that Jest should scan for tests and modules within\n  // rootDir: undefined,\n\n  // A list of paths to directories that Jest should use to search for files in\n  // roots: [\n  //   \"<rootDir>\"\n  // ],\n\n  // Allows you to use a custom runner instead of Jest's default test runner\n  // runner: \"jest-runner\",\n\n  // The paths to modules that run some code to configure or set up the testing environment before each test\n  // setupFiles: [],\n\n  // A list of paths to modules that run some code to configure or set up the testing framework before each test\n  // setupFilesAfterEnv: [],\n\n  // The number of seconds after which a test is considered as slow and reported as such in the results.\n  // slowTestThreshold: 5,\n\n  // A list of paths to snapshot serializer modules Jest should use for snapshot testing\n  // snapshotSerializers: [],\n\n  // The test environment that will be used for testing\n  testEnvironment: 'jsdom',\n\n  // Options that will be passed to the testEnvironment\n  // testEnvironmentOptions: {},\n\n  // Adds a location field to test results\n  // testLocationInResults: false,\n\n  // The glob patterns Jest uses to detect test files\n  // testMatch: [\n  //   \"**/__tests__/**/*.[jt]s?(x)\",\n  //   \"**/?(*.)+(spec|test).[tj]s?(x)\"\n  // ],\n\n  // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped\n  // testPathIgnorePatterns: [\n  //   \"/node_modules/\"\n  // ],\n\n  // The regexp pattern or array of patterns that Jest uses to detect test files\n  // testRegex: [],\n\n  // This option allows the use of a custom results processor\n  // testResultsProcessor: undefined,\n\n  // This option allows use of a custom test runner\n  // testRunner: \"jest-circus/runner\",\n\n  // A map from regular expressions to paths to transformers\n  // transform: undefined,\n\n  // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation\n  // transformIgnorePatterns: [\n  //   \"/node_modules/\",\n  //   \"\\\\.pnp\\\\.[^\\\\/]+$\"\n  // ],\n\n  // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them\n  // unmockedModulePathPatterns: undefined,\n\n  // Indicates whether each individual test should be reported during the run\n  // verbose: undefined,\n\n  // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode\n  // watchPathIgnorePatterns: [],\n\n  // Whether to use watchman for file crawling\n  // watchman: true,\n};\n"
  },
  {
    "path": "packages/layout/package.json",
    "content": "{\n  \"name\": \"@chamn/layout\",\n  \"version\": \"0.10.4\",\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"files\": [\n    \"dist\"\n  ],\n  \"type\": \"module\",\n  \"main\": \"dist/index.cjs.js\",\n  \"module\": \"dist/index.es.js\",\n  \"types\": \"dist/index.d.ts\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./dist/index.d.ts\",\n      \"module-sync\": \"./dist/index.es.js\",\n      \"import\": \"./dist/index.es.js\",\n      \"require\": \"./dist/index.cjs.js\"\n    },\n    \"./dist/*\": \"./dist/*\"\n  },\n  \"scripts\": {\n    \"start\": \"build-script\",\n    \"build\": \"build-script --build\",\n    \"build:analyze\": \"build-script --build --analyze\",\n    \"build:w\": \"build-script --build --watch\",\n    \"lint\": \"eslint --ext .tsx,.ts src/\",\n    \"prettier\": \"prettier --write ./src\",\n    \"test\": \"jest\"\n  },\n  \"peerDependencies\": {\n    \"react\": \">=16.9.0\",\n    \"react-dom\": \">=16.9.0\"\n  },\n  \"dependencies\": {\n    \"@chamn/model\": \"workspace:*\",\n    \"@chamn/render\": \"workspace:*\",\n    \"lodash-es\": \"^4.17.21\",\n    \"mitt\": \"^3.0.0\",\n    \"re-resizable\": \"^6.9.9\"\n  },\n  \"devDependencies\": {\n    \"@chamn/build-script\": \"workspace:*\",\n    \"@chamn/demo-page\": \"workspace:*\",\n    \"@types/lodash-es\": \"^4.17.6\",\n    \"@types/react\": \"^18.2.0\",\n    \"@types/react-dom\": \"^18.0.6\",\n    \"antd\": \"^5.23.2\",\n    \"react\": \"^18.2.0\",\n    \"react-dom\": \"^18.2.0\",\n    \"sass\": \"^1.54.0\"\n  },\n  \"config\": {},\n  \"gitHead\": \"dc3e55fdeb903a8012f6ebd3ebc018ed61ad89db\"\n}\n"
  },
  {
    "path": "packages/layout/public/render.umd.js",
    "content": "(function (b, D) {\n  typeof exports === 'object' && typeof module < 'u'\n    ? D(exports, require('react'), require('react-dom'))\n    : typeof define === 'function' && define.amd\n    ? define(['exports', 'react', 'react-dom'], D)\n    : ((b = typeof globalThis < 'u' ? globalThis : b || self), D((b.CRender = {}), b.React, b.ReactDOM));\n})(this, function (b, D, ut) {\n  'use strict';\n  var Nv = Object.defineProperty;\n  var $v = (b, D, ut) =>\n    D in b ? Nv(b, D, { enumerable: !0, configurable: !0, writable: !0, value: ut }) : (b[D] = ut);\n  var g = (b, D, ut) => ($v(b, typeof D !== 'symbol' ? D + '' : D, ut), ut);\n  const aa = (t) => (t && typeof t === 'object' && 'default' in t ? t : { default: t });\n  function ia(t) {\n    if (t && t.__esModule) return t;\n    const e = Object.create(null, { [Symbol.toStringTag]: { value: 'Module' } });\n    if (t) {\n      for (const r in t)\n        if (r !== 'default') {\n          const n = Object.getOwnPropertyDescriptor(t, r);\n          Object.defineProperty(e, r, n.get ? n : { enumerable: !0, get: () => t[r] });\n        }\n    }\n    return (e.default = t), Object.freeze(e);\n  }\n  const O = aa(D),\n    sa = ia(ut),\n    ca = (t) => () => {\n      console.warn(`${t} need to be implement getComponent`);\n    },\n    ua = ['customPageRootRender'],\n    Dr = [\n      'pageRender',\n      'render',\n      'convertModelToComponent',\n      'getComponent',\n      'getContext',\n      'getUtils',\n      'getDataLink',\n      'createDataLink',\n      'transformProps',\n      'transformData',\n      'transformGlobalData',\n      'errorCatch',\n      'clear',\n    ],\n    Rr = (t) =>\n      [...Dr, ...ua].reduce((r, n) => {\n        var o;\n        return t != null && t[n] ? (r[n] = (o = t[n]) == null ? void 0 : o.bind(t)) : Dr.includes(n) && (r[n] = ca), r;\n      }, {});\n  var la = Object.defineProperty,\n    fa = (t, e, r) => (e in t ? la(t, e, { enumerable: !0, configurable: !0, writable: !0, value: r }) : (t[e] = r)),\n    j = (t, e, r) => (fa(t, typeof e !== 'symbol' ? e + '' : e, r), r),\n    da = typeof global === 'object' && global && global.Object === Object && global;\n  const Ir = da;\n  var pa = typeof self === 'object' && self && self.Object === Object && self,\n    ha = Ir || pa || Function('return this')();\n  const Y = ha;\n  var va = Y.Symbol;\n  const G = va;\n  var Lr = Object.prototype,\n    ma = Lr.hasOwnProperty,\n    ya = Lr.toString,\n    Bt = G ? G.toStringTag : void 0;\n  function ga(t) {\n    var e = ma.call(t, Bt),\n      r = t[Bt];\n    try {\n      t[Bt] = void 0;\n      var n = !0;\n    } catch {}\n    var o = ya.call(t);\n    return n && (e ? (t[Bt] = r) : delete t[Bt]), o;\n  }\n  var ba = Object.prototype,\n    _a = ba.toString;\n  function ja(t) {\n    return _a.call(t);\n  }\n  var Sa = '[object Null]',\n    Oa = '[object Undefined]',\n    Fr = G ? G.toStringTag : void 0;\n  function lt(t) {\n    return t == null ? (t === void 0 ? Oa : Sa) : Fr && Fr in Object(t) ? ga(t) : ja(t);\n  }\n  function X(t) {\n    return t != null && typeof t === 'object';\n  }\n  var Ca = '[object Symbol]';\n  function Ee(t) {\n    return typeof t === 'symbol' || (X(t) && lt(t) == Ca);\n  }\n  function Ne(t, e) {\n    for (var r = -1, n = t == null ? 0 : t.length, o = Array(n); ++r < n; ) o[r] = e(t[r], r, t);\n    return o;\n  }\n  var wa = Array.isArray;\n  const P = wa;\n  var Ea = 1 / 0,\n    Br = G ? G.prototype : void 0,\n    Ur = Br ? Br.toString : void 0;\n  function kr(t) {\n    if (typeof t === 'string') return t;\n    if (P(t)) return Ne(t, kr) + '';\n    if (Ee(t)) return Ur ? Ur.call(t) : '';\n    var e = t + '';\n    return e == '0' && 1 / t == -Ea ? '-0' : e;\n  }\n  function V(t) {\n    var e = typeof t;\n    return t != null && (e == 'object' || e == 'function');\n  }\n  function $e(t) {\n    return t;\n  }\n  var Na = '[object AsyncFunction]',\n    $a = '[object Function]',\n    Ta = '[object GeneratorFunction]',\n    Aa = '[object Proxy]';\n  function Te(t) {\n    if (!V(t)) return !1;\n    var e = lt(t);\n    return e == $a || e == Ta || e == Na || e == Aa;\n  }\n  var Ma = Y['__core-js_shared__'];\n  const Ae = Ma;\n  var zr = (function () {\n    var t = /[^.]+$/.exec((Ae && Ae.keys && Ae.keys.IE_PROTO) || '');\n    return t ? 'Symbol(src)_1.' + t : '';\n  })();\n  function Pa(t) {\n    return !!zr && zr in t;\n  }\n  var xa = Function.prototype,\n    Da = xa.toString;\n  function ft(t) {\n    if (t != null) {\n      try {\n        return Da.call(t);\n      } catch {}\n      try {\n        return t + '';\n      } catch {}\n    }\n    return '';\n  }\n  var Ra = /[\\\\^$.*+?()[\\]{}|]/g,\n    Ia = /^\\[object .+?Constructor\\]$/,\n    La = Function.prototype,\n    Fa = Object.prototype,\n    Ba = La.toString,\n    Ua = Fa.hasOwnProperty,\n    ka = RegExp(\n      '^' +\n        Ba.call(Ua)\n          .replace(Ra, '\\\\$&')\n          .replace(/hasOwnProperty|(function).*?(?=\\\\\\()| for .+?(?=\\\\\\])/g, '$1.*?') +\n        '$'\n    );\n  function za(t) {\n    if (!V(t) || Pa(t)) return !1;\n    var e = Te(t) ? ka : Ia;\n    return e.test(ft(t));\n  }\n  function Ga(t, e) {\n    return t == null ? void 0 : t[e];\n  }\n  function dt(t, e) {\n    var r = Ga(t, e);\n    return za(r) ? r : void 0;\n  }\n  var Ha = dt(Y, 'WeakMap');\n  const Me = Ha;\n  var Gr = Object.create,\n    Va = (function () {\n      function t() {}\n      return function (e) {\n        if (!V(e)) return {};\n        if (Gr) return Gr(e);\n        t.prototype = e;\n        var r = new t();\n        return (t.prototype = void 0), r;\n      };\n    })();\n  const Wa = Va;\n  function Ja(t, e, r) {\n    switch (r.length) {\n      case 0:\n        return t.call(e);\n      case 1:\n        return t.call(e, r[0]);\n      case 2:\n        return t.call(e, r[0], r[1]);\n      case 3:\n        return t.call(e, r[0], r[1], r[2]);\n    }\n    return t.apply(e, r);\n  }\n  function qa() {}\n  function Hr(t, e) {\n    var r = -1,\n      n = t.length;\n    for (e || (e = Array(n)); ++r < n; ) e[r] = t[r];\n    return e;\n  }\n  var Qa = 800,\n    Ya = 16,\n    Xa = Date.now;\n  function Ka(t) {\n    var e = 0,\n      r = 0;\n    return function () {\n      var n = Xa(),\n        o = Ya - (n - r);\n      if (((r = n), o > 0)) {\n        if (++e >= Qa) return arguments[0];\n      } else e = 0;\n      return t.apply(void 0, arguments);\n    };\n  }\n  function Za(t) {\n    return function () {\n      return t;\n    };\n  }\n  var ti = (function () {\n    try {\n      var t = dt(Object, 'defineProperty');\n      return t({}, '', {}), t;\n    } catch {}\n  })();\n  const ie = ti;\n  var ei = ie\n      ? function (t, e) {\n          return ie(t, 'toString', { configurable: !0, enumerable: !1, value: Za(e), writable: !0 });\n        }\n      : $e,\n    ri = Ka(ei);\n  const Vr = ri;\n  function ni(t, e) {\n    for (var r = -1, n = t == null ? 0 : t.length; ++r < n && e(t[r], r, t) !== !1; );\n    return t;\n  }\n  function oi(t, e, r, n) {\n    for (var o = t.length, a = r + (n ? 1 : -1); n ? a-- : ++a < o; ) if (e(t[a], a, t)) return a;\n    return -1;\n  }\n  function ai(t) {\n    return t !== t;\n  }\n  function ii(t, e, r) {\n    for (var n = r - 1, o = t.length; ++n < o; ) if (t[n] === e) return n;\n    return -1;\n  }\n  function si(t, e, r) {\n    return e === e ? ii(t, e, r) : oi(t, ai, r);\n  }\n  function ci(t, e) {\n    var r = t == null ? 0 : t.length;\n    return !!r && si(t, e, 0) > -1;\n  }\n  var ui = 9007199254740991,\n    li = /^(?:0|[1-9]\\d*)$/;\n  function se(t, e) {\n    var r = typeof t;\n    return (\n      (e = e == null ? ui : e), !!e && (r == 'number' || (r != 'symbol' && li.test(t))) && t > -1 && t % 1 == 0 && t < e\n    );\n  }\n  function Pe(t, e, r) {\n    e == '__proto__' && ie ? ie(t, e, { configurable: !0, enumerable: !0, value: r, writable: !0 }) : (t[e] = r);\n  }\n  function Ut(t, e) {\n    return t === e || (t !== t && e !== e);\n  }\n  var fi = Object.prototype,\n    di = fi.hasOwnProperty;\n  function xe(t, e, r) {\n    var n = t[e];\n    (!(di.call(t, e) && Ut(n, r)) || (r === void 0 && !(e in t))) && Pe(t, e, r);\n  }\n  function bt(t, e, r, n) {\n    var o = !r;\n    r || (r = {});\n    for (var a = -1, s = e.length; ++a < s; ) {\n      var i = e[a],\n        c = n ? n(r[i], t[i], i, r, t) : void 0;\n      c === void 0 && (c = t[i]), o ? Pe(r, i, c) : xe(r, i, c);\n    }\n    return r;\n  }\n  var Wr = Math.max;\n  function Jr(t, e, r) {\n    return (\n      (e = Wr(e === void 0 ? t.length - 1 : e, 0)),\n      function () {\n        for (var n = arguments, o = -1, a = Wr(n.length - e, 0), s = Array(a); ++o < a; ) s[o] = n[e + o];\n        o = -1;\n        for (var i = Array(e + 1); ++o < e; ) i[o] = n[o];\n        return (i[e] = r(s)), Ja(t, this, i);\n      }\n    );\n  }\n  function qr(t, e) {\n    return Vr(Jr(t, e, $e), t + '');\n  }\n  var pi = 9007199254740991;\n  function De(t) {\n    return typeof t === 'number' && t > -1 && t % 1 == 0 && t <= pi;\n  }\n  function ce(t) {\n    return t != null && De(t.length) && !Te(t);\n  }\n  function hi(t, e, r) {\n    if (!V(r)) return !1;\n    var n = typeof e;\n    return (n == 'number' ? ce(r) && se(e, r.length) : n == 'string' && e in r) ? Ut(r[e], t) : !1;\n  }\n  function vi(t) {\n    return qr(function (e, r) {\n      var n = -1,\n        o = r.length,\n        a = o > 1 ? r[o - 1] : void 0,\n        s = o > 2 ? r[2] : void 0;\n      for (\n        a = t.length > 3 && typeof a === 'function' ? (o--, a) : void 0,\n          s && hi(r[0], r[1], s) && ((a = o < 3 ? void 0 : a), (o = 1)),\n          e = Object(e);\n        ++n < o;\n\n      ) {\n        var i = r[n];\n        i && t(e, i, n, a);\n      }\n      return e;\n    });\n  }\n  var mi = Object.prototype;\n  function Re(t) {\n    var e = t && t.constructor,\n      r = (typeof e === 'function' && e.prototype) || mi;\n    return t === r;\n  }\n  function yi(t, e) {\n    for (var r = -1, n = Array(t); ++r < t; ) n[r] = e(r);\n    return n;\n  }\n  var gi = '[object Arguments]';\n  function Qr(t) {\n    return X(t) && lt(t) == gi;\n  }\n  var Yr = Object.prototype,\n    bi = Yr.hasOwnProperty,\n    _i = Yr.propertyIsEnumerable,\n    ji = Qr(\n      (function () {\n        return arguments;\n      })()\n    )\n      ? Qr\n      : function (t) {\n          return X(t) && bi.call(t, 'callee') && !_i.call(t, 'callee');\n        };\n  const kt = ji;\n  function Si() {\n    return !1;\n  }\n  var Xr = typeof b === 'object' && b && !b.nodeType && b,\n    Kr = Xr && typeof module === 'object' && module && !module.nodeType && module,\n    Oi = Kr && Kr.exports === Xr,\n    Zr = Oi ? Y.Buffer : void 0,\n    Ci = Zr ? Zr.isBuffer : void 0,\n    wi = Ci || Si;\n  const zt = wi;\n  var Ei = '[object Arguments]',\n    Ni = '[object Array]',\n    $i = '[object Boolean]',\n    Ti = '[object Date]',\n    Ai = '[object Error]',\n    Mi = '[object Function]',\n    Pi = '[object Map]',\n    xi = '[object Number]',\n    Di = '[object Object]',\n    Ri = '[object RegExp]',\n    Ii = '[object Set]',\n    Li = '[object String]',\n    Fi = '[object WeakMap]',\n    Bi = '[object ArrayBuffer]',\n    Ui = '[object DataView]',\n    ki = '[object Float32Array]',\n    zi = '[object Float64Array]',\n    Gi = '[object Int8Array]',\n    Hi = '[object Int16Array]',\n    Vi = '[object Int32Array]',\n    Wi = '[object Uint8Array]',\n    Ji = '[object Uint8ClampedArray]',\n    qi = '[object Uint16Array]',\n    Qi = '[object Uint32Array]',\n    $ = {};\n  ($[ki] = $[zi] = $[Gi] = $[Hi] = $[Vi] = $[Wi] = $[Ji] = $[qi] = $[Qi] = !0),\n    ($[Ei] =\n      $[Ni] =\n      $[Bi] =\n      $[$i] =\n      $[Ui] =\n      $[Ti] =\n      $[Ai] =\n      $[Mi] =\n      $[Pi] =\n      $[xi] =\n      $[Di] =\n      $[Ri] =\n      $[Ii] =\n      $[Li] =\n      $[Fi] =\n        !1);\n  function Yi(t) {\n    return X(t) && De(t.length) && !!$[lt(t)];\n  }\n  function Ie(t) {\n    return function (e) {\n      return t(e);\n    };\n  }\n  var tn = typeof b === 'object' && b && !b.nodeType && b,\n    Gt = tn && typeof module === 'object' && module && !module.nodeType && module,\n    Xi = Gt && Gt.exports === tn,\n    Le = Xi && Ir.process,\n    Ki = (function () {\n      try {\n        var t = Gt && Gt.require && Gt.require('util').types;\n        return t || (Le && Le.binding && Le.binding('util'));\n      } catch {}\n    })();\n  const _t = Ki;\n  var en = _t && _t.isTypedArray,\n    Zi = en ? Ie(en) : Yi;\n  const Fe = Zi;\n  var ts = Object.prototype,\n    es = ts.hasOwnProperty;\n  function rn(t, e) {\n    var r = P(t),\n      n = !r && kt(t),\n      o = !r && !n && zt(t),\n      a = !r && !n && !o && Fe(t),\n      s = r || n || o || a,\n      i = s ? yi(t.length, String) : [],\n      c = i.length;\n    for (var u in t)\n      (e || es.call(t, u)) &&\n        !(\n          s &&\n          (u == 'length' ||\n            (o && (u == 'offset' || u == 'parent')) ||\n            (a && (u == 'buffer' || u == 'byteLength' || u == 'byteOffset')) ||\n            se(u, c))\n        ) &&\n        i.push(u);\n    return i;\n  }\n  function nn(t, e) {\n    return function (r) {\n      return t(e(r));\n    };\n  }\n  var rs = nn(Object.keys, Object);\n  const ns = rs;\n  var os = Object.prototype,\n    as = os.hasOwnProperty;\n  function is(t) {\n    if (!Re(t)) return ns(t);\n    var e = [];\n    for (var r in Object(t)) as.call(t, r) && r != 'constructor' && e.push(r);\n    return e;\n  }\n  function ue(t) {\n    return ce(t) ? rn(t) : is(t);\n  }\n  function ss(t) {\n    var e = [];\n    if (t != null) for (var r in Object(t)) e.push(r);\n    return e;\n  }\n  var cs = Object.prototype,\n    us = cs.hasOwnProperty;\n  function ls(t) {\n    if (!V(t)) return ss(t);\n    var e = Re(t),\n      r = [];\n    for (var n in t) (n == 'constructor' && (e || !us.call(t, n))) || r.push(n);\n    return r;\n  }\n  function Ht(t) {\n    return ce(t) ? rn(t, !0) : ls(t);\n  }\n  var fs = /\\.|\\[(?:[^[\\]]*|([\"'])(?:(?!\\1)[^\\\\]|\\\\.)*?\\1)\\]/,\n    ds = /^\\w*$/;\n  function Be(t, e) {\n    if (P(t)) return !1;\n    var r = typeof t;\n    return r == 'number' || r == 'symbol' || r == 'boolean' || t == null || Ee(t)\n      ? !0\n      : ds.test(t) || !fs.test(t) || (e != null && t in Object(e));\n  }\n  var ps = dt(Object, 'create');\n  const Vt = ps;\n  function hs() {\n    (this.__data__ = Vt ? Vt(null) : {}), (this.size = 0);\n  }\n  function vs(t) {\n    var e = this.has(t) && delete this.__data__[t];\n    return (this.size -= e ? 1 : 0), e;\n  }\n  var ms = '__lodash_hash_undefined__',\n    ys = Object.prototype,\n    gs = ys.hasOwnProperty;\n  function bs(t) {\n    var e = this.__data__;\n    if (Vt) {\n      var r = e[t];\n      return r === ms ? void 0 : r;\n    }\n    return gs.call(e, t) ? e[t] : void 0;\n  }\n  var _s = Object.prototype,\n    js = _s.hasOwnProperty;\n  function Ss(t) {\n    var e = this.__data__;\n    return Vt ? e[t] !== void 0 : js.call(e, t);\n  }\n  var Os = '__lodash_hash_undefined__';\n  function Cs(t, e) {\n    var r = this.__data__;\n    return (this.size += this.has(t) ? 0 : 1), (r[t] = Vt && e === void 0 ? Os : e), this;\n  }\n  function pt(t) {\n    var e = -1,\n      r = t == null ? 0 : t.length;\n    for (this.clear(); ++e < r; ) {\n      var n = t[e];\n      this.set(n[0], n[1]);\n    }\n  }\n  (pt.prototype.clear = hs),\n    (pt.prototype.delete = vs),\n    (pt.prototype.get = bs),\n    (pt.prototype.has = Ss),\n    (pt.prototype.set = Cs);\n  function ws() {\n    (this.__data__ = []), (this.size = 0);\n  }\n  function le(t, e) {\n    for (var r = t.length; r--; ) if (Ut(t[r][0], e)) return r;\n    return -1;\n  }\n  var Es = Array.prototype,\n    Ns = Es.splice;\n  function $s(t) {\n    var e = this.__data__,\n      r = le(e, t);\n    if (r < 0) return !1;\n    var n = e.length - 1;\n    return r == n ? e.pop() : Ns.call(e, r, 1), --this.size, !0;\n  }\n  function Ts(t) {\n    var e = this.__data__,\n      r = le(e, t);\n    return r < 0 ? void 0 : e[r][1];\n  }\n  function As(t) {\n    return le(this.__data__, t) > -1;\n  }\n  function Ms(t, e) {\n    var r = this.__data__,\n      n = le(r, t);\n    return n < 0 ? (++this.size, r.push([t, e])) : (r[n][1] = e), this;\n  }\n  function et(t) {\n    var e = -1,\n      r = t == null ? 0 : t.length;\n    for (this.clear(); ++e < r; ) {\n      var n = t[e];\n      this.set(n[0], n[1]);\n    }\n  }\n  (et.prototype.clear = ws),\n    (et.prototype.delete = $s),\n    (et.prototype.get = Ts),\n    (et.prototype.has = As),\n    (et.prototype.set = Ms);\n  var Ps = dt(Y, 'Map');\n  const Wt = Ps;\n  function xs() {\n    (this.size = 0), (this.__data__ = { hash: new pt(), map: new (Wt || et)(), string: new pt() });\n  }\n  function Ds(t) {\n    var e = typeof t;\n    return e == 'string' || e == 'number' || e == 'symbol' || e == 'boolean' ? t !== '__proto__' : t === null;\n  }\n  function fe(t, e) {\n    var r = t.__data__;\n    return Ds(e) ? r[typeof e === 'string' ? 'string' : 'hash'] : r.map;\n  }\n  function Rs(t) {\n    var e = fe(this, t).delete(t);\n    return (this.size -= e ? 1 : 0), e;\n  }\n  function Is(t) {\n    return fe(this, t).get(t);\n  }\n  function Ls(t) {\n    return fe(this, t).has(t);\n  }\n  function Fs(t, e) {\n    var r = fe(this, t),\n      n = r.size;\n    return r.set(t, e), (this.size += r.size == n ? 0 : 1), this;\n  }\n  function rt(t) {\n    var e = -1,\n      r = t == null ? 0 : t.length;\n    for (this.clear(); ++e < r; ) {\n      var n = t[e];\n      this.set(n[0], n[1]);\n    }\n  }\n  (rt.prototype.clear = xs),\n    (rt.prototype.delete = Rs),\n    (rt.prototype.get = Is),\n    (rt.prototype.has = Ls),\n    (rt.prototype.set = Fs);\n  var Bs = 'Expected a function';\n  function Ue(t, e) {\n    if (typeof t !== 'function' || (e != null && typeof e !== 'function')) throw new TypeError(Bs);\n    var r = function () {\n      var n = arguments,\n        o = e ? e.apply(this, n) : n[0],\n        a = r.cache;\n      if (a.has(o)) return a.get(o);\n      var s = t.apply(this, n);\n      return (r.cache = a.set(o, s) || a), s;\n    };\n    return (r.cache = new (Ue.Cache || rt)()), r;\n  }\n  Ue.Cache = rt;\n  var Us = 500;\n  function ks(t) {\n    var e = Ue(t, function (n) {\n        return r.size === Us && r.clear(), n;\n      }),\n      r = e.cache;\n    return e;\n  }\n  var zs = /[^.[\\]]+|\\[(?:(-?\\d+(?:\\.\\d+)?)|([\"'])((?:(?!\\2)[^\\\\]|\\\\.)*?)\\2)\\]|(?=(?:\\.|\\[\\])(?:\\.|\\[\\]|$))/g,\n    Gs = /\\\\(\\\\)?/g,\n    Hs = ks(function (t) {\n      var e = [];\n      return (\n        t.charCodeAt(0) === 46 && e.push(''),\n        t.replace(zs, function (r, n, o, a) {\n          e.push(o ? a.replace(Gs, '$1') : n || r);\n        }),\n        e\n      );\n    });\n  const Vs = Hs;\n  function Ws(t) {\n    return t == null ? '' : kr(t);\n  }\n  function jt(t, e) {\n    return P(t) ? t : Be(t, e) ? [t] : Vs(Ws(t));\n  }\n  var Js = 1 / 0;\n  function St(t) {\n    if (typeof t === 'string' || Ee(t)) return t;\n    var e = t + '';\n    return e == '0' && 1 / t == -Js ? '-0' : e;\n  }\n  function de(t, e) {\n    e = jt(e, t);\n    for (var r = 0, n = e.length; t != null && r < n; ) t = t[St(e[r++])];\n    return r && r == n ? t : void 0;\n  }\n  function on(t, e, r) {\n    var n = t == null ? void 0 : de(t, e);\n    return n === void 0 ? r : n;\n  }\n  function ke(t, e) {\n    for (var r = -1, n = e.length, o = t.length; ++r < n; ) t[o + r] = e[r];\n    return t;\n  }\n  var an = G ? G.isConcatSpreadable : void 0;\n  function qs(t) {\n    return P(t) || kt(t) || !!(an && t && t[an]);\n  }\n  function ze(t, e, r, n, o) {\n    var a = -1,\n      s = t.length;\n    for (r || (r = qs), o || (o = []); ++a < s; ) {\n      var i = t[a];\n      e > 0 && r(i) ? (e > 1 ? ze(i, e - 1, r, n, o) : ke(o, i)) : n || (o[o.length] = i);\n    }\n    return o;\n  }\n  function Qs(t) {\n    var e = t == null ? 0 : t.length;\n    return e ? ze(t, 1) : [];\n  }\n  function Ys(t) {\n    return Vr(Jr(t, void 0, Qs), t + '');\n  }\n  var Xs = nn(Object.getPrototypeOf, Object);\n  const Ge = Xs;\n  var Ks = '[object Object]',\n    Zs = Function.prototype,\n    tc = Object.prototype,\n    sn = Zs.toString,\n    ec = tc.hasOwnProperty,\n    rc = sn.call(Object);\n  function U(t) {\n    if (!X(t) || lt(t) != Ks) return !1;\n    var e = Ge(t);\n    if (e === null) return !0;\n    var r = ec.call(e, 'constructor') && e.constructor;\n    return typeof r === 'function' && r instanceof r && sn.call(r) == rc;\n  }\n  function nc(t, e, r) {\n    var n = -1,\n      o = t.length;\n    e < 0 && (e = -e > o ? 0 : o + e),\n      (r = r > o ? o : r),\n      r < 0 && (r += o),\n      (o = e > r ? 0 : (r - e) >>> 0),\n      (e >>>= 0);\n    for (var a = Array(o); ++n < o; ) a[n] = t[n + e];\n    return a;\n  }\n  function oc() {\n    (this.__data__ = new et()), (this.size = 0);\n  }\n  function ac(t) {\n    var e = this.__data__,\n      r = e.delete(t);\n    return (this.size = e.size), r;\n  }\n  function ic(t) {\n    return this.__data__.get(t);\n  }\n  function sc(t) {\n    return this.__data__.has(t);\n  }\n  var cc = 200;\n  function uc(t, e) {\n    var r = this.__data__;\n    if (r instanceof et) {\n      var n = r.__data__;\n      if (!Wt || n.length < cc - 1) return n.push([t, e]), (this.size = ++r.size), this;\n      r = this.__data__ = new rt(n);\n    }\n    return r.set(t, e), (this.size = r.size), this;\n  }\n  function W(t) {\n    var e = (this.__data__ = new et(t));\n    this.size = e.size;\n  }\n  (W.prototype.clear = oc),\n    (W.prototype.delete = ac),\n    (W.prototype.get = ic),\n    (W.prototype.has = sc),\n    (W.prototype.set = uc);\n  function lc(t, e) {\n    return t && bt(e, ue(e), t);\n  }\n  function fc(t, e) {\n    return t && bt(e, Ht(e), t);\n  }\n  var cn = typeof b === 'object' && b && !b.nodeType && b,\n    un = cn && typeof module === 'object' && module && !module.nodeType && module,\n    dc = un && un.exports === cn,\n    ln = dc ? Y.Buffer : void 0,\n    fn = ln ? ln.allocUnsafe : void 0;\n  function dn(t, e) {\n    if (e) return t.slice();\n    var r = t.length,\n      n = fn ? fn(r) : new t.constructor(r);\n    return t.copy(n), n;\n  }\n  function pc(t, e) {\n    for (var r = -1, n = t == null ? 0 : t.length, o = 0, a = []; ++r < n; ) {\n      var s = t[r];\n      e(s, r, t) && (a[o++] = s);\n    }\n    return a;\n  }\n  function pn() {\n    return [];\n  }\n  var hc = Object.prototype,\n    vc = hc.propertyIsEnumerable,\n    hn = Object.getOwnPropertySymbols,\n    mc = hn\n      ? function (t) {\n          return t == null\n            ? []\n            : ((t = Object(t)),\n              pc(hn(t), function (e) {\n                return vc.call(t, e);\n              }));\n        }\n      : pn;\n  const He = mc;\n  function yc(t, e) {\n    return bt(t, He(t), e);\n  }\n  var gc = Object.getOwnPropertySymbols,\n    bc = gc\n      ? function (t) {\n          for (var e = []; t; ) ke(e, He(t)), (t = Ge(t));\n          return e;\n        }\n      : pn;\n  const vn = bc;\n  function _c(t, e) {\n    return bt(t, vn(t), e);\n  }\n  function mn(t, e, r) {\n    var n = e(t);\n    return P(t) ? n : ke(n, r(t));\n  }\n  function Ve(t) {\n    return mn(t, ue, He);\n  }\n  function We(t) {\n    return mn(t, Ht, vn);\n  }\n  var jc = dt(Y, 'DataView');\n  const Je = jc;\n  var Sc = dt(Y, 'Promise');\n  const qe = Sc;\n  var Oc = dt(Y, 'Set');\n  const Ot = Oc;\n  var yn = '[object Map]',\n    Cc = '[object Object]',\n    gn = '[object Promise]',\n    bn = '[object Set]',\n    _n = '[object WeakMap]',\n    jn = '[object DataView]',\n    wc = ft(Je),\n    Ec = ft(Wt),\n    Nc = ft(qe),\n    $c = ft(Ot),\n    Tc = ft(Me),\n    ht = lt;\n  ((Je && ht(new Je(new ArrayBuffer(1))) != jn) ||\n    (Wt && ht(new Wt()) != yn) ||\n    (qe && ht(qe.resolve()) != gn) ||\n    (Ot && ht(new Ot()) != bn) ||\n    (Me && ht(new Me()) != _n)) &&\n    (ht = function (t) {\n      var e = lt(t),\n        r = e == Cc ? t.constructor : void 0,\n        n = r ? ft(r) : '';\n      if (n)\n        switch (n) {\n          case wc:\n            return jn;\n          case Ec:\n            return yn;\n          case Nc:\n            return gn;\n          case $c:\n            return bn;\n          case Tc:\n            return _n;\n        }\n      return e;\n    });\n  const Jt = ht;\n  var Ac = Object.prototype,\n    Mc = Ac.hasOwnProperty;\n  function Pc(t) {\n    var e = t.length,\n      r = new t.constructor(e);\n    return e && typeof t[0] === 'string' && Mc.call(t, 'index') && ((r.index = t.index), (r.input = t.input)), r;\n  }\n  var xc = Y.Uint8Array;\n  const pe = xc;\n  function Qe(t) {\n    var e = new t.constructor(t.byteLength);\n    return new pe(e).set(new pe(t)), e;\n  }\n  function Dc(t, e) {\n    var r = e ? Qe(t.buffer) : t.buffer;\n    return new t.constructor(r, t.byteOffset, t.byteLength);\n  }\n  var Rc = /\\w*$/;\n  function Ic(t) {\n    var e = new t.constructor(t.source, Rc.exec(t));\n    return (e.lastIndex = t.lastIndex), e;\n  }\n  var Sn = G ? G.prototype : void 0,\n    On = Sn ? Sn.valueOf : void 0;\n  function Lc(t) {\n    return On ? Object(On.call(t)) : {};\n  }\n  function Cn(t, e) {\n    var r = e ? Qe(t.buffer) : t.buffer;\n    return new t.constructor(r, t.byteOffset, t.length);\n  }\n  var Fc = '[object Boolean]',\n    Bc = '[object Date]',\n    Uc = '[object Map]',\n    kc = '[object Number]',\n    zc = '[object RegExp]',\n    Gc = '[object Set]',\n    Hc = '[object String]',\n    Vc = '[object Symbol]',\n    Wc = '[object ArrayBuffer]',\n    Jc = '[object DataView]',\n    qc = '[object Float32Array]',\n    Qc = '[object Float64Array]',\n    Yc = '[object Int8Array]',\n    Xc = '[object Int16Array]',\n    Kc = '[object Int32Array]',\n    Zc = '[object Uint8Array]',\n    tu = '[object Uint8ClampedArray]',\n    eu = '[object Uint16Array]',\n    ru = '[object Uint32Array]';\n  function nu(t, e, r) {\n    var n = t.constructor;\n    switch (e) {\n      case Wc:\n        return Qe(t);\n      case Fc:\n      case Bc:\n        return new n(+t);\n      case Jc:\n        return Dc(t, r);\n      case qc:\n      case Qc:\n      case Yc:\n      case Xc:\n      case Kc:\n      case Zc:\n      case tu:\n      case eu:\n      case ru:\n        return Cn(t, r);\n      case Uc:\n        return new n();\n      case kc:\n      case Hc:\n        return new n(t);\n      case zc:\n        return Ic(t);\n      case Gc:\n        return new n();\n      case Vc:\n        return Lc(t);\n    }\n  }\n  function wn(t) {\n    return typeof t.constructor === 'function' && !Re(t) ? Wa(Ge(t)) : {};\n  }\n  var ou = '[object Map]';\n  function au(t) {\n    return X(t) && Jt(t) == ou;\n  }\n  var En = _t && _t.isMap,\n    iu = En ? Ie(En) : au;\n  const su = iu;\n  var cu = '[object Set]';\n  function uu(t) {\n    return X(t) && Jt(t) == cu;\n  }\n  var Nn = _t && _t.isSet,\n    lu = Nn ? Ie(Nn) : uu;\n  const fu = lu;\n  var du = 1,\n    pu = 2,\n    hu = 4,\n    $n = '[object Arguments]',\n    vu = '[object Array]',\n    mu = '[object Boolean]',\n    yu = '[object Date]',\n    gu = '[object Error]',\n    Tn = '[object Function]',\n    bu = '[object GeneratorFunction]',\n    _u = '[object Map]',\n    ju = '[object Number]',\n    An = '[object Object]',\n    Su = '[object RegExp]',\n    Ou = '[object Set]',\n    Cu = '[object String]',\n    wu = '[object Symbol]',\n    Eu = '[object WeakMap]',\n    Nu = '[object ArrayBuffer]',\n    $u = '[object DataView]',\n    Tu = '[object Float32Array]',\n    Au = '[object Float64Array]',\n    Mu = '[object Int8Array]',\n    Pu = '[object Int16Array]',\n    xu = '[object Int32Array]',\n    Du = '[object Uint8Array]',\n    Ru = '[object Uint8ClampedArray]',\n    Iu = '[object Uint16Array]',\n    Lu = '[object Uint32Array]',\n    E = {};\n  (E[$n] =\n    E[vu] =\n    E[Nu] =\n    E[$u] =\n    E[mu] =\n    E[yu] =\n    E[Tu] =\n    E[Au] =\n    E[Mu] =\n    E[Pu] =\n    E[xu] =\n    E[_u] =\n    E[ju] =\n    E[An] =\n    E[Su] =\n    E[Ou] =\n    E[Cu] =\n    E[wu] =\n    E[Du] =\n    E[Ru] =\n    E[Iu] =\n    E[Lu] =\n      !0),\n    (E[gu] = E[Tn] = E[Eu] = !1);\n  function qt(t, e, r, n, o, a) {\n    var s,\n      i = e & du,\n      c = e & pu,\n      u = e & hu;\n    if ((r && (s = o ? r(t, n, o, a) : r(t)), s !== void 0)) return s;\n    if (!V(t)) return t;\n    var l = P(t);\n    if (l) {\n      if (((s = Pc(t)), !i)) return Hr(t, s);\n    } else {\n      var d = Jt(t),\n        f = d == Tn || d == bu;\n      if (zt(t)) return dn(t, i);\n      if (d == An || d == $n || (f && !o)) {\n        if (((s = c || f ? {} : wn(t)), !i)) return c ? _c(t, fc(s, t)) : yc(t, lc(s, t));\n      } else {\n        if (!E[d]) return o ? t : {};\n        s = nu(t, d, i);\n      }\n    }\n    a || (a = new W());\n    var p = a.get(t);\n    if (p) return p;\n    a.set(t, s),\n      fu(t)\n        ? t.forEach(function (_) {\n            s.add(qt(_, e, r, _, t, a));\n          })\n        : su(t) &&\n          t.forEach(function (_, S) {\n            s.set(S, qt(_, e, r, S, t, a));\n          });\n    var h = u ? (c ? We : Ve) : c ? Ht : ue,\n      y = l ? void 0 : h(t);\n    return (\n      ni(y || t, function (_, S) {\n        y && ((S = _), (_ = t[S])), xe(s, S, qt(_, e, r, S, t, a));\n      }),\n      s\n    );\n  }\n  var Fu = 1,\n    Bu = 4;\n  function Mn(t) {\n    return qt(t, Fu | Bu);\n  }\n  var Uu = '__lodash_hash_undefined__';\n  function ku(t) {\n    return this.__data__.set(t, Uu), this;\n  }\n  function zu(t) {\n    return this.__data__.has(t);\n  }\n  function Qt(t) {\n    var e = -1,\n      r = t == null ? 0 : t.length;\n    for (this.__data__ = new rt(); ++e < r; ) this.add(t[e]);\n  }\n  (Qt.prototype.add = Qt.prototype.push = ku), (Qt.prototype.has = zu);\n  function Gu(t, e) {\n    for (var r = -1, n = t == null ? 0 : t.length; ++r < n; ) if (e(t[r], r, t)) return !0;\n    return !1;\n  }\n  function Pn(t, e) {\n    return t.has(e);\n  }\n  var Hu = 1,\n    Vu = 2;\n  function xn(t, e, r, n, o, a) {\n    var s = r & Hu,\n      i = t.length,\n      c = e.length;\n    if (i != c && !(s && c > i)) return !1;\n    var u = a.get(t),\n      l = a.get(e);\n    if (u && l) return u == e && l == t;\n    var d = -1,\n      f = !0,\n      p = r & Vu ? new Qt() : void 0;\n    for (a.set(t, e), a.set(e, t); ++d < i; ) {\n      var h = t[d],\n        y = e[d];\n      if (n) var _ = s ? n(y, h, d, e, t, a) : n(h, y, d, t, e, a);\n      if (_ !== void 0) {\n        if (_) continue;\n        f = !1;\n        break;\n      }\n      if (p) {\n        if (\n          !Gu(e, function (S, w) {\n            if (!Pn(p, w) && (h === S || o(h, S, r, n, a))) return p.push(w);\n          })\n        ) {\n          f = !1;\n          break;\n        }\n      } else if (!(h === y || o(h, y, r, n, a))) {\n        f = !1;\n        break;\n      }\n    }\n    return a.delete(t), a.delete(e), f;\n  }\n  function Wu(t) {\n    var e = -1,\n      r = Array(t.size);\n    return (\n      t.forEach(function (n, o) {\n        r[++e] = [o, n];\n      }),\n      r\n    );\n  }\n  function Ye(t) {\n    var e = -1,\n      r = Array(t.size);\n    return (\n      t.forEach(function (n) {\n        r[++e] = n;\n      }),\n      r\n    );\n  }\n  var Ju = 1,\n    qu = 2,\n    Qu = '[object Boolean]',\n    Yu = '[object Date]',\n    Xu = '[object Error]',\n    Ku = '[object Map]',\n    Zu = '[object Number]',\n    tl = '[object RegExp]',\n    el = '[object Set]',\n    rl = '[object String]',\n    nl = '[object Symbol]',\n    ol = '[object ArrayBuffer]',\n    al = '[object DataView]',\n    Dn = G ? G.prototype : void 0,\n    Xe = Dn ? Dn.valueOf : void 0;\n  function il(t, e, r, n, o, a, s) {\n    switch (r) {\n      case al:\n        if (t.byteLength != e.byteLength || t.byteOffset != e.byteOffset) return !1;\n        (t = t.buffer), (e = e.buffer);\n      case ol:\n        return !(t.byteLength != e.byteLength || !a(new pe(t), new pe(e)));\n      case Qu:\n      case Yu:\n      case Zu:\n        return Ut(+t, +e);\n      case Xu:\n        return t.name == e.name && t.message == e.message;\n      case tl:\n      case rl:\n        return t == e + '';\n      case Ku:\n        var i = Wu;\n      case el:\n        var c = n & Ju;\n        if ((i || (i = Ye), t.size != e.size && !c)) return !1;\n        var u = s.get(t);\n        if (u) return u == e;\n        (n |= qu), s.set(t, e);\n        var l = xn(i(t), i(e), n, o, a, s);\n        return s.delete(t), l;\n      case nl:\n        if (Xe) return Xe.call(t) == Xe.call(e);\n    }\n    return !1;\n  }\n  var sl = 1,\n    cl = Object.prototype,\n    ul = cl.hasOwnProperty;\n  function ll(t, e, r, n, o, a) {\n    var s = r & sl,\n      i = Ve(t),\n      c = i.length,\n      u = Ve(e),\n      l = u.length;\n    if (c != l && !s) return !1;\n    for (var d = c; d--; ) {\n      var f = i[d];\n      if (!(s ? f in e : ul.call(e, f))) return !1;\n    }\n    var p = a.get(t),\n      h = a.get(e);\n    if (p && h) return p == e && h == t;\n    var y = !0;\n    a.set(t, e), a.set(e, t);\n    for (var _ = s; ++d < c; ) {\n      f = i[d];\n      var S = t[f],\n        w = e[f];\n      if (n) var I = s ? n(w, S, f, e, t, a) : n(S, w, f, t, e, a);\n      if (!(I === void 0 ? S === w || o(S, w, r, n, a) : I)) {\n        y = !1;\n        break;\n      }\n      _ || (_ = f == 'constructor');\n    }\n    if (y && !_) {\n      var M = t.constructor,\n        L = e.constructor;\n      M != L &&\n        'constructor' in t &&\n        'constructor' in e &&\n        !(typeof M === 'function' && M instanceof M && typeof L === 'function' && L instanceof L) &&\n        (y = !1);\n    }\n    return a.delete(t), a.delete(e), y;\n  }\n  var fl = 1,\n    Rn = '[object Arguments]',\n    In = '[object Array]',\n    he = '[object Object]',\n    dl = Object.prototype,\n    Ln = dl.hasOwnProperty;\n  function pl(t, e, r, n, o, a) {\n    var s = P(t),\n      i = P(e),\n      c = s ? In : Jt(t),\n      u = i ? In : Jt(e);\n    (c = c == Rn ? he : c), (u = u == Rn ? he : u);\n    var l = c == he,\n      d = u == he,\n      f = c == u;\n    if (f && zt(t)) {\n      if (!zt(e)) return !1;\n      (s = !0), (l = !1);\n    }\n    if (f && !l) return a || (a = new W()), s || Fe(t) ? xn(t, e, r, n, o, a) : il(t, e, c, r, n, o, a);\n    if (!(r & fl)) {\n      var p = l && Ln.call(t, '__wrapped__'),\n        h = d && Ln.call(e, '__wrapped__');\n      if (p || h) {\n        var y = p ? t.value() : t,\n          _ = h ? e.value() : e;\n        return a || (a = new W()), o(y, _, r, n, a);\n      }\n    }\n    return f ? (a || (a = new W()), ll(t, e, r, n, o, a)) : !1;\n  }\n  function Ke(t, e, r, n, o) {\n    return t === e ? !0 : t == null || e == null || (!X(t) && !X(e)) ? t !== t && e !== e : pl(t, e, r, n, Ke, o);\n  }\n  var hl = 1,\n    vl = 2;\n  function ml(t, e, r, n) {\n    var o = r.length,\n      a = o,\n      s = !n;\n    if (t == null) return !a;\n    for (t = Object(t); o--; ) {\n      var i = r[o];\n      if (s && i[2] ? i[1] !== t[i[0]] : !(i[0] in t)) return !1;\n    }\n    for (; ++o < a; ) {\n      i = r[o];\n      var c = i[0],\n        u = t[c],\n        l = i[1];\n      if (s && i[2]) {\n        if (u === void 0 && !(c in t)) return !1;\n      } else {\n        var d = new W();\n        if (n) var f = n(u, l, c, t, e, d);\n        if (!(f === void 0 ? Ke(l, u, hl | vl, n, d) : f)) return !1;\n      }\n    }\n    return !0;\n  }\n  function Fn(t) {\n    return t === t && !V(t);\n  }\n  function yl(t) {\n    for (var e = ue(t), r = e.length; r--; ) {\n      var n = e[r],\n        o = t[n];\n      e[r] = [n, o, Fn(o)];\n    }\n    return e;\n  }\n  function Bn(t, e) {\n    return function (r) {\n      return r == null ? !1 : r[t] === e && (e !== void 0 || t in Object(r));\n    };\n  }\n  function gl(t) {\n    var e = yl(t);\n    return e.length == 1 && e[0][2]\n      ? Bn(e[0][0], e[0][1])\n      : function (r) {\n          return r === t || ml(r, t, e);\n        };\n  }\n  function bl(t, e) {\n    return t != null && e in Object(t);\n  }\n  function _l(t, e, r) {\n    e = jt(e, t);\n    for (var n = -1, o = e.length, a = !1; ++n < o; ) {\n      var s = St(e[n]);\n      if (!(a = t != null && r(t, s))) break;\n      t = t[s];\n    }\n    return a || ++n != o ? a : ((o = t == null ? 0 : t.length), !!o && De(o) && se(s, o) && (P(t) || kt(t)));\n  }\n  function jl(t, e) {\n    return t != null && _l(t, e, bl);\n  }\n  var Sl = 1,\n    Ol = 2;\n  function Cl(t, e) {\n    return Be(t) && Fn(e)\n      ? Bn(St(t), e)\n      : function (r) {\n          var n = on(r, t);\n          return n === void 0 && n === e ? jl(r, t) : Ke(e, n, Sl | Ol);\n        };\n  }\n  function wl(t) {\n    return function (e) {\n      return e == null ? void 0 : e[t];\n    };\n  }\n  function El(t) {\n    return function (e) {\n      return de(e, t);\n    };\n  }\n  function Nl(t) {\n    return Be(t) ? wl(St(t)) : El(t);\n  }\n  function Ze(t) {\n    return typeof t === 'function'\n      ? t\n      : t == null\n      ? $e\n      : typeof t === 'object'\n      ? P(t)\n        ? Cl(t[0], t[1])\n        : gl(t)\n      : Nl(t);\n  }\n  function $l(t) {\n    return function (e, r, n) {\n      for (var o = -1, a = Object(e), s = n(e), i = s.length; i--; ) {\n        var c = s[t ? i : ++o];\n        if (r(a[c], c, a) === !1) break;\n      }\n      return e;\n    };\n  }\n  var Tl = $l();\n  const Al = Tl;\n  function tr(t, e, r) {\n    ((r !== void 0 && !Ut(t[e], r)) || (r === void 0 && !(e in t))) && Pe(t, e, r);\n  }\n  function er(t) {\n    return X(t) && ce(t);\n  }\n  function rr(t, e) {\n    if (!(e === 'constructor' && typeof t[e] === 'function') && e != '__proto__') return t[e];\n  }\n  function Ml(t) {\n    return bt(t, Ht(t));\n  }\n  function Pl(t, e, r, n, o, a, s) {\n    var i = rr(t, r),\n      c = rr(e, r),\n      u = s.get(c);\n    if (u) {\n      tr(t, r, u);\n      return;\n    }\n    var l = a ? a(i, c, r + '', t, e, s) : void 0,\n      d = l === void 0;\n    if (d) {\n      var f = P(c),\n        p = !f && zt(c),\n        h = !f && !p && Fe(c);\n      (l = c),\n        f || p || h\n          ? P(i)\n            ? (l = i)\n            : er(i)\n            ? (l = Hr(i))\n            : p\n            ? ((d = !1), (l = dn(c, !0)))\n            : h\n            ? ((d = !1), (l = Cn(c, !0)))\n            : (l = [])\n          : U(c) || kt(c)\n          ? ((l = i), kt(i) ? (l = Ml(i)) : (!V(i) || Te(i)) && (l = wn(c)))\n          : (d = !1);\n    }\n    d && (s.set(c, l), o(l, c, n, a, s), s.delete(c)), tr(t, r, l);\n  }\n  function Un(t, e, r, n, o) {\n    t !== e &&\n      Al(\n        e,\n        function (a, s) {\n          if ((o || (o = new W()), V(a))) Pl(t, e, s, r, Un, n, o);\n          else {\n            var i = n ? n(rr(t, s), a, s + '', t, e, o) : void 0;\n            i === void 0 && (i = a), tr(t, s, i);\n          }\n        },\n        Ht\n      );\n  }\n  function xl(t, e, r) {\n    for (var n = -1, o = t == null ? 0 : t.length; ++n < o; ) if (r(e, t[n])) return !0;\n    return !1;\n  }\n  function kn(t) {\n    var e = t == null ? 0 : t.length;\n    return e ? t[e - 1] : void 0;\n  }\n  function Dl(t, e) {\n    return e.length < 2 ? t : de(t, nc(e, 0, -1));\n  }\n  var Rl = vi(function (t, e, r) {\n    Un(t, e, r);\n  });\n  const zn = Rl;\n  var Il = 'Expected a function';\n  function Ll(t) {\n    if (typeof t !== 'function') throw new TypeError(Il);\n    return function () {\n      var e = arguments;\n      switch (e.length) {\n        case 0:\n          return !t.call(this);\n        case 1:\n          return !t.call(this, e[0]);\n        case 2:\n          return !t.call(this, e[0], e[1]);\n        case 3:\n          return !t.call(this, e[0], e[1], e[2]);\n      }\n      return !t.apply(this, e);\n    };\n  }\n  function Fl(t, e) {\n    return (e = jt(e, t)), (t = Dl(t, e)), t == null || delete t[St(kn(e))];\n  }\n  function Bl(t) {\n    return U(t) ? void 0 : t;\n  }\n  var Ul = 1,\n    kl = 2,\n    zl = 4,\n    Gl = Ys(function (t, e) {\n      var r = {};\n      if (t == null) return r;\n      var n = !1;\n      (e = Ne(e, function (a) {\n        return (a = jt(a, t)), n || (n = a.length > 1), a;\n      })),\n        bt(t, We(t), r),\n        n && (r = qt(r, Ul | kl | zl, Bl));\n      for (var o = e.length; o--; ) Fl(r, e[o]);\n      return r;\n    });\n  const Gn = Gl;\n  function Hl(t, e, r, n) {\n    if (!V(t)) return t;\n    e = jt(e, t);\n    for (var o = -1, a = e.length, s = a - 1, i = t; i != null && ++o < a; ) {\n      var c = St(e[o]),\n        u = r;\n      if (c === '__proto__' || c === 'constructor' || c === 'prototype') return t;\n      if (o != s) {\n        var l = i[c];\n        (u = n ? n(l, c, i) : void 0), u === void 0 && (u = V(l) ? l : se(e[o + 1]) ? [] : {});\n      }\n      xe(i, c, u), (i = i[c]);\n    }\n    return t;\n  }\n  function Vl(t, e, r) {\n    for (var n = -1, o = e.length, a = {}; ++n < o; ) {\n      var s = e[n],\n        i = de(t, s);\n      r(i, s) && Hl(a, jt(s, t), i);\n    }\n    return a;\n  }\n  function Wl(t, e) {\n    if (t == null) return {};\n    var r = Ne(We(t), function (n) {\n      return [n];\n    });\n    return (\n      (e = Ze(e)),\n      Vl(t, r, function (n, o) {\n        return e(n, o[0]);\n      })\n    );\n  }\n  function Jl(t, e) {\n    return Wl(t, Ll(Ze(e)));\n  }\n  var ql = 1 / 0,\n    Ql =\n      Ot && 1 / Ye(new Ot([, -0]))[1] == ql\n        ? function (t) {\n            return new Ot(t);\n          }\n        : qa;\n  const Yl = Ql;\n  var Xl = 200;\n  function Kl(t, e, r) {\n    var n = -1,\n      o = ci,\n      a = t.length,\n      s = !0,\n      i = [],\n      c = i;\n    if (r) (s = !1), (o = xl);\n    else if (a >= Xl) {\n      var u = e ? null : Yl(t);\n      if (u) return Ye(u);\n      (s = !1), (o = Pn), (c = new Qt());\n    } else c = e ? [] : i;\n    t: for (; ++n < a; ) {\n      var l = t[n],\n        d = e ? e(l) : l;\n      if (((l = r || l !== 0 ? l : 0), s && d === d)) {\n        for (var f = c.length; f--; ) if (c[f] === d) continue t;\n        e && c.push(d), i.push(l);\n      } else o(c, d, r) || (c !== i && c.push(d), i.push(l));\n    }\n    return i;\n  }\n  var Zl = qr(function (t) {\n    var e = kn(t);\n    return er(e) && (e = void 0), Kl(ze(t, 1, er, !0), Ze(e));\n  });\n  const tf = Zl;\n  class Hn extends TypeError {\n    constructor(e, r) {\n      let n;\n      const { message: o, ...a } = e,\n        { path: s } = e,\n        i = s.length === 0 ? o : `At path: ${s.join('.')} -- ${o}`;\n      super(i),\n        (this.value = void 0),\n        (this.key = void 0),\n        (this.type = void 0),\n        (this.refinement = void 0),\n        (this.path = void 0),\n        (this.branch = void 0),\n        (this.failures = void 0),\n        Object.assign(this, a),\n        (this.name = this.constructor.name),\n        (this.failures = () => (n != null ? n : (n = [e, ...r()])));\n    }\n  }\n  function ef(t) {\n    return J(t) && typeof t[Symbol.iterator] === 'function';\n  }\n  function J(t) {\n    return typeof t === 'object' && t != null;\n  }\n  function z(t) {\n    return typeof t === 'symbol' ? t.toString() : typeof t === 'string' ? JSON.stringify(t) : `${t}`;\n  }\n  function rf(t) {\n    const { done: e, value: r } = t.next();\n    return e ? void 0 : r;\n  }\n  function nf(t, e, r, n) {\n    if (t === !0) return;\n    t === !1 ? (t = {}) : typeof t === 'string' && (t = { message: t });\n    const { path: o, branch: a } = e,\n      { type: s } = r,\n      {\n        refinement: i,\n        message: c = `Expected a value of type \\`${s}\\`${i ? ` with refinement \\`${i}\\`` : ''}, but received: \\`${z(\n          n\n        )}\\``,\n      } = t;\n    return { value: n, type: s, refinement: i, key: o[o.length - 1], path: o, branch: a, ...t, message: c };\n  }\n  function* Vn(t, e, r, n) {\n    ef(t) || (t = [t]);\n    for (const o of t) {\n      const a = nf(o, e, r, n);\n      a && (yield a);\n    }\n  }\n  function* nr(t, e, r) {\n    r === void 0 && (r = {});\n    const { path: n = [], branch: o = [t], coerce: a = !1, mask: s = !1 } = r,\n      i = { path: n, branch: o };\n    if (a && ((t = e.coercer(t, i)), s && e.type !== 'type' && J(e.schema) && J(t) && !Array.isArray(t)))\n      for (const u in t) e.schema[u] === void 0 && delete t[u];\n    let c = 'valid';\n    for (const u of e.validator(t, i)) (c = 'not_valid'), yield [u, void 0];\n    for (let [u, l, d] of e.entries(t, i)) {\n      const f = nr(l, d, {\n        path: u === void 0 ? n : [...n, u],\n        branch: u === void 0 ? o : [...o, l],\n        coerce: a,\n        mask: s,\n      });\n      for (const p of f)\n        p[0]\n          ? ((c = p[0].refinement != null ? 'not_refined' : 'not_valid'), yield [p[0], void 0])\n          : a &&\n            ((l = p[1]),\n            u === void 0\n              ? (t = l)\n              : t instanceof Map\n              ? t.set(u, l)\n              : t instanceof Set\n              ? t.add(l)\n              : J(t) && (l !== void 0 || u in t) && (t[u] = l));\n    }\n    if (c !== 'not_valid') for (const u of e.refiner(t, i)) (c = 'not_refined'), yield [u, void 0];\n    c === 'valid' && (yield [void 0, t]);\n  }\n  class K {\n    constructor(e) {\n      (this.TYPE = void 0),\n        (this.type = void 0),\n        (this.schema = void 0),\n        (this.coercer = void 0),\n        (this.validator = void 0),\n        (this.refiner = void 0),\n        (this.entries = void 0);\n      const { type: r, schema: n, validator: o, refiner: a, coercer: s = (c) => c, entries: i = function* () {} } = e;\n      (this.type = r),\n        (this.schema = n),\n        (this.entries = i),\n        (this.coercer = s),\n        o\n          ? (this.validator = (c, u) => {\n              const l = o(c, u);\n              return Vn(l, u, this, c);\n            })\n          : (this.validator = () => []),\n        a\n          ? (this.refiner = (c, u) => {\n              const l = a(c, u);\n              return Vn(l, u, this, c);\n            })\n          : (this.refiner = () => []);\n    }\n    assert(e) {\n      return Wn(e, this);\n    }\n    create(e) {\n      return of(e, this);\n    }\n    is(e) {\n      return sf(e, this);\n    }\n    mask(e) {\n      return af(e, this);\n    }\n    validate(e, r) {\n      return r === void 0 && (r = {}), Ct(e, this, r);\n    }\n  }\n  function Wn(t, e) {\n    const r = Ct(t, e);\n    if (r[0]) throw r[0];\n  }\n  function of(t, e) {\n    const r = Ct(t, e, { coerce: !0 });\n    if (r[0]) throw r[0];\n    return r[1];\n  }\n  function af(t, e) {\n    const r = Ct(t, e, { coerce: !0, mask: !0 });\n    if (r[0]) throw r[0];\n    return r[1];\n  }\n  function sf(t, e) {\n    return !Ct(t, e)[0];\n  }\n  function Ct(t, e, r) {\n    r === void 0 && (r = {});\n    const n = nr(t, e, r),\n      o = rf(n);\n    return o[0]\n      ? [\n          new Hn(o[0], function* () {\n            for (const a of n) a[0] && (yield a[0]);\n          }),\n          void 0,\n        ]\n      : [void 0, o[1]];\n  }\n  function or() {\n    for (var t = arguments.length, e = new Array(t), r = 0; r < t; r++) e[r] = arguments[r];\n    const n = e[0].type === 'type',\n      o = e.map((s) => s.schema),\n      a = Object.assign({}, ...o);\n    return n ? Yn(a) : C(a);\n  }\n  function st(t, e) {\n    return new K({ type: t, schema: null, validator: e });\n  }\n  function wt(t) {\n    return new K({\n      type: 'dynamic',\n      schema: null,\n      *entries(e, r) {\n        yield* t(e, r).entries(e, r);\n      },\n      validator(e, r) {\n        return t(e, r).validator(e, r);\n      },\n      coercer(e, r) {\n        return t(e, r).coercer(e, r);\n      },\n      refiner(e, r) {\n        return t(e, r).refiner(e, r);\n      },\n    });\n  }\n  function Jn(t, e) {\n    const { schema: r } = t,\n      n = { ...r };\n    for (const o of e) delete n[o];\n    switch (t.type) {\n      case 'type':\n        return Yn(n);\n      default:\n        return C(n);\n    }\n  }\n  function R() {\n    return st('any', () => !0);\n  }\n  function x(t) {\n    return new K({\n      type: 'array',\n      schema: t,\n      *entries(e) {\n        if (t && Array.isArray(e)) for (const [r, n] of e.entries()) yield [r, n, t];\n      },\n      coercer(e) {\n        return Array.isArray(e) ? e.slice() : e;\n      },\n      validator(e) {\n        return Array.isArray(e) || `Expected an array value, but received: ${z(e)}`;\n      },\n    });\n  }\n  function nt() {\n    return st('boolean', (t) => typeof t === 'boolean');\n  }\n  function ar(t) {\n    const e = {},\n      r = t.map((n) => z(n)).join();\n    for (const n of t) e[n] = n;\n    return new K({\n      type: 'enums',\n      schema: e,\n      validator(n) {\n        return t.includes(n) || `Expected one of \\`${r}\\`, but received: ${z(n)}`;\n      },\n    });\n  }\n  function qn() {\n    return st('func', (t) => typeof t === 'function' || `Expected a function, but received: ${z(t)}`);\n  }\n  function k(t) {\n    const e = z(t),\n      r = typeof t;\n    return new K({\n      type: 'literal',\n      schema: r === 'string' || r === 'number' || r === 'boolean' ? t : null,\n      validator(n) {\n        return n === t || `Expected the literal \\`${e}\\`, but received: ${z(n)}`;\n      },\n    });\n  }\n  function cf() {\n    return st('never', () => !1);\n  }\n  function Qn() {\n    return st('number', (t) => (typeof t === 'number' && !isNaN(t)) || `Expected a number, but received: ${z(t)}`);\n  }\n  function C(t) {\n    const e = t ? Object.keys(t) : [],\n      r = cf();\n    return new K({\n      type: 'object',\n      schema: t || null,\n      *entries(n) {\n        if (t && J(n)) {\n          const o = new Set(Object.keys(n));\n          for (const a of e) o.delete(a), yield [a, n[a], t[a]];\n          for (const a of o) yield [a, n[a], r];\n        }\n      },\n      validator(n) {\n        return J(n) || `Expected an object, but received: ${z(n)}`;\n      },\n      coercer(n) {\n        return J(n) ? { ...n } : n;\n      },\n    });\n  }\n  function m(t) {\n    return new K({\n      ...t,\n      validator: (e, r) => e === void 0 || t.validator(e, r),\n      refiner: (e, r) => e === void 0 || t.refiner(e, r),\n    });\n  }\n  function Yt(t, e) {\n    return new K({\n      type: 'record',\n      schema: null,\n      *entries(r) {\n        if (J(r))\n          for (const n in r) {\n            const o = r[n];\n            yield [n, n, t], yield [n, o, e];\n          }\n      },\n      validator(r) {\n        return J(r) || `Expected an object, but received: ${z(r)}`;\n      },\n    });\n  }\n  function v() {\n    return st('string', (t) => typeof t === 'string' || `Expected a string, but received: ${z(t)}`);\n  }\n  function Yn(t) {\n    const e = Object.keys(t);\n    return new K({\n      type: 'type',\n      schema: t,\n      *entries(r) {\n        if (J(r)) for (const n of e) yield [n, r[n], t[n]];\n      },\n      validator(r) {\n        return J(r) || `Expected an object, but received: ${z(r)}`;\n      },\n    });\n  }\n  function B(t) {\n    const e = t.map((r) => r.type).join(' | ');\n    return new K({\n      type: 'union',\n      schema: null,\n      coercer(r, n) {\n        return (\n          t.find((o) => {\n            const [a] = o.validate(r, { coerce: !0 });\n            return !a;\n          }) || uf()\n        ).coercer(r, n);\n      },\n      validator(r, n) {\n        const o = [];\n        for (const a of t) {\n          const [...s] = nr(r, a, n),\n            [i] = s;\n          if (i[0]) for (const [c] of s) c && o.push(c);\n          else return [];\n        }\n        return [`Expected the value to satisfy a union of \\`${e}\\`, but received: ${z(r)}`, ...o];\n      },\n    });\n  }\n  function uf() {\n    return st('unknown', () => !0);\n  }\n  const ir = C({ package: v(), version: v(), exportName: v(), destructuring: m(nt()), subName: m(v()), main: m(v()) }),\n    lf = x(ir),\n    ff = ['CBlock', 'CContainer', 'CImage', 'CCanvas', 'CVideo', 'CAudio', 'CText', 'CNativeTag'];\n  var H = ((t) => ((t.SLOT = 'SLOT'), (t.FUNCTION = 'FUNCTION'), (t.EXPRESSION = 'EXPRESSION'), t))(H || {}),\n    sr = ((t) => ((t.DESIGN = 'design'), (t.SAVE = 'save'), t))(sr || {}),\n    cr = ((t) => ((t.FUNC = 'FUNC'), (t.COMP = 'COMP'), t))(cr || {});\n  const df = () =>\n      st('normalObj', (t) =>\n        !U(t) || [H.SLOT, H.EXPRESSION, H.FUNCTION].includes(t == null ? void 0 : t.type)\n          ? !1\n          : (Ct(t, Yt(v(), ur)), !0)\n      ),\n    ur = B([\n      v(),\n      Qn(),\n      nt(),\n      C({ type: k(H.SLOT), renderType: ar([cr.FUNC, cr.COMP]), params: m(x(v())), value: wt(() => B([Et, x(Et)])) }),\n      C({ type: k(H.EXPRESSION), value: v() }),\n      C({ type: k(H.FUNCTION), value: v() }),\n      df(),\n      x(wt(() => ur)),\n    ]),\n    Xn = C({ type: k(H.EXPRESSION), value: v() }),\n    Et = C({\n      id: m(v()),\n      title: m(v()),\n      componentName: v(),\n      props: m(Yt(v(), ur)),\n      nodeName: m(v()),\n      state: m(Yt(v(), R())),\n      children: wt(() => m(x(B([v(), Et])))),\n      configure: m(R()),\n      css: m(R()),\n      style: m(R()),\n      classNames: m(x(R())),\n      refId: m(v()),\n      extra: m(Yt(R(), R())),\n      condition: m(B([nt(), Xn])),\n      tempDevConfig: m(R()),\n      loop: m(\n        C({\n          open: nt(),\n          data: B([x(R()), Xn]),\n          args: m(x(v())),\n          forName: m(v()),\n          forIndex: m(v()),\n          key: m(R()),\n          name: m(v()),\n        })\n      ),\n    }),\n    ve = B([v(), C({ label: v(), tip: m(v()) })]),\n    pf = C({ type: k('shape'), value: x(C({ name: v(), title: ve, valueType: wt(() => me) })) }),\n    hf = C({ type: k('enums'), value: x(v()) });\n  C({ type: k('array'), value: wt(() => me) });\n  const vf = C({ type: k('array'), value: wt(() => x(me)) }),\n    me = B([\n      ar(['array', 'boolean', 'number', 'object', 'string']),\n      ar(['component', 'expression', 'function']),\n      pf,\n      hf,\n      vf,\n    ]),\n    mf = B([v(), C({ componentName: v(), props: m(R()), initialValue: m(R()), component: m(R()) })]),\n    lr = C({\n      name: v(),\n      title: ve,\n      valueType: me,\n      description: m(v()),\n      defaultValue: R(),\n      setters: m(x(mf)),\n      condition: m(qn()),\n    }),\n    yf = B([v(), qn()]);\n  var fr = ((t) => ((t.SINGLE = 'single'), (t.GROUP = 'group'), t))(fr || {});\n  B([v(), C({ name: v(), describe: m(v()), params: m(C({ name: v(), description: v() })), template: v() })]);\n  const gf = C({\n      id: m(v()),\n      title: v(),\n      snapshot: B([v(), R()]),\n      snapshotText: m(v()),\n      description: m(v()),\n      tags: m(x(v())),\n      groupName: m(v()),\n      category: m(v()),\n      schema: or(Jn(Et, ['id']), C({ componentName: m(v()) })),\n    }),\n    bf = C({\n      componentName: v(),\n      title: v(),\n      screenshot: m(v()),\n      icon: m(v()),\n      tags: m(x(v())),\n      groupName: m(v()),\n      category: m(v()),\n      priority: m(Qn()),\n      npm: m(ir),\n      snippets: x(gf),\n      props: x(\n        B([\n          lr,\n          C({ title: m(ve), type: k('single'), content: lr }),\n          C({ title: m(ve), type: k('group'), content: x(lr) }),\n        ])\n      ),\n      fixedProps: m(R()),\n      isContainer: m(B([nt(), C({ placeholder: v(), width: v(), height: v() })])),\n      isModal: m(B([nt(), C({ visibleKey: v() })])),\n      isSupportStyle: m(nt()),\n      isSupportDispatchNativeEvent: m(nt()),\n      isLayout: m(nt()),\n      rootSelector: m(v()),\n      selectionToolBars: m(x(yf)),\n      extra: m(Yt(R(), R())),\n    }),\n    _f =\n      (t) =>\n      ({ data: e, message: r, throwError: n }) => {\n        const o = t({ data: e, message: r, throwError: n });\n        if (o.isValidate) return o;\n        if (n)\n          throw o.message || r\n            ? new Error(`${o.message || r}\n originData: ${JSON.stringify(e)}`)\n            : new Error(`${JSON.stringify(e)}\n data struct format is invalidate`);\n        return o;\n      },\n    dr = (t) => {\n      const { data: e, message: r, throwError: n, dataStruct: o } = t;\n      return _f(({ data: a }) => {\n        try {\n          return Wn(a, o), { isValidate: !0 };\n        } catch (s) {\n          let i = s;\n          return (\n            s instanceof Hn &&\n              (i = s.failures().map(\n                (c) => `\\u3010${c.path.join('.')}\\u3011: ${c.message}\n`\n              )),\n            { isValidate: !1, message: i, error: s }\n          );\n        }\n      })({ data: e, message: r, throwError: n });\n    };\n  var Nt = ((t) => ((t.ROOT_CONTAINER = 'RootContainer'), t))(Nt || {});\n  const jf = C({ type: k(H.FUNCTION), value: v() }),\n    Sf = or(Jn(Et, ['componentName']), C({ componentName: k('RootContainer') }));\n  function Of(t) {\n    return {\n      all: (t = t || new Map()),\n      on: function (e, r) {\n        var n = t.get(e);\n        n ? n.push(r) : t.set(e, [r]);\n      },\n      off: function (e, r) {\n        var n = t.get(e);\n        n && (r ? n.splice(n.indexOf(r) >>> 0, 1) : t.set(e, []));\n      },\n      emit: function (e, r) {\n        var n = t.get(e);\n        n &&\n          n.slice().map(function (o) {\n            o(r);\n          }),\n          (n = t.get('*')) &&\n            n.slice().map(function (o) {\n              o(e, r);\n            });\n      },\n    };\n  }\n  const Xt = Of(),\n    Cf = (t, e) => {\n      const r = { ...t, value: [] },\n        n = t.value;\n      let o = new mt([]);\n      return (\n        e && (o = e.materialsMode || new mt([])),\n        n &&\n          (P(n)\n            ? (r.value = n.map((a) => new A(a, { parent: e, materials: o })))\n            : U(n) && r.value.push(new A(n, { parent: e, materials: o }))),\n        r\n      );\n    };\n  class $t {\n    constructor(e, r) {\n      j(this, 'nodeType', 'SLOT'),\n        j(this, 'rawData'),\n        j(this, 'parent'),\n        j(this, 'emitter', Xt),\n        j(this, 'data'),\n        j(this, 'id'),\n        j(this, 'materialsMode'),\n        (this.parent = (r == null ? void 0 : r.parent) || null),\n        (this.rawData = e);\n      const n = (r == null ? void 0 : r.materials) || new mt([]);\n      (this.materialsMode = n), (this.id = Z()), (this.data = Cf(e, this));\n    }\n    get value() {\n      return this.data;\n    }\n    export(e) {\n      const r = this.data,\n        n = (o) => {\n          if (o instanceof A) return o.export(e);\n          if (U(o)) {\n            const a = {};\n            return (\n              Object.keys(o || {}).forEach((s) => {\n                a[s] = n(o[s]);\n              }),\n              a\n            );\n          }\n          return P(o) ? o.map((a) => n(a)) : (e === 'design' && delete o.id, o);\n        };\n      return n(r);\n    }\n  }\n  const Kn = (t) => {\n      let e = [];\n      return (\n        t.forEach((r) => {\n          const n = r;\n          n.type\n            ? n.type === fr.SINGLE\n              ? e.push(n.content)\n              : n.type === fr.GROUP && (e = [...e, ...Kn(n.content)])\n            : e.push(r);\n        }),\n        e\n      );\n    },\n    pr = (t, e, r) => {\n      if (t.type) return t.type === H.SLOT ? new $t(t, { parent: e, materials: r }) : t;\n      if (U(t)) {\n        const n = {};\n        return (\n          Object.keys(t).forEach((o) => {\n            n[o] = hr(t[o], e, r);\n          }),\n          n\n        );\n      } else return Array.isArray(t) ? t.map((n) => pr(n, e, r)) : t;\n    },\n    hr = (t, e, r) => (U(t) ? pr(t, e, r) : P(t) ? t.map((n) => pr(n, e, r)) : t);\n  class ct {\n    constructor(e, r, n) {\n      j(this, 'nodeType', 'PROP'),\n        j(this, 'rawData'),\n        j(this, 'parent'),\n        j(this, 'emitter', Xt),\n        j(this, 'data'),\n        j(this, 'name'),\n        j(this, 'materialsMode');\n      const o = (n == null ? void 0 : n.materials) || new mt([]);\n      (this.materialsMode = o),\n        (this.parent = n == null ? void 0 : n.parent),\n        (this.rawData = r),\n        (this.name = e),\n        (this.data = hr(r, this, o));\n    }\n    isIncludeSlot() {\n      return !1;\n    }\n    isIncludeExpression() {\n      return !1;\n    }\n    get value() {\n      return this.data;\n    }\n    updateValue(e) {\n      const r = this.data;\n      (this.data = hr(e != null ? e : r, this, this.materialsMode)),\n        this.emitter.emit('onPropChange', { value: this.data, preValue: r, node: this }),\n        this.parent &&\n          !(this.parent instanceof $t) &&\n          this.emitter.emit('onNodeChange', {\n            value: this.parent.value,\n            preValue: this.parent.value,\n            node: this.parent,\n          });\n    }\n    get material() {\n      const e = this.parent;\n      if (e instanceof A) {\n        const r = e.material;\n        return Kn((r == null ? void 0 : r.value.props) || []).find((n) => n.name === this.name);\n      } else return null;\n    }\n    export(e) {\n      const r = this.data,\n        n = (o) => {\n          if (o instanceof ct || o instanceof $t || o instanceof A) return o.export(e);\n          if (P(o)) return o.map((a) => n(a));\n          if (U(o)) {\n            const a = {};\n            return (\n              Object.keys(o || {}).forEach((s) => {\n                a[s] = n(o[s]);\n              }),\n              a\n            );\n          }\n          return o;\n        };\n      return n(r);\n    }\n  }\n  const wf = (t) => {\n      if (typeof t === 'string') return !0;\n      dr({ data: t, dataStruct: Et, throwError: !0 });\n    },\n    Zn = (t, e, r = new mt([])) => {\n      var n;\n      if (typeof t === 'string') return t;\n      const o = {\n          ...t,\n          id: (n = t.id) != null ? n : Z(),\n          children: [],\n          props: {},\n          configure: zn(t.configure || {}, { propsSetter: {}, advanceSetter: {} }),\n        },\n        a = Object.keys(t.props || {});\n      return (\n        a.length &&\n          a.forEach((s) => {\n            var i;\n            const c = (i = t.props) == null ? void 0 : i[s];\n            if (c instanceof ct) {\n              o.props[s] = c;\n              return;\n            }\n            o.props[s] = new ct(s, c || '', { parent: e, materials: r });\n          }),\n        t.children &&\n          (Array.isArray(t.children)\n            ? (o.children = t.children.map((s) => {\n                if (s instanceof A) return s;\n                if (U(s)) {\n                  const i = s;\n                  return new A(i, { parent: e, materials: r });\n                } else return s;\n              }))\n            : (t.children instanceof A && (o.children = [t.children]),\n              (o.children = [new A(t.children, { parent: e, materials: r })]))),\n        o\n      );\n    };\n  class A {\n    constructor(e, r) {\n      j(this, 'nodeType', 'NODE'),\n        j(this, 'rawData'),\n        j(this, 'data'),\n        j(this, 'emitter', Xt),\n        j(this, 'parent'),\n        j(this, 'materialsModel'),\n        j(this, 'listenerHandle'),\n        j(this, 'onChangeCbQueue'),\n        (this.rawData = JSON.parse(JSON.stringify(e))),\n        wf(e);\n      const n = (r == null ? void 0 : r.materials) || new mt([]);\n      (this.parent = (r == null ? void 0 : r.parent) || null),\n        (this.materialsModel = n),\n        (this.data = Zn(e, this, n)),\n        (this.listenerHandle = []),\n        (this.onChangeCbQueue = []),\n        this.registerListener();\n    }\n    registerListener() {\n      const e = (r) => {\n        const { node: n } = r;\n        n === this && n.id === this.id && this.onChangeCbQueue.forEach((o) => o(r));\n      };\n      this.emitter.on('onNodeChange', e),\n        this.listenerHandle.push(() => {\n          this.emitter.off('onNodeChange', e);\n        });\n    }\n    onChange(e) {\n      return (\n        this.onChangeCbQueue.push(e),\n        () => {\n          this.onChangeCbQueue = this.onChangeCbQueue.filter((r) => r !== e);\n        }\n      );\n    }\n    destroy() {\n      this.listenerHandle.forEach((e) => e());\n    }\n    get id() {\n      return this.data.id;\n    }\n    get value() {\n      return this.data;\n    }\n    clone(e) {\n      const r = { ...this.export('design'), id: e || Z() };\n      return new A(r, { materials: this.materialsModel });\n    }\n    updateValue(e) {\n      const r = this.data,\n        n = { ...this.data, ...e };\n      (this.data = Zn(n, this)), this.emitter.emit('onNodeChange', { value: n, preValue: r, node: this });\n    }\n    contains(e) {\n      return yr(this, e);\n    }\n    get props() {\n      return this.data.props;\n    }\n    get material() {\n      const e = this.materialsModel;\n      return e == null ? void 0 : e.findByComponentName(this.data.componentName);\n    }\n    getPlainProps() {\n      const e = this.data,\n        r = {};\n      return (\n        Object.keys(e.props || {}).forEach((n) => {\n          r[n] = e.props[n].export('design');\n        }),\n        r\n      );\n    }\n    export(e) {\n      var r;\n      const n = this.data;\n      if (typeof n === 'string') return n;\n      const o = {};\n      Object.keys(n.props || {}).forEach((l) => {\n        o[l] = n.props[l].export(e);\n      });\n      const a = (r = n.children) == null ? void 0 : r.map((l) => (l instanceof A ? l.export(e) : l)),\n        s = n.configure || {},\n        i = s.propsSetter || {},\n        c = {};\n      Object.keys(i).forEach((l) => {\n        const d = on(i, l, !1);\n        d && (c[l] = d);\n      }),\n        (s.propsSetter = c),\n        this.material && this.materialsModel.usedMaterials.push(this.material);\n      let u = { ...n, configure: s, props: o, children: a };\n      return e === 'design' && delete u.id, (u = mr(u)), u;\n    }\n  }\n  const to = (t, e, r) => {\n    const n = {\n      ...t,\n      id: Z(),\n      props: {},\n      componentName: Nt.ROOT_CONTAINER,\n      children: [],\n      configure: zn(t.configure || {}, { propsSetter: {}, advanceSetter: {} }),\n    };\n    let o = [];\n    P(t.children)\n      ? (o = t.children.map((s) => (s instanceof A ? s : U(s) ? new A(s, { parent: e, materials: r }) : s)))\n      : t.children instanceof A\n      ? o.push(t.children)\n      : t.children && U(t.children) && o.push(new A(t.children, { parent: e, materials: r }));\n    const a = Object.keys(t.props || {});\n    return (\n      a.length &&\n        a.forEach((s) => {\n          var i;\n          const c = (i = t.props) == null ? void 0 : i[s];\n          c instanceof ct ? (n.props[s] = c) : (n.props[s] = new ct(s, c || '', { parent: e, materials: r }));\n        }),\n      (n.children = o),\n      n\n    );\n  };\n  class Kt {\n    constructor(e, { parent: r, materials: n }) {\n      j(this, 'rawData'),\n        j(this, 'data'),\n        j(this, 'nodeType', Nt.ROOT_CONTAINER),\n        j(this, 'emitter', Xt),\n        j(this, 'materialsModel'),\n        j(this, 'listenerHandle'),\n        j(this, 'onChangeCbQueue'),\n        j(this, 'parent'),\n        (this.materialsModel = n),\n        (this.rawData = JSON.parse(JSON.stringify(e))),\n        (this.data = to(e, this, n)),\n        (this.listenerHandle = []),\n        (this.onChangeCbQueue = []),\n        this.registerListener(),\n        (this.parent = r);\n    }\n    registerListener() {\n      const e = (r) => {\n        const { node: n } = r;\n        n === this && n.id === this.id && this.onChangeCbQueue.forEach((o) => o(r));\n      };\n      this.emitter.on('onNodeChange', e),\n        this.listenerHandle.push(() => {\n          this.emitter.off('onNodeChange', e);\n        });\n    }\n    onChange(e) {\n      return (\n        this.onChangeCbQueue.push(e),\n        () => {\n          this.onChangeCbQueue = this.onChangeCbQueue.filter((r) => r !== e);\n        }\n      );\n    }\n    get id() {\n      return this.data.id;\n    }\n    get value() {\n      return this.data;\n    }\n    get props() {\n      return this.data.props;\n    }\n    get material() {\n      const e = this.materialsModel;\n      return e == null ? void 0 : e.findByComponentName(this.data.componentName);\n    }\n    updateValue(e) {\n      const r = this.data,\n        n = { ...this.data, ...e };\n      (this.data = to(n, this, this.materialsModel)),\n        this.emitter.emit('onNodeChange', { value: this.data, preValue: r, node: this });\n    }\n    contains(e) {\n      return yr(this, e);\n    }\n    export(e = sr.SAVE) {\n      var r;\n      const n = this.data,\n        o = {};\n      Object.keys(n.props || {}).forEach((c) => {\n        o[c] = n.props[c].export(e);\n      });\n      const a =\n          ((r = n.children) == null\n            ? void 0\n            : r.map((c) => {\n                var u;\n                return (u = c == null ? void 0 : c.export) == null ? void 0 : u.call(c, e);\n              })) || [],\n        s = { ...n, props: o, children: a.filter((c) => c) };\n      let i = Gn(s, ['id']);\n      return (i = mr(i)), i;\n    }\n    getPlainProps() {\n      const e = this.data,\n        r = {};\n      return (\n        Object.keys(e.props || {}).forEach((n) => {\n          r[n] = e.props[n].export('design');\n        }),\n        r\n      );\n    }\n    destroy() {\n      this.listenerHandle.forEach((e) => e());\n    }\n    clone(e) {\n      const r = { ...this.export('design'), id: e || Z() };\n      return new Kt(r, { materials: this.materialsModel, parent: null });\n    }\n  }\n  const vt = (t) => (t == null ? void 0 : t.type) === H.EXPRESSION,\n    Ef = (t) => (t == null ? void 0 : t.type) == H.FUNCTION,\n    Z = () => Math.random().toString(32).slice(3, 9),\n    Nf = (t) => (t == null ? void 0 : t.nodeType) === 'NODE',\n    vr = (t) => (t == null ? void 0 : t.nodeType) === 'PROP',\n    $f = (t) => (t == null ? void 0 : t.nodeType) === 'SLOT',\n    mr = (t) => Jl(t, (e) => (U(e) ? !Object.keys(e).length : P(e) ? !e.length : !e));\n  function yr(t, e) {\n    const r = [t];\n    for (; r.length; ) {\n      const n = r.shift();\n      if ((n == null ? void 0 : n.id) === e) return n;\n      const o = (n == null ? void 0 : n.props) || {},\n        a = (i) => {\n          if (i instanceof A) {\n            r.push(i);\n            return;\n          }\n          if ((i instanceof $t && a(i.value.value), i instanceof ct)) {\n            a(i.value);\n            return;\n          }\n          if (U(i)) {\n            const c = i;\n            Object.keys(c).map((u) => {\n              a(c[u]);\n            });\n            return;\n          }\n          if (P(i)) {\n            i.forEach((c) => {\n              a(c);\n            });\n            return;\n          }\n        };\n      a(o);\n      const s = (n == null ? void 0 : n.value.children.filter((i) => i instanceof A)) || [];\n      r.push(...s);\n    }\n    return null;\n  }\n  const Tf = (t) => {\n    const e = Mn(t),\n      r = e.snippets;\n    return (\n      delete e.snippets,\n      (e.snippets = r.map((n) => ({\n        ...e,\n        ...n,\n        id: n.id || `${t.componentName}-${Z()}`,\n        title: n.title || t.title,\n        category: n.category || t.category,\n        tags: [...(n.tags || []), ...(t.tags || [])],\n        groupName: n.groupName || t.groupName,\n        snapshot: n.snapshot || t.icon,\n        snapshotText: n.snapshotText,\n        schema: { ...n.schema, componentName: n.schema.componentName || t.componentName },\n      }))),\n      e\n    );\n  };\n  class Af {\n    constructor(e) {\n      j(this, 'rawData'), j(this, 'data'), (this.rawData = e), (this.data = Tf(e));\n    }\n    get value() {\n      return this.data;\n    }\n    get rawValue() {\n      return this.rawData;\n    }\n    get componentName() {\n      return this.data.componentName;\n    }\n    get snippets() {\n      return this.data.snippets;\n    }\n    getSnippetById(e) {\n      return this.data.snippets.find((r) => r.id === e);\n    }\n  }\n  const Mf = (t) => {\n      if (!P(t)) throw new Error('Materials must be a array');\n      return t.map((e) => new Af(e));\n    },\n    Pf = (t) => {\n      t == null ||\n        t.forEach((e) => {\n          dr({ data: e, dataStruct: bf, throwError: !0 });\n        });\n    };\n  class mt {\n    constructor(e) {\n      j(this, 'rawData'), j(this, 'data'), j(this, 'usedMaterials', []), (this.rawData = e), Pf(e), (this.data = Mf(e));\n    }\n    findByComponentName(e) {\n      return this.data.find((r) => r.componentName === e);\n    }\n    findSnippetById(e) {\n      const r = [...this.data];\n      let n = null;\n      for (; !n && r.length; ) {\n        const o = r.pop();\n        n = o == null ? void 0 : o.getSnippetById(e);\n      }\n      return n;\n    }\n    getAllSnippets() {\n      let e = this.data.reduce((a, s) => (a.push(...s.snippets), a), []);\n      const r = [],\n        n = { default: [] };\n      (e = e.sort((a, s) => ((a.category || '') > (s.category || '') ? 1 : -1))),\n        e.forEach((a) => {\n          const s = a.groupName || 'default';\n          r.includes(s) || (r.push(s), (n[s] = [])), n[s].push(a);\n        });\n      const o = [];\n      return (\n        r.forEach((a) => {\n          const s = ['default'],\n            i = { default: [] },\n            c = n[a];\n          if (c.length !== 0) {\n            c.forEach((d) => {\n              const f = d.category || 'default';\n              s.includes(f) || (s.push(f), (i[f] = [])), i[f].push(d);\n            });\n            const u = [];\n            s.forEach((d) => {\n              i[d].length && u.push({ name: d, list: i[d] });\n            });\n            const l = { name: a, list: u };\n            o.push(l);\n          }\n        }),\n        o\n      );\n    }\n    get value() {\n      return this.data;\n    }\n    get rawValue() {\n      return this.rawData;\n    }\n  }\n  const xf = C({\n      version: v(),\n      name: v(),\n      css: m(v()),\n      renderType: m(B([k('COMPONENT'), k('PAGE')])),\n      methods: m(x(jf)),\n      componentsMeta: x(or(C({ componentName: v() }), ir)),\n      thirdLibs: m(lf),\n      componentsTree: Sf,\n      assets: m(x(R())),\n    }),\n    eo = (t) => (dr({ data: t, dataStruct: xf, throwError: !0 }), t),\n    gr = (t, e, r) => ({ ...t, componentsTree: new Kt(t.componentsTree, { parent: e, materials: r }) });\n  class ye {\n    constructor(e, r) {\n      j(this, 'nodeType', 'PAGE'),\n        j(this, 'rawData'),\n        j(this, 'emitter', Xt),\n        j(this, 'data'),\n        j(this, 'parent'),\n        j(this, 'materialsModel'),\n        j(this, 'assetPackagesList'),\n        eo(e),\n        (this.assetPackagesList = (r == null ? void 0 : r.assetPackagesList) || []),\n        (this.rawData = JSON.parse(JSON.stringify(e))),\n        (this.materialsModel = new mt((r == null ? void 0 : r.materials) || [])),\n        (this.data = gr(e, this, this.materialsModel));\n    }\n    updatePage(e) {\n      const r = this.data;\n      (this.rawData = JSON.parse(JSON.stringify(e))),\n        (this.data = gr(e, this, this.materialsModel)),\n        this.emitter.emit('onPageChange', { value: this.data, preValue: r, node: this });\n    }\n    reloadPage(e) {\n      const r = this.data;\n      (this.rawData = JSON.parse(JSON.stringify(e))),\n        (this.data = gr(e, this, this.materialsModel)),\n        this.emitter.emit('onReloadPage', { value: this.data, preValue: r, node: this });\n    }\n    get value() {\n      return this.data;\n    }\n    getNode(e) {\n      const r = this.data.componentsTree;\n      return yr(r, e);\n    }\n    addNode(e, r, n = 'AFTER') {\n      var o, a, s;\n      if (n === 'AFTER' || n === 'BEFORE') {\n        const i = r.parent;\n        if (i === null && r instanceof Kt) return console.warn('Not found parent node'), !1;\n        if (i instanceof ct) return console.warn('CProp can not add node'), !1;\n        if (i instanceof $t) {\n          const u = i.value.value,\n            l = (o = u.findIndex((d) => d === r)) != null ? o : -1;\n          return l >= 0\n            ? (n === 'BEFORE' ? u.splice(l, 0, e) : u.splice(l + 1, 0, e),\n              (e.parent = i),\n              (a = i.parent) == null || a.updateValue(),\n              !0)\n            : !1;\n        }\n        if (i instanceof ye) return !1;\n        const c = (s = i == null ? void 0 : i.value.children.findIndex((u) => u === r)) != null ? s : -1;\n        return c >= 0\n          ? (n === 'BEFORE'\n              ? i == null || i.value.children.splice(c, 0, e)\n              : i == null || i.value.children.splice(c + 1, 0, e),\n            (e.parent = i),\n            i == null || i.updateValue(),\n            !0)\n          : (console.warn('Not found target node'), !1);\n      }\n      if (n === 'CHILD_START') return r.value.children.unshift(e), (e.parent = r), r.updateValue(), !0;\n      if (n === 'CHILD_END') return r.value.children.push(e), (e.parent = r), r.updateValue(), !0;\n      if (U(n)) {\n        const i = n;\n        if (i.type === 'CHILD') {\n          const c = i.pos,\n            u = i.index || 0;\n          return (\n            c === 'BEFORE'\n              ? r == null || r.value.children.splice(u, 0, e)\n              : r == null || r.value.children.splice(u + 1, 0, e),\n            (e.parent = r),\n            r.updateValue(),\n            !0\n          );\n        } else console.warn('Can not parse pos obj');\n      }\n      return !1;\n    }\n    createNode(e) {\n      return delete e.id, new A(e, { parent: null, materials: this.materialsModel });\n    }\n    addNodeById(e, r, n = 'AFTER') {\n      const o = this.getNode(r);\n      return o ? this.addNode(e, o, n) : (console.warn(`Not find a node by ${r}, pls check it`), !1);\n    }\n    copyNode(e) {\n      const r = e.export('design');\n      r.id = Z();\n      const n = new A(r, { parent: e.parent, materials: this.materialsModel });\n      return this.addNode(n, e, 'AFTER'), n;\n    }\n    copyNodeById(e) {\n      const r = this.getNode(e);\n      return r && r instanceof A ? this.copyNode(r) : !1;\n    }\n    moveNode(e, r, n) {\n      this.deleteNode(e);\n      let o = r;\n      return ['AFTER', 'BEFORE'].includes(n) && (o = r.parent), (e.parent = o), this.addNode(e, r, n);\n    }\n    moveNodeById(e, r, n) {\n      const o = this.getNode(e),\n        a = this.getNode(r);\n      return o && a && o instanceof A && a instanceof A ? this.moveNode(o, a, n) : !1;\n    }\n    deleteNode(e) {\n      var r;\n      const n = e.parent;\n      if (!n) throw new Error('parent node is null or undefined, pls check it');\n      if (n instanceof $t) {\n        const o = n.value.value,\n          a = o.findIndex((i) => i === e),\n          s = o[a];\n        return o.splice(a, 1), (r = n.parent) == null || r.updateValue(), s;\n      }\n      if (n instanceof A || n instanceof Kt) {\n        const o = n.value.children,\n          a = o.findIndex((i) => i === e),\n          s = o[a];\n        return o.splice(a, 1), n.updateValue(), s;\n      }\n    }\n    deleteNodeById(e) {\n      const r = this.getNode(e);\n      if (r) return this.deleteNode(r);\n    }\n    export(e = sr.SAVE) {\n      var r;\n      const n = this.data.componentsTree.export(e),\n        o = this.assetPackagesList,\n        a = [],\n        s = this.materialsModel.usedMaterials\n          .map((c) => {\n            const u = o.find((l) => {\n              var d;\n              return l.package === ((d = c.value.npm) == null ? void 0 : d.package);\n            });\n            return u && a.push(u), { componentName: c.componentName, ...Mn(c.value.npm || {}) };\n          })\n          .filter((c) => {\n            if (c.componentName && c.package && c.version) return !0;\n          });\n      this.materialsModel.usedMaterials = [];\n      let i = { ...this.data, componentsTree: mr(n), componentsMeta: s, thirdLibs: this.data.thirdLibs, assets: [] };\n      return (\n        (r = this.data.thirdLibs) == null ||\n          r.forEach((c) => {\n            const u = o.find((l) => {\n              l.package, l.package;\n            });\n            u && a.push(u);\n          }),\n        (i.assets = tf(a, (c) => c.package)),\n        (i = Gn(i, ['id'])),\n        JSON.parse(JSON.stringify(i))\n      );\n    }\n  }\n  Nt.ROOT_CONTAINER;\n  var Df = typeof global === 'object' && global && global.Object === Object && global;\n  const ro = Df;\n  var Rf = typeof self === 'object' && self && self.Object === Object && self,\n    If = ro || Rf || Function('return this')();\n  const Tt = If;\n  var Lf = Tt.Symbol;\n  const At = Lf;\n  var no = Object.prototype,\n    Ff = no.hasOwnProperty,\n    Bf = no.toString,\n    Zt = At ? At.toStringTag : void 0;\n  function Uf(t) {\n    var e = Ff.call(t, Zt),\n      r = t[Zt];\n    try {\n      t[Zt] = void 0;\n      var n = !0;\n    } catch {}\n    var o = Bf.call(t);\n    return n && (e ? (t[Zt] = r) : delete t[Zt]), o;\n  }\n  var kf = Object.prototype,\n    zf = kf.toString;\n  function Gf(t) {\n    return zf.call(t);\n  }\n  var Hf = '[object Null]',\n    Vf = '[object Undefined]',\n    oo = At ? At.toStringTag : void 0;\n  function te(t) {\n    return t == null ? (t === void 0 ? Vf : Hf) : oo && oo in Object(t) ? Uf(t) : Gf(t);\n  }\n  function Mt(t) {\n    return t != null && typeof t === 'object';\n  }\n  var Wf = '[object Symbol]';\n  function Jf(t) {\n    return typeof t === 'symbol' || (Mt(t) && te(t) == Wf);\n  }\n  function qf(t, e) {\n    for (var r = -1, n = t == null ? 0 : t.length, o = Array(n); ++r < n; ) o[r] = e(t[r], r, t);\n    return o;\n  }\n  var Qf = Array.isArray;\n  const Pt = Qf;\n  var Yf = 1 / 0,\n    ao = At ? At.prototype : void 0,\n    io = ao ? ao.toString : void 0;\n  function so(t) {\n    if (typeof t === 'string') return t;\n    if (Pt(t)) return qf(t, so) + '';\n    if (Jf(t)) return io ? io.call(t) : '';\n    var e = t + '';\n    return e == '0' && 1 / t == -Yf ? '-0' : e;\n  }\n  function yt(t) {\n    var e = typeof t;\n    return t != null && (e == 'object' || e == 'function');\n  }\n  function co(t) {\n    return t;\n  }\n  var Xf = '[object AsyncFunction]',\n    Kf = '[object Function]',\n    Zf = '[object GeneratorFunction]',\n    td = '[object Proxy]';\n  function br(t) {\n    if (!yt(t)) return !1;\n    var e = te(t);\n    return e == Kf || e == Zf || e == Xf || e == td;\n  }\n  var ed = Tt['__core-js_shared__'];\n  const _r = ed;\n  var uo = (function () {\n    var t = /[^.]+$/.exec((_r && _r.keys && _r.keys.IE_PROTO) || '');\n    return t ? 'Symbol(src)_1.' + t : '';\n  })();\n  function rd(t) {\n    return !!uo && uo in t;\n  }\n  var nd = Function.prototype,\n    od = nd.toString;\n  function ad(t) {\n    if (t != null) {\n      try {\n        return od.call(t);\n      } catch {}\n      try {\n        return t + '';\n      } catch {}\n    }\n    return '';\n  }\n  var id = /[\\\\^$.*+?()[\\]{}|]/g,\n    sd = /^\\[object .+?Constructor\\]$/,\n    cd = Function.prototype,\n    ud = Object.prototype,\n    ld = cd.toString,\n    fd = ud.hasOwnProperty,\n    dd = RegExp(\n      '^' +\n        ld\n          .call(fd)\n          .replace(id, '\\\\$&')\n          .replace(/hasOwnProperty|(function).*?(?=\\\\\\()| for .+?(?=\\\\\\])/g, '$1.*?') +\n        '$'\n    );\n  function pd(t) {\n    if (!yt(t) || rd(t)) return !1;\n    var e = br(t) ? dd : sd;\n    return e.test(ad(t));\n  }\n  function hd(t, e) {\n    return t == null ? void 0 : t[e];\n  }\n  function jr(t, e) {\n    var r = hd(t, e);\n    return pd(r) ? r : void 0;\n  }\n  var lo = Object.create,\n    vd = (function () {\n      function t() {}\n      return function (e) {\n        if (!yt(e)) return {};\n        if (lo) return lo(e);\n        t.prototype = e;\n        var r = new t();\n        return (t.prototype = void 0), r;\n      };\n    })();\n  const md = vd;\n  function yd(t, e, r) {\n    switch (r.length) {\n      case 0:\n        return t.call(e);\n      case 1:\n        return t.call(e, r[0]);\n      case 2:\n        return t.call(e, r[0], r[1]);\n      case 3:\n        return t.call(e, r[0], r[1], r[2]);\n    }\n    return t.apply(e, r);\n  }\n  function gd(t, e) {\n    var r = -1,\n      n = t.length;\n    for (e || (e = Array(n)); ++r < n; ) e[r] = t[r];\n    return e;\n  }\n  var bd = 800,\n    _d = 16,\n    jd = Date.now;\n  function Sd(t) {\n    var e = 0,\n      r = 0;\n    return function () {\n      var n = jd(),\n        o = _d - (n - r);\n      if (((r = n), o > 0)) {\n        if (++e >= bd) return arguments[0];\n      } else e = 0;\n      return t.apply(void 0, arguments);\n    };\n  }\n  function Od(t) {\n    return function () {\n      return t;\n    };\n  }\n  var Cd = (function () {\n    try {\n      var t = jr(Object, 'defineProperty');\n      return t({}, '', {}), t;\n    } catch {}\n  })();\n  const ge = Cd;\n  var wd = ge\n      ? function (t, e) {\n          return ge(t, 'toString', { configurable: !0, enumerable: !1, value: Od(e), writable: !0 });\n        }\n      : co,\n    Ed = Sd(wd);\n  const Nd = Ed;\n  var $d = 9007199254740991,\n    Td = /^(?:0|[1-9]\\d*)$/;\n  function fo(t, e) {\n    var r = typeof t;\n    return (\n      (e = e == null ? $d : e), !!e && (r == 'number' || (r != 'symbol' && Td.test(t))) && t > -1 && t % 1 == 0 && t < e\n    );\n  }\n  function Sr(t, e, r) {\n    e == '__proto__' && ge ? ge(t, e, { configurable: !0, enumerable: !0, value: r, writable: !0 }) : (t[e] = r);\n  }\n  function be(t, e) {\n    return t === e || (t !== t && e !== e);\n  }\n  var Ad = Object.prototype,\n    Md = Ad.hasOwnProperty;\n  function Pd(t, e, r) {\n    var n = t[e];\n    (!(Md.call(t, e) && be(n, r)) || (r === void 0 && !(e in t))) && Sr(t, e, r);\n  }\n  function xd(t, e, r, n) {\n    var o = !r;\n    r || (r = {});\n    for (var a = -1, s = e.length; ++a < s; ) {\n      var i = e[a],\n        c = n ? n(r[i], t[i], i, r, t) : void 0;\n      c === void 0 && (c = t[i]), o ? Sr(r, i, c) : Pd(r, i, c);\n    }\n    return r;\n  }\n  var po = Math.max;\n  function Dd(t, e, r) {\n    return (\n      (e = po(e === void 0 ? t.length - 1 : e, 0)),\n      function () {\n        for (var n = arguments, o = -1, a = po(n.length - e, 0), s = Array(a); ++o < a; ) s[o] = n[e + o];\n        o = -1;\n        for (var i = Array(e + 1); ++o < e; ) i[o] = n[o];\n        return (i[e] = r(s)), yd(t, this, i);\n      }\n    );\n  }\n  function Rd(t, e) {\n    return Nd(Dd(t, e, co), t + '');\n  }\n  var Id = 9007199254740991;\n  function ho(t) {\n    return typeof t === 'number' && t > -1 && t % 1 == 0 && t <= Id;\n  }\n  function Or(t) {\n    return t != null && ho(t.length) && !br(t);\n  }\n  function Ld(t, e, r) {\n    if (!yt(r)) return !1;\n    var n = typeof e;\n    return (n == 'number' ? Or(r) && fo(e, r.length) : n == 'string' && e in r) ? be(r[e], t) : !1;\n  }\n  function Fd(t) {\n    return Rd(function (e, r) {\n      var n = -1,\n        o = r.length,\n        a = o > 1 ? r[o - 1] : void 0,\n        s = o > 2 ? r[2] : void 0;\n      for (\n        a = t.length > 3 && typeof a === 'function' ? (o--, a) : void 0,\n          s && Ld(r[0], r[1], s) && ((a = o < 3 ? void 0 : a), (o = 1)),\n          e = Object(e);\n        ++n < o;\n\n      ) {\n        var i = r[n];\n        i && t(e, i, n, a);\n      }\n      return e;\n    });\n  }\n  var Bd = Object.prototype;\n  function vo(t) {\n    var e = t && t.constructor,\n      r = (typeof e === 'function' && e.prototype) || Bd;\n    return t === r;\n  }\n  function Ud(t, e) {\n    for (var r = -1, n = Array(t); ++r < t; ) n[r] = e(r);\n    return n;\n  }\n  var kd = '[object Arguments]';\n  function mo(t) {\n    return Mt(t) && te(t) == kd;\n  }\n  var yo = Object.prototype,\n    zd = yo.hasOwnProperty,\n    Gd = yo.propertyIsEnumerable,\n    Hd = mo(\n      (function () {\n        return arguments;\n      })()\n    )\n      ? mo\n      : function (t) {\n          return Mt(t) && zd.call(t, 'callee') && !Gd.call(t, 'callee');\n        };\n  const Cr = Hd;\n  function Vd() {\n    return !1;\n  }\n  var go = typeof b === 'object' && b && !b.nodeType && b,\n    bo = go && typeof module === 'object' && module && !module.nodeType && module,\n    Wd = bo && bo.exports === go,\n    _o = Wd ? Tt.Buffer : void 0,\n    Jd = _o ? _o.isBuffer : void 0,\n    qd = Jd || Vd;\n  const jo = qd;\n  var Qd = '[object Arguments]',\n    Yd = '[object Array]',\n    Xd = '[object Boolean]',\n    Kd = '[object Date]',\n    Zd = '[object Error]',\n    tp = '[object Function]',\n    ep = '[object Map]',\n    rp = '[object Number]',\n    np = '[object Object]',\n    op = '[object RegExp]',\n    ap = '[object Set]',\n    ip = '[object String]',\n    sp = '[object WeakMap]',\n    cp = '[object ArrayBuffer]',\n    up = '[object DataView]',\n    lp = '[object Float32Array]',\n    fp = '[object Float64Array]',\n    dp = '[object Int8Array]',\n    pp = '[object Int16Array]',\n    hp = '[object Int32Array]',\n    vp = '[object Uint8Array]',\n    mp = '[object Uint8ClampedArray]',\n    yp = '[object Uint16Array]',\n    gp = '[object Uint32Array]',\n    T = {};\n  (T[lp] = T[fp] = T[dp] = T[pp] = T[hp] = T[vp] = T[mp] = T[yp] = T[gp] = !0),\n    (T[Qd] =\n      T[Yd] =\n      T[cp] =\n      T[Xd] =\n      T[up] =\n      T[Kd] =\n      T[Zd] =\n      T[tp] =\n      T[ep] =\n      T[rp] =\n      T[np] =\n      T[op] =\n      T[ap] =\n      T[ip] =\n      T[sp] =\n        !1);\n  function bp(t) {\n    return Mt(t) && ho(t.length) && !!T[te(t)];\n  }\n  function _p(t) {\n    return function (e) {\n      return t(e);\n    };\n  }\n  var So = typeof b === 'object' && b && !b.nodeType && b,\n    ee = So && typeof module === 'object' && module && !module.nodeType && module,\n    jp = ee && ee.exports === So,\n    wr = jp && ro.process,\n    Sp = (function () {\n      try {\n        var t = ee && ee.require && ee.require('util').types;\n        return t || (wr && wr.binding && wr.binding('util'));\n      } catch {}\n    })();\n  const Oo = Sp;\n  var Co = Oo && Oo.isTypedArray,\n    Op = Co ? _p(Co) : bp;\n  const wo = Op;\n  var Cp = Object.prototype,\n    wp = Cp.hasOwnProperty;\n  function Ep(t, e) {\n    var r = Pt(t),\n      n = !r && Cr(t),\n      o = !r && !n && jo(t),\n      a = !r && !n && !o && wo(t),\n      s = r || n || o || a,\n      i = s ? Ud(t.length, String) : [],\n      c = i.length;\n    for (var u in t)\n      (e || wp.call(t, u)) &&\n        !(\n          s &&\n          (u == 'length' ||\n            (o && (u == 'offset' || u == 'parent')) ||\n            (a && (u == 'buffer' || u == 'byteLength' || u == 'byteOffset')) ||\n            fo(u, c))\n        ) &&\n        i.push(u);\n    return i;\n  }\n  function Np(t, e) {\n    return function (r) {\n      return t(e(r));\n    };\n  }\n  function $p(t) {\n    var e = [];\n    if (t != null) for (var r in Object(t)) e.push(r);\n    return e;\n  }\n  var Tp = Object.prototype,\n    Ap = Tp.hasOwnProperty;\n  function Mp(t) {\n    if (!yt(t)) return $p(t);\n    var e = vo(t),\n      r = [];\n    for (var n in t) (n == 'constructor' && (e || !Ap.call(t, n))) || r.push(n);\n    return r;\n  }\n  function Eo(t) {\n    return Or(t) ? Ep(t, !0) : Mp(t);\n  }\n  var Pp = jr(Object, 'create');\n  const re = Pp;\n  function xp() {\n    (this.__data__ = re ? re(null) : {}), (this.size = 0);\n  }\n  function Dp(t) {\n    var e = this.has(t) && delete this.__data__[t];\n    return (this.size -= e ? 1 : 0), e;\n  }\n  var Rp = '__lodash_hash_undefined__',\n    Ip = Object.prototype,\n    Lp = Ip.hasOwnProperty;\n  function Fp(t) {\n    var e = this.__data__;\n    if (re) {\n      var r = e[t];\n      return r === Rp ? void 0 : r;\n    }\n    return Lp.call(e, t) ? e[t] : void 0;\n  }\n  var Bp = Object.prototype,\n    Up = Bp.hasOwnProperty;\n  function kp(t) {\n    var e = this.__data__;\n    return re ? e[t] !== void 0 : Up.call(e, t);\n  }\n  var zp = '__lodash_hash_undefined__';\n  function Gp(t, e) {\n    var r = this.__data__;\n    return (this.size += this.has(t) ? 0 : 1), (r[t] = re && e === void 0 ? zp : e), this;\n  }\n  function gt(t) {\n    var e = -1,\n      r = t == null ? 0 : t.length;\n    for (this.clear(); ++e < r; ) {\n      var n = t[e];\n      this.set(n[0], n[1]);\n    }\n  }\n  (gt.prototype.clear = xp),\n    (gt.prototype.delete = Dp),\n    (gt.prototype.get = Fp),\n    (gt.prototype.has = kp),\n    (gt.prototype.set = Gp);\n  function Hp() {\n    (this.__data__ = []), (this.size = 0);\n  }\n  function _e(t, e) {\n    for (var r = t.length; r--; ) if (be(t[r][0], e)) return r;\n    return -1;\n  }\n  var Vp = Array.prototype,\n    Wp = Vp.splice;\n  function Jp(t) {\n    var e = this.__data__,\n      r = _e(e, t);\n    if (r < 0) return !1;\n    var n = e.length - 1;\n    return r == n ? e.pop() : Wp.call(e, r, 1), --this.size, !0;\n  }\n  function qp(t) {\n    var e = this.__data__,\n      r = _e(e, t);\n    return r < 0 ? void 0 : e[r][1];\n  }\n  function Qp(t) {\n    return _e(this.__data__, t) > -1;\n  }\n  function Yp(t, e) {\n    var r = this.__data__,\n      n = _e(r, t);\n    return n < 0 ? (++this.size, r.push([t, e])) : (r[n][1] = e), this;\n  }\n  function ot(t) {\n    var e = -1,\n      r = t == null ? 0 : t.length;\n    for (this.clear(); ++e < r; ) {\n      var n = t[e];\n      this.set(n[0], n[1]);\n    }\n  }\n  (ot.prototype.clear = Hp),\n    (ot.prototype.delete = Jp),\n    (ot.prototype.get = qp),\n    (ot.prototype.has = Qp),\n    (ot.prototype.set = Yp);\n  var Xp = jr(Tt, 'Map');\n  const No = Xp;\n  function Kp() {\n    (this.size = 0), (this.__data__ = { hash: new gt(), map: new (No || ot)(), string: new gt() });\n  }\n  function Zp(t) {\n    var e = typeof t;\n    return e == 'string' || e == 'number' || e == 'symbol' || e == 'boolean' ? t !== '__proto__' : t === null;\n  }\n  function je(t, e) {\n    var r = t.__data__;\n    return Zp(e) ? r[typeof e === 'string' ? 'string' : 'hash'] : r.map;\n  }\n  function th(t) {\n    var e = je(this, t).delete(t);\n    return (this.size -= e ? 1 : 0), e;\n  }\n  function eh(t) {\n    return je(this, t).get(t);\n  }\n  function rh(t) {\n    return je(this, t).has(t);\n  }\n  function nh(t, e) {\n    var r = je(this, t),\n      n = r.size;\n    return r.set(t, e), (this.size += r.size == n ? 0 : 1), this;\n  }\n  function xt(t) {\n    var e = -1,\n      r = t == null ? 0 : t.length;\n    for (this.clear(); ++e < r; ) {\n      var n = t[e];\n      this.set(n[0], n[1]);\n    }\n  }\n  (xt.prototype.clear = Kp),\n    (xt.prototype.delete = th),\n    (xt.prototype.get = eh),\n    (xt.prototype.has = rh),\n    (xt.prototype.set = nh);\n  function $o(t) {\n    return t == null ? '' : so(t);\n  }\n  var oh = Np(Object.getPrototypeOf, Object);\n  const To = oh;\n  var ah = '[object Object]',\n    ih = Function.prototype,\n    sh = Object.prototype,\n    Ao = ih.toString,\n    ch = sh.hasOwnProperty,\n    uh = Ao.call(Object);\n  function Dt(t) {\n    if (!Mt(t) || te(t) != ah) return !1;\n    var e = To(t);\n    if (e === null) return !0;\n    var r = ch.call(e, 'constructor') && e.constructor;\n    return typeof r === 'function' && r instanceof r && Ao.call(r) == uh;\n  }\n  function lh(t, e, r) {\n    var n = -1,\n      o = t.length;\n    e < 0 && (e = -e > o ? 0 : o + e),\n      (r = r > o ? o : r),\n      r < 0 && (r += o),\n      (o = e > r ? 0 : (r - e) >>> 0),\n      (e >>>= 0);\n    for (var a = Array(o); ++n < o; ) a[n] = t[n + e];\n    return a;\n  }\n  function fh(t, e, r) {\n    var n = t.length;\n    return (r = r === void 0 ? n : r), !e && r >= n ? t : lh(t, e, r);\n  }\n  var dh = '\\\\ud800-\\\\udfff',\n    ph = '\\\\u0300-\\\\u036f',\n    hh = '\\\\ufe20-\\\\ufe2f',\n    vh = '\\\\u20d0-\\\\u20ff',\n    mh = ph + hh + vh,\n    yh = '\\\\ufe0e\\\\ufe0f',\n    gh = '\\\\u200d',\n    bh = RegExp('[' + gh + dh + mh + yh + ']');\n  function Mo(t) {\n    return bh.test(t);\n  }\n  function _h(t) {\n    return t.split('');\n  }\n  var Po = '\\\\ud800-\\\\udfff',\n    jh = '\\\\u0300-\\\\u036f',\n    Sh = '\\\\ufe20-\\\\ufe2f',\n    Oh = '\\\\u20d0-\\\\u20ff',\n    Ch = jh + Sh + Oh,\n    wh = '\\\\ufe0e\\\\ufe0f',\n    Eh = '[' + Po + ']',\n    Er = '[' + Ch + ']',\n    Nr = '\\\\ud83c[\\\\udffb-\\\\udfff]',\n    Nh = '(?:' + Er + '|' + Nr + ')',\n    xo = '[^' + Po + ']',\n    Do = '(?:\\\\ud83c[\\\\udde6-\\\\uddff]){2}',\n    Ro = '[\\\\ud800-\\\\udbff][\\\\udc00-\\\\udfff]',\n    $h = '\\\\u200d',\n    Io = Nh + '?',\n    Lo = '[' + wh + ']?',\n    Th = '(?:' + $h + '(?:' + [xo, Do, Ro].join('|') + ')' + Lo + Io + ')*',\n    Ah = Lo + Io + Th,\n    Mh = '(?:' + [xo + Er + '?', Er, Do, Ro, Eh].join('|') + ')',\n    Ph = RegExp(Nr + '(?=' + Nr + ')|' + Mh + Ah, 'g');\n  function xh(t) {\n    return t.match(Ph) || [];\n  }\n  function Dh(t) {\n    return Mo(t) ? xh(t) : _h(t);\n  }\n  function Rh(t) {\n    return function (e) {\n      e = $o(e);\n      var r = Mo(e) ? Dh(e) : void 0,\n        n = r ? r[0] : e.charAt(0),\n        o = r ? fh(r, 1).join('') : e.slice(1);\n      return n[t]() + o;\n    };\n  }\n  var Ih = Rh('toUpperCase');\n  const Lh = Ih;\n  function Fh(t) {\n    return Lh($o(t).toLowerCase());\n  }\n  function Bh() {\n    (this.__data__ = new ot()), (this.size = 0);\n  }\n  function Uh(t) {\n    var e = this.__data__,\n      r = e.delete(t);\n    return (this.size = e.size), r;\n  }\n  function kh(t) {\n    return this.__data__.get(t);\n  }\n  function zh(t) {\n    return this.__data__.has(t);\n  }\n  var Gh = 200;\n  function Hh(t, e) {\n    var r = this.__data__;\n    if (r instanceof ot) {\n      var n = r.__data__;\n      if (!No || n.length < Gh - 1) return n.push([t, e]), (this.size = ++r.size), this;\n      r = this.__data__ = new xt(n);\n    }\n    return r.set(t, e), (this.size = r.size), this;\n  }\n  function Rt(t) {\n    var e = (this.__data__ = new ot(t));\n    this.size = e.size;\n  }\n  (Rt.prototype.clear = Bh),\n    (Rt.prototype.delete = Uh),\n    (Rt.prototype.get = kh),\n    (Rt.prototype.has = zh),\n    (Rt.prototype.set = Hh);\n  var Fo = typeof b === 'object' && b && !b.nodeType && b,\n    Bo = Fo && typeof module === 'object' && module && !module.nodeType && module,\n    Vh = Bo && Bo.exports === Fo,\n    Uo = Vh ? Tt.Buffer : void 0,\n    ko = Uo ? Uo.allocUnsafe : void 0;\n  function Wh(t, e) {\n    if (e) return t.slice();\n    var r = t.length,\n      n = ko ? ko(r) : new t.constructor(r);\n    return t.copy(n), n;\n  }\n  var Jh = Tt.Uint8Array;\n  const zo = Jh;\n  function qh(t) {\n    var e = new t.constructor(t.byteLength);\n    return new zo(e).set(new zo(t)), e;\n  }\n  function Qh(t, e) {\n    var r = e ? qh(t.buffer) : t.buffer;\n    return new t.constructor(r, t.byteOffset, t.length);\n  }\n  function Yh(t) {\n    return typeof t.constructor === 'function' && !vo(t) ? md(To(t)) : {};\n  }\n  function Xh(t) {\n    return function (e, r, n) {\n      for (var o = -1, a = Object(e), s = n(e), i = s.length; i--; ) {\n        var c = s[t ? i : ++o];\n        if (r(a[c], c, a) === !1) break;\n      }\n      return e;\n    };\n  }\n  var Kh = Xh();\n  const Zh = Kh;\n  function $r(t, e, r) {\n    ((r !== void 0 && !be(t[e], r)) || (r === void 0 && !(e in t))) && Sr(t, e, r);\n  }\n  function tv(t) {\n    return Mt(t) && Or(t);\n  }\n  function Tr(t, e) {\n    if (!(e === 'constructor' && typeof t[e] === 'function') && e != '__proto__') return t[e];\n  }\n  function ev(t) {\n    return xd(t, Eo(t));\n  }\n  function rv(t, e, r, n, o, a, s) {\n    var i = Tr(t, r),\n      c = Tr(e, r),\n      u = s.get(c);\n    if (u) {\n      $r(t, r, u);\n      return;\n    }\n    var l = a ? a(i, c, r + '', t, e, s) : void 0,\n      d = l === void 0;\n    if (d) {\n      var f = Pt(c),\n        p = !f && jo(c),\n        h = !f && !p && wo(c);\n      (l = c),\n        f || p || h\n          ? Pt(i)\n            ? (l = i)\n            : tv(i)\n            ? (l = gd(i))\n            : p\n            ? ((d = !1), (l = Wh(c, !0)))\n            : h\n            ? ((d = !1), (l = Qh(c, !0)))\n            : (l = [])\n          : Dt(c) || Cr(c)\n          ? ((l = i), Cr(i) ? (l = ev(i)) : (!yt(i) || br(i)) && (l = Yh(c)))\n          : (d = !1);\n    }\n    d && (s.set(c, l), o(l, c, n, a, s), s.delete(c)), $r(t, r, l);\n  }\n  function Go(t, e, r, n, o) {\n    t !== e &&\n      Zh(\n        e,\n        function (a, s) {\n          if ((o || (o = new Rt()), yt(a))) rv(t, e, s, r, Go, n, o);\n          else {\n            var i = n ? n(Tr(t, s), a, s + '', t, e, o) : void 0;\n            i === void 0 && (i = a), $r(t, s, i);\n          }\n        },\n        Eo\n      );\n  }\n  var nv = Fd(function (t, e, r) {\n    Go(t, e, r);\n  });\n  const ov = nv;\n  function av(t) {\n    const e = t.prototype;\n    return !!(e && e.isReactComponent);\n  }\n  function iv(t) {\n    var n, o;\n    const r = typeof Symbol === 'function' && Symbol.for ? Symbol.for('react.forward_ref') : 60112;\n    return (\n      (t == null ? void 0 : t.$$typeof) === r ||\n      ((n = t == null ? void 0 : t.prototype) == null ? void 0 : n.isReactComponent) ||\n      ((o = t == null ? void 0 : t.prototype) == null ? void 0 : o.setState) ||\n      t._forwardRef\n    );\n  }\n  function sv(t) {\n    class e extends D.Component {\n      render() {\n        return D.createElement(t, this.props);\n      }\n    }\n    return (e.displayName = t.displayName), e;\n  }\n  const It = (t, e) => {\n      const r = (n) => {\n        const a = `\n    ${Object.keys(e).map((s) => `const ${s} = $$context['${s}'];`).join(`\n`)}\n    return ${n};\n    `;\n        return new Function('$$context', a)(e);\n      };\n      try {\n        return r(t);\n      } catch (n) {\n        console.warn(n);\n        const o = `[${t}] expression run failed`;\n        return console.warn(o), null;\n      }\n    },\n    cv = (t, e, r) =>\n      function (...o) {\n        try {\n          const a = `\n        var f = ${t};\n        var args = Array.from(arguments);\n        var __$$storeManager__ = args.pop();\n        var $$context = args.pop();\n        $$context.stateManager = __$$storeManager__.getStateSnapshot();\n        return f.apply(f, args)\n      `;\n          new Function(a)(...o, e, r);\n        } catch (a) {\n          console.warn(a);\n        }\n      },\n    Ho = (t, e) => {\n      const r = {};\n      return (\n        e.forEach((n, o) => {\n          r[n] = t[o];\n        }),\n        r\n      );\n    },\n    Vo = (t) => {\n      const e = {};\n      return (\n        Object.keys(t).forEach((r) => {\n          let n = r.replace('-webkit', 'Webkit');\n          (n = n.replace('-ms', 'ms')), (n = n.replace('-moz', 'Moz')), (n = n.replace('-o', 'O'));\n          let o = n.split('-');\n          o.length >= 2 && (o = o.map((a, s) => (s !== 0 ? Fh(a) : a))), (e[o.join('')] = t[r]);\n        }),\n        e\n      );\n    },\n    Wo = (t) => {\n      let e = '';\n      return (\n        Object.keys(t).forEach((r) => {\n          e += `${r}:${t[r]};`;\n        }),\n        e\n      );\n    },\n    Se = 'DYNAMIC',\n    uv = ['$$context', '$$nodeModel'],\n    Jo = (t) => {\n      let e;\n      const r = new Set(),\n        n = (c, u) => {\n          const l = typeof c === 'function' ? c(e) : c;\n          if (!Object.is(l, e)) {\n            const d = e;\n            (e = (u != null ? u : typeof l !== 'object') ? l : Object.assign({}, e, l)), r.forEach((f) => f(e, d));\n          }\n        },\n        o = () => e,\n        i = {\n          setState: n,\n          getState: o,\n          subscribe: (c) => (r.add(c), () => r.delete(c)),\n          destroy: () => {\n            (({ BASE_URL: '/', MODE: 'production', DEV: !1, PROD: !0 } && 'production') !== 'production' &&\n              console.warn(\n                '[DEPRECATED] The `destroy` method will be unsupported in a future version. Instead use unsubscribe function returned by subscribe. Everything will be garbage-collected if store is garbage-collected.'\n              ),\n              r.clear());\n          },\n        };\n      return (e = t(n, o, i)), i;\n    },\n    lv = (t) => (t ? Jo(t) : Jo);\n  var fv = (t) => (\n    ({ BASE_URL: '/', MODE: 'production', DEV: !1, PROD: !0 } && 'production') !== 'production' &&\n      console.warn(\n        \"[DEPRECATED] Default export is deprecated. Instead use import { createStore } from 'zustand/vanilla'.\"\n      ),\n    lv(t)\n  );\n  class dv {\n    constructor() {\n      g(this, 'storeMap', new Map());\n    }\n    addStore(e, r) {\n      const n = fv(r);\n      return this.storeMap.set(e, n), (n.name = e), n;\n    }\n    setStore(e, r) {\n      this.storeMap.set(e, r);\n    }\n    removeStore(e) {\n      this.storeMap.delete(e);\n    }\n    getStore(e) {\n      return this.storeMap.get(e);\n    }\n    connect(e, r) {\n      const n = this.storeMap.get(e);\n      return n ? n.subscribe(r) : (console.warn('store not exits'), () => {});\n    }\n    getStateSnapshot() {\n      const e = {};\n      return (\n        this.storeMap.forEach((r, n) => {\n          e[n] = {\n            state: r.getState(),\n            updateState: (o) => {\n              r.setState(o);\n            },\n          };\n        }),\n        e\n      );\n    }\n    destroy() {\n      this.storeMap.forEach((e) => {\n        e.destroy();\n      }),\n        (this.storeMap = new Map());\n    }\n  }\n  class qo {\n    constructor() {\n      g(this, 'renderMode', 'normal');\n      g(this, 'components', {});\n      g(this, 'storeManager', new dv());\n      g(this, 'runtimeComponentCache', new Map());\n      g(this, 'onGetRef');\n      g(this, 'onGetComponent');\n      g(this, 'onComponentMount');\n      g(this, 'onComponentDestroy');\n      g(this, 'processNodeConfigHook');\n    }\n    getComponent(e) {\n      var o;\n      const r = e.value.componentName;\n      let n = this.components[r] || (() => `Component [${r}] not found`);\n      return (\n        iv(n) || ((n = sv(n)), (this.components[r] = n)),\n        this.onGetComponent && (n = (o = this.onGetComponent) == null ? void 0 : o.call(this, n, e)),\n        n\n      );\n    }\n    getContext(e = {}, r) {\n      let n = e;\n      return r && ((n = { ...e }), (n.__proto__ = r || null)), n;\n    }\n    pageRender(\n      e,\n      {\n        components: r,\n        onGetRef: n,\n        $$context: o = {},\n        onGetComponent: a,\n        onComponentMount: s,\n        onComponentDestroy: i,\n        renderMode: c,\n        processNodeConfigHook: u,\n      }\n    ) {\n      (this.renderMode = c),\n        (this.components = r),\n        (this.onGetRef = n),\n        (this.onGetComponent = a),\n        (this.onComponentMount = s),\n        (this.onComponentDestroy = i),\n        (this.processNodeConfigHook = u);\n      const l = e.value.componentsTree,\n        d = this.getComponent(l),\n        f = this.convertModelToComponent(d, e.value.componentsTree),\n        p = {},\n        h = l.props;\n      return (\n        Object.keys(h).forEach((y) => {\n          p[y] = h[y].value;\n        }),\n        (p.$$context = o),\n        this.render(f, p)\n      );\n    }\n    transformProps(e = {}, { $$context: r }) {\n      const n = e,\n        o = (s) => {\n          if (Array.isArray(s)) return s.map((i) => o(i));\n          if (vr(s)) return o(s.value);\n          if ($f(s)) {\n            const i = s.value,\n              c = i.value;\n            if (!c) return console.warn('slot value is null, this maybe cause some error, pls check it', e), () => {};\n            const u = (l) => {\n              const d = `${l.id}-${Se}`;\n              if (this.runtimeComponentCache.get(l.id))\n                return { key: d, component: this.runtimeComponentCache.get(l.id) };\n              const f = this.getComponent(l),\n                p = this.convertModelToComponent(f, l),\n                h = i.params || [];\n              return {\n                component: (...S) => {\n                  const w = Ho(S, h),\n                    I = this.getContext({ params: w }, r);\n                  return this.render(p, { $$context: I, key: d });\n                },\n                key: d,\n              };\n            };\n            if (Array.isArray(c)) {\n              const l = c == null ? void 0 : c.map((d) => u(d));\n              return (...d) =>\n                l.map((f) =>\n                  av(f.component)\n                    ? O.default.createElement(f.component, { $$context: r, key: f.key })\n                    : f.component(...d)\n                );\n            } else return u(c).component;\n          } else {\n            if (vt(s)) return It(s.value, r || {});\n            if (Ef(s)) return cv(s.value, r, this.storeManager);\n            if (Dt(s)) {\n              let i = s;\n              vr(s) && (i = s.value);\n              const c = i,\n                u = {};\n              return (\n                Object.keys(i).forEach((l) => {\n                  u[l] = o(c[l]);\n                }),\n                u\n              );\n            } else return s;\n          }\n        },\n        a = {};\n      return (\n        Object.keys(n).forEach((s) => {\n          const i = n[s];\n          a[s] = o(i);\n        }),\n        a\n      );\n    }\n    collectSpecialProps(e = {}, r) {\n      const n = [],\n        o = (a, s) => {\n          let i = s;\n          vr(s) && (i = s.value),\n            r(i)\n              ? n.push({ keyPath: a, val: i })\n              : Pt(i)\n              ? i.forEach((c, u) => {\n                  o([...a, String(u)], c);\n                })\n              : Dt(i) &&\n                Object.keys(i).forEach((c) => {\n                  o([...a, c], i[c]);\n                });\n        };\n      return o(['$root'], e), n;\n    }\n    convertModelToComponent(e, r) {\n      const n = this;\n      class o extends O.default.Component {\n        constructor(i) {\n          super(i);\n          g(this, '_CONDITION', !0);\n          g(this, '_DESIGN_BOX', !1);\n          g(this, '_NODE_MODEL', r);\n          g(this, '_NODE_ID', r.id);\n          g(this, 'UNIQUE_ID', `${r.id}_${Z()}`);\n          g(this, 'targetComponentRef');\n          g(this, 'listenerHandle', []);\n          g(this, 'storeState');\n          g(this, 'staticState', {});\n          g(this, 'storeListenDisposeLint', []);\n          g(this, 'domHeader');\n          g(this, 'mediaStyleDomMap', {});\n          g(this, 'updateState', (i) => {\n            this.storeState.setState(i), this.forceUpdate();\n          });\n          g(this, 'getStyleDomById', (i) => {\n            const c = this.mediaStyleDomMap;\n            let u = c[i];\n            return u || ((u = document.createElement('style')), (u.type = 'text/css'), (c[i] = u)), (u.id = i), u;\n          });\n          g(this, 'addMediaCSS', () => {\n            var u;\n            let i = this.domHeader;\n            if (\n              (i || ((i = (u = document.getElementsByTagName('head')) == null ? void 0 : u[0]), (this.domHeader = i)),\n              !this.domHeader)\n            )\n              return;\n            const c = this._NODE_MODEL.value.css;\n            !c ||\n              c.value.forEach((l) => {\n                var p;\n                const d = `${this.UNIQUE_ID}_${l.state}`;\n                let f = `.${c.class}`;\n                if ((l.state !== 'normal' && (f = `${f}:${l.state}`), Object.keys(l.style).length !== 0)) {\n                  const h = this.getStyleDomById(d);\n                  (h.innerText = `${f} { ${Wo(l.style)} }`), i == null || i.appendChild(h);\n                }\n                (p = l.media) != null &&\n                  p.length &&\n                  l.media.forEach((h) => {\n                    const y = `${d}_${h.type}_${h.value}`,\n                      _ = this.getStyleDomById(y);\n                    (_.media = `screen and (${h.type}:${h.value}px)`),\n                      (_.innerHTML = `${f} { ${Wo(h.style)} }`),\n                      i == null || i.appendChild(_);\n                  });\n              });\n          });\n          g(this, 'removeMediaCSS', () => {\n            const i = this.mediaStyleDomMap;\n            Object.keys(i).forEach((c) => {\n              var u;\n              (u = this.domHeader) == null || u.removeChild(i[c]);\n            });\n          });\n          g(this, 'rebuildNode', () => {\n            this.storeListenDisposeLint.forEach((i) => i()),\n              this.removeMediaCSS(),\n              this.connectStore(),\n              this.addMediaCSS(),\n              this.forceUpdate();\n          });\n          (this.targetComponentRef = O.default.createRef()), (this.state = r.value.state || {});\n          const c = r.value.nodeName || r.id,\n            u = n.storeManager.getStore(c);\n          u\n            ? ((this.storeState = u), u.setState({ ...(r.value.state || {}) }))\n            : (this.storeState = n.storeManager.addStore(c, () => ({ ...(r.value.state || {}) }))),\n            this.storeState.subscribe((l) => {\n              this.setState({ ...l });\n            }),\n            this.connectStore();\n        }\n        connectStore() {\n          const i = n.collectSpecialProps(r.props, (f) => !!vt(f)),\n            c = n.collectSpecialProps({ css: r.value.css, class: r.value.classNames }, (f) => !!vt(f)),\n            u = [...i, ...c]\n              .map((f) => {\n                const p = f.val,\n                  y = /\\$\\$context.stateManager\\.(.+?)\\./gim.exec(p.value);\n                return y != null && y.length ? y[1] : '';\n              })\n              .filter(Boolean),\n            l = Array.from(new Set(u)),\n            d = [];\n          l.length &&\n            l.forEach((f) => {\n              n.storeManager.getStore(f) ||\n                (n.storeManager.addStore(f, () => ({})), console.log(n.storeManager, f, 'not exits'));\n              const h = n.storeManager.connect(f, () => {\n                this.forceUpdate();\n              });\n              d.push(h);\n            }),\n            (this.storeListenDisposeLint = d);\n        }\n        componentDidMount() {\n          var c;\n          this.addMediaCSS(),\n            n.onGetRef && n.onGetRef(this.targetComponentRef, r, this),\n            (c = n.onComponentMount) == null || c.call(n, this, r);\n          const i = () => {\n            n.storeManager.setStore(r.value.nodeName || r.id, this.storeState),\n              this.storeState.setState({ ...this.state, ...(r.value.state || {}) }),\n              this.rebuildNode();\n          };\n          r.onChange(i);\n        }\n        componentWillUnmount() {\n          var i;\n          this.storeListenDisposeLint.forEach((c) => c()),\n            this.removeMediaCSS(),\n            (i = n.onComponentDestroy) == null || i.call(n, this, r);\n        }\n        render() {\n          var Lt, N, ne;\n          const { $$context: i, ...c } = this.props,\n            u = { key: r.id, ...r.props, ...c },\n            l = { state: this.state || {}, updateState: this.updateState, staticState: this.staticState };\n          r.value.componentName === Nt.ROOT_CONTAINER &&\n            ((l.globalState = this.state), (l.updateGlobalState = this.updateState)),\n            (l.stateManager = n.storeManager.getStateSnapshot());\n          const d = n.getContext(l, i),\n            f = r.value.loop;\n          let p = [];\n          if (f && f.open) {\n            this.targetComponentRef.current = [];\n            let F = f.data || [];\n            if (vt(f.data)) {\n              const it = f.data;\n              F = It(it.value, d || {});\n            }\n            return (\n              (p = F.map((...it) => {\n                var ra, na;\n                const oe = it[1],\n                  Ft = [f.forName || 'item', f.forIndex || 'index'],\n                  Ar = Ho(it, Ft);\n                let Mr = 'loopData';\n                f.name && (Mr = `${Mr}${f.name}`);\n                const ae = n.getContext({ [Mr]: Ar, staticState: this.staticState }, d),\n                  tt = n.transformProps(u, { $$context: ae }),\n                  Cv =\n                    ((ra = r.value.classNames) == null\n                      ? void 0\n                      : ra.map((Q) => {\n                          var we;\n                          const Ce = Q.name;\n                          return (\n                            vt(Q.status) ? It(String(((we = Q.status) == null ? void 0 : we.value) || ''), ae) : !1\n                          )\n                            ? Ce\n                            : '';\n                        })) || [];\n                let Pr = `${(na = tt.className) != null ? na : ''} ${Cv.join(' ')}`.trim();\n                r.value.css && (Pr = `${r.value.css.class} ${Pr}`.trim()), (tt.className = Pr);\n                const wv = n.transformProps(r.value.style, { $$context: ae });\n                r.value.style && (tt.style = Vo(wv || {}));\n                const { children: Oe } = tt;\n                let xr = [];\n                if (Oe !== void 0) delete tt.children, (xr = Array.isArray(Oe) ? Oe : [Oe]);\n                else {\n                  const Q = [];\n                  r.value.children.forEach((oa, we) => {\n                    const Ev = n.buildComponent(oa, { $$context: ae, idx: we });\n                    Q.push(Ev);\n                  }),\n                    (xr = Q);\n                }\n                if (((tt.key = `${tt.key}-${oe}`), vt(f.key))) {\n                  const Q = f.key,\n                    Ce = It(Q.value, ae || {});\n                  tt.key += `-${Ce}`;\n                }\n                return (\n                  (tt.ref = (Q) => {\n                    (this.targetComponentRef.current = this.targetComponentRef.current || []),\n                      (this.targetComponentRef.current[oe] = Q);\n                  }),\n                  n.render(e, tt, ...xr)\n                );\n              })),\n              p\n            );\n          }\n          const h = n.transformProps(u, { $$context: d }),\n            { children: y } = h;\n          let _ = [];\n          if (y !== void 0) delete h.children, (_ = Array.isArray(y) ? y : [y]);\n          else {\n            const F = [];\n            r.value.children.forEach((oe, Ft) => {\n              const Ar = n.buildComponent(oe, { $$context: d, idx: Ft });\n              F.push(Ar);\n            }),\n              (_ = F);\n          }\n          h.ref = this.targetComponentRef;\n          const S =\n            ((Lt = r.value.classNames) == null\n              ? void 0\n              : Lt.map((F) => {\n                  var Ft;\n                  const it = F.name;\n                  return (vt(F.status) ? It(((Ft = F.status) == null ? void 0 : Ft.value) || '', d) : !1) ? it : '';\n                })) || [];\n          let w = `${(N = h.className) != null ? N : ''} ${S.join(' ')}`.trim();\n          r.value.css && (w = `${r.value.css.class} ${w}`.trim()), (h.className = w);\n          const I = n.transformProps(r.value.style, { $$context: d });\n          r.value.style && (h.style = Vo(I || {}));\n          let M = (ne = r.value.condition) != null ? ne : !0;\n          typeof M !== 'boolean' && (M = It(M.value, d || {}));\n          let L = { condition: M, props: h };\n          n.processNodeConfigHook && (L = n.processNodeConfigHook(L, r));\n          const q = n.render(e, L.props, ..._);\n          return (\n            (this._CONDITION = L.condition),\n            L.condition ? q : O.default.createElement('div', { style: { display: 'none' } }, q)\n          );\n        }\n      }\n      return g(o, '__CP_TYPE__', Se), (o.displayName = `${r.value.componentName}Dynamic`), o;\n    }\n    buildComponent(e, { $$context: r = {} }) {\n      const n = this.runtimeComponentCache;\n      return typeof e === 'string'\n        ? this.render(e)\n        : Nf(e)\n        ? (({ currentNode: a }) => {\n            const s = a.value.id;\n            let i = null;\n            if (n.get(s)) i = n.get(s);\n            else {\n              const l = this.getComponent(a);\n              i = this.convertModelToComponent(l, a);\n            }\n            !n.get(s) && this.renderMode !== 'design' && n.set(s, i);\n            const c = `${s}-${Se}`,\n              u = { $$context: r, $$nodeModel: e, key: c };\n            return this.render(i, u);\n          })({ currentNode: e })\n        : void 0;\n    }\n    render(e, r = {}, ...n) {\n      return typeof e === 'string' || typeof e === 'number'\n        ? String(e)\n        : (uv.forEach((a) => {\n            a in r && e.__CP_TYPE__ !== Se && delete r[a];\n          }),\n          O.default.createElement(e, r, ...n));\n    }\n    clear() {\n      this.runtimeComponentCache.clear(), this.storeManager.destroy();\n    }\n  }\n  const pv = Rr(new qo()),\n    hv = [\n      'a',\n      'abbr',\n      'acronym',\n      'address',\n      'applet',\n      'area',\n      'article',\n      'aside',\n      'audio',\n      'b',\n      'base',\n      'basefont',\n      'bdi',\n      'bdo',\n      'bgsound',\n      'big',\n      'blink',\n      'blockquote',\n      'body',\n      'br',\n      'button',\n      'canvas',\n      'caption',\n      'center',\n      'cite',\n      'code',\n      'col',\n      'colgroup',\n      'command',\n      'content',\n      'data',\n      'datalist',\n      'dd',\n      'del',\n      'details',\n      'dfn',\n      'dialog',\n      'dir',\n      'div',\n      'dl',\n      'dt',\n      'element',\n      'em',\n      'embed',\n      'fieldset',\n      'figcaption',\n      'figure',\n      'font',\n      'footer',\n      'form',\n      'frame',\n      'frameset',\n      'h1',\n      'h2',\n      'h3',\n      'h4',\n      'h5',\n      'h6',\n      'head',\n      'header',\n      'hgroup',\n      'hr',\n      'html',\n      'i',\n      'iframe',\n      'image',\n      'img',\n      'input',\n      'ins',\n      'isindex',\n      'kbd',\n      'keygen',\n      'label',\n      'legend',\n      'li',\n      'link',\n      'listing',\n      'main',\n      'map',\n      'mark',\n      'marquee',\n      'math',\n      'menu',\n      'menuitem',\n      'meta',\n      'meter',\n      'multicol',\n      'nav',\n      'nextid',\n      'nobr',\n      'noembed',\n      'noframes',\n      'noscript',\n      'object',\n      'ol',\n      'optgroup',\n      'option',\n      'output',\n      'p',\n      'param',\n      'picture',\n      'plaintext',\n      'pre',\n      'progress',\n      'q',\n      'rb',\n      'rbc',\n      'rp',\n      'rt',\n      'rtc',\n      'ruby',\n      's',\n      'samp',\n      'script',\n      'search',\n      'section',\n      'select',\n      'shadow',\n      'slot',\n      'small',\n      'source',\n      'spacer',\n      'span',\n      'strike',\n      'strong',\n      'style',\n      'sub',\n      'summary',\n      'sup',\n      'svg',\n      'table',\n      'tbody',\n      'td',\n      'template',\n      'textarea',\n      'tfoot',\n      'th',\n      'thead',\n      'time',\n      'title',\n      'tr',\n      'track',\n      'tt',\n      'u',\n      'ul',\n      'var',\n      'video',\n      'wbr',\n      'xmp',\n    ],\n    at = (t) => {\n      const e = {};\n      return (\n        t.forEach((r) => {\n          e[r.key] = r.value;\n        }),\n        e\n      );\n    },\n    vv = [...hv, ...ff].reduce(\n      (t, e) => (\n        (t[e] = ({ children: r, $$attributes: n = [], ...o }) => {\n          let a = r;\n          return Array.isArray(r) || (a = [r]), O.default.createElement(e, { ...o, ...at(n) }, ...a);\n        }),\n        t\n      ),\n      {}\n    ),\n    mv = {\n      RootContainer: ({ children: t }) => t,\n      ...vv,\n      CContainer: ({ children: t, $$attributes: e = [], afterMount: r, beforeDestroy: n, ...o }) => {\n        let a = t;\n        return (\n          Array.isArray(t) || (a = [t]),\n          D.useEffect(\n            () => (\n              r == null || r(o),\n              () => {\n                n == null || n(o);\n              }\n            ),\n            []\n          ),\n          O.default.createElement('div', { ...o, ...at(e) }, ...a)\n        );\n      },\n      CVideo: ({ children: t, $$attributes: e = [], ...r }) => {\n        let n = t;\n        return Array.isArray(t) || (n = [t]), O.default.createElement('video', { ...r, ...at(e) }, ...n);\n      },\n      CAudio: ({ children: t, $$attributes: e = [], ...r }) => {\n        let n = t;\n        return Array.isArray(t) || (n = [t]), O.default.createElement('video', { ...r, ...at(e) }, ...n);\n      },\n      CBlock: ({ children: t, width: e, height: r, $$attributes: n = [], ...o }) => {\n        let a = t;\n        Array.isArray(t) || (a = [t]), (a = a.filter((u) => u !== void 0));\n        const { style: s = {}, ...i } = at(n),\n          c = { height: r, width: e, ...s, ...(o.style || {}) };\n        return O.default.createElement('div', { ...o, ...i, style: c }, ...a);\n      },\n      CImage: ({ children: t, $$attributes: e = [], ...r }) => {\n        let n = t;\n        return Array.isArray(t) || (n = [t]), O.default.createElement('img', { ...r, ...at(e) }, ...n);\n      },\n      CText: ({ children: t, $$attributes: e = [], content: r, ...n }) =>\n        O.default.createElement('span', { ...n, ...at(e) }, r),\n      CCanvas: ({ children: t, $$attributes: e = [], ...r }) => {\n        let n = t;\n        return Array.isArray(t) || (n = [t]), O.default.createElement('canvas', { ...r, ...at(e) }, ...n);\n      },\n      CNativeTag: ({ children: t, $$attributes: e = [], htmlTag: r = 'div', ...n }) => {\n        let o = t;\n        return Array.isArray(t) || (o = [t]), O.default.createElement(r, { ...n, ...at(e) }, ...o);\n      },\n    };\n  class yv {\n    constructor() {\n      g(this, 'refMap', new Map());\n    }\n    get(e) {\n      return this.refMap.get(e);\n    }\n    add(e, r) {\n      this.refMap.set(e, r);\n    }\n    remove(e) {\n      this.refMap.delete(e);\n    }\n    destroy() {\n      this.refMap.clear();\n    }\n  }\n  class Qo extends O.default.Component {\n    constructor(r) {\n      super(r);\n      g(this, 'refManager');\n      g(this, 'dynamicComponentInstanceMap', new Map());\n      g(this, 'onGetRef', (r, n, o) => {\n        var a, s;\n        (s = (a = this.props).onGetRef) == null || s.call(a, r, n, o),\n          this.dynamicComponentInstanceMap.set(n.id, o),\n          this.refManager.add(n.value.refId || n.id, r);\n      });\n      g(this, 'rerender', (r) => {\n        if ((this.props.adapter.clear(), (r == null ? void 0 : r.nodeType) === 'PAGE' && r))\n          this.setState({ pageModel: r });\n        else if (Dt(r) && eo(r)) {\n          const n = r;\n          this.setState({ pageModel: new ye(n, { materials: this.state.pageModel.materialsModel.rawValue }) });\n        }\n      });\n      (this.state = { pageModel: r.pageModel || new ye(r.page) }), (this.refManager = new yv());\n    }\n    getPageModel() {\n      return this.state.pageModel;\n    }\n    componentDidMount() {\n      const { render: r } = this.props;\n      r && (r.ref.current = this);\n    }\n    componentWillUnmount() {\n      this.refManager.destroy();\n    }\n    render() {\n      const { props: r } = this,\n        { adapter: n, onGetComponent: o, onComponentDestroy: a, onComponentMount: s } = r,\n        { pageModel: i } = this.state;\n      if (!i) return console.warn('pageModel is null'), null;\n      const c = { ...mv, ...r.components };\n      return n.pageRender(i, {\n        libs: {},\n        components: c,\n        onGetRef: this.onGetRef,\n        onGetComponent: o,\n        onComponentMount: s,\n        onComponentDestroy: a,\n        $$context: { refs: this.refManager },\n        renderMode: r.renderMode,\n        processNodeConfigHook: r.processNodeConfigHook,\n      });\n    }\n  }\n  const gv = () => {\n    const t = D.useRef(null);\n    return {\n      ref: t,\n      rerender: function (...e) {\n        t.current && t.current.rerender(...e);\n      },\n    };\n  };\n  class bv extends O.default.Component {\n    constructor(r) {\n      super(r);\n      g(this, 'onDoubleClick', () => {\n        this.setState({ hasError: !1, error: null });\n      });\n      this.state = { hasError: !1, error: '' };\n    }\n    static getDerivedStateFromError(r) {\n      return { hasError: !0, error: r };\n    }\n    componentDidCatch(r, n) {\n      var o, a;\n      (a = (o = this.props).onError) == null || a.call(o, n);\n    }\n    render() {\n      if (this.state.hasError) {\n        const { onDoubleClick: r } = this,\n          n = this.props.node.value;\n        console.error(this.props.node, this.props.children);\n        const o = O.default.createElement(\n          'div',\n          {\n            style: {\n              backgroundColor: 'rgb(255 206 215 / 13%)',\n              padding: '5px',\n              color: '#ff0000b0',\n              textAlign: 'center',\n              fontSize: '12px',\n            },\n          },\n          'Render error, node id: ',\n          n.id,\n          ', node name\\uFF1A',\n          n.title,\n          ' component name\\uFF1A',\n          n.title || n.componentName,\n          O.default.createElement('p', null, 'msg: ', String(this.state.error)),\n          O.default.createElement(\n            'button',\n            {\n              onDoubleClick: r,\n              style: {\n                border: '1px solid rgba(100,100,100,0.1)',\n                backgroundColor: '#fff',\n                padding: '5px 10px',\n                borderRadius: '2px',\n                color: 'gray',\n                cursor: 'pointer',\n                marginTop: '5px',\n              },\n            },\n            'double click to refresh'\n          ),\n          O.default.createElement('div', { style: { display: 'none' } })\n        );\n        return O.default.createElement(this.props.targetComponent, { onlyRenderChild: !0 }, o);\n      }\n      return this.props.children;\n    }\n  }\n  class Yo {\n    constructor() {\n      g(this, 'instanceMap', new Map());\n    }\n    get(e) {\n      return this.instanceMap.get(e);\n    }\n    add(e, r) {\n      const n = this.instanceMap.get(e);\n      n ? n.push(r) : this.instanceMap.set(e, [r]);\n    }\n    remove(e, r) {\n      const n = this.instanceMap.get(e);\n      if (r !== void 0 && Array.isArray(n)) {\n        const o = n.filter((a) => a !== r);\n        this.instanceMap.set(e, o);\n      } else this.instanceMap.delete(e);\n    }\n    destroy() {\n      this.instanceMap.clear();\n    }\n  }\n  const Xo = (t) => {\n    const { node: e } = t,\n      r = D.useMemo(() => {\n        var c, u;\n        const i = (u = (c = e.material) == null ? void 0 : c.value) == null ? void 0 : u.isContainer;\n        return Dt(i) ? i : { placeholder: 'Drag the component to place it', width: '100%', height: '100%', style: {} };\n      }, [t.node]),\n      { placeholder: n, height: o, width: a, style: s } = r;\n    return O.default.createElement(\n      'div',\n      {\n        style: {\n          display: 'flex',\n          alignItems: 'center',\n          justifyContent: 'center',\n          backgroundColor: 'rgba(200,200,200,0.1)',\n          border: '1px solid rgba(0,0,0,0.1)',\n          borderRadius: '2px',\n          fontSize: '14px',\n          color: 'gray',\n          cursor: 'default',\n          minWidth: '300px',\n          minHeight: '50px',\n          width: a,\n          height: o,\n          ...s,\n        },\n      },\n      n\n    );\n  };\n  class _v extends O.default.Component {\n    constructor(r) {\n      super(r);\n      g(this, 'instanceManager', new Yo());\n      g(this, 'renderRef');\n      g(this, 'dropPlaceholder', Xo);\n      g(this, 'onGetComponent', (r, n) => {\n        const o = this;\n        class a extends O.default.Component {\n          constructor() {\n            super(...arguments);\n            g(this, '_DESIGN_BOX', !0);\n            g(this, '_NODE_MODEL', n);\n            g(this, '_NODE_ID', n.id);\n            g(this, '_UNIQUE_ID', `${n.id}_${Z()}`);\n            g(this, '_STATUS');\n          }\n          componentDidMount() {\n            o.instanceManager.add(n.id, this);\n          }\n          componentWillUnmount() {\n            (this._STATUS = 'DESTROY'), o.instanceManager.remove(n.id, this);\n          }\n          render() {\n            var p;\n            const { children: c = [], onlyRenderChild: u, ...l } = this.props;\n            let d = c;\n            return (\n              Pt(c) || (d = [c]),\n              !d.filter(Boolean).length &&\n                (((p = n.material) == null ? void 0 : p.value.isContainer) ||\n                  n.value.componentName === Nt.ROOT_CONTAINER) &&\n                d.push(O.default.createElement(o.dropPlaceholder, { node: n })),\n              u ? d : O.default.createElement(r, l, ...d)\n            );\n          }\n        }\n        return O.default.forwardRef(function (i, c) {\n          return O.default.createElement(\n            bv,\n            { node: n, targetComponent: a },\n            O.default.createElement(a, { ref: c, ...i })\n          );\n        });\n      });\n      (this.renderRef = O.default.createRef()), r.dropPlaceholder && (this.dropPlaceholder = r.dropPlaceholder);\n    }\n    componentDidMount() {\n      var r, n;\n      (n = (r = this.props).onMount) == null || n.call(r, this);\n    }\n    getPageModel() {\n      var r;\n      return (r = this.renderRef.current) == null ? void 0 : r.state.pageModel;\n    }\n    rerender(r) {\n      var n;\n      return (n = this.renderRef.current) == null ? void 0 : n.rerender(r);\n    }\n    getInstancesById(r, n) {\n      let o = [...(this.instanceManager.get(r) || [])];\n      return n !== void 0 && (o = o.filter((a) => n === (a == null ? void 0 : a._UNIQUE_ID))), o;\n    }\n    getInstanceByDom(r) {\n      const n = Ko(r);\n      if (!n) return null;\n      const o = Zo(n);\n      return (o == null ? void 0 : o.stateNode) || null;\n    }\n    getDomsById(r, n) {\n      const o = this.getInstancesById(r),\n        a = [];\n      return (\n        o == null ||\n          o.forEach((s) => {\n            if ((s == null ? void 0 : s._STATUS) === 'DESTROY') return;\n            const i = sa.findDOMNode(s);\n            if (i && !(i instanceof Text))\n              if (n) {\n                const c = Array.from(i.querySelectorAll(n));\n                a.push(...c);\n              } else a.push(i);\n          }),\n        a\n      );\n    }\n    getDomRectById(r, n) {\n      return this.getDomsById(r, n)\n        .map((s) => (s == null ? void 0 : s.getBoundingClientRect()))\n        .filter(Boolean);\n    }\n    render() {\n      const { props: r, onGetComponent: n } = this,\n        { render: o, ...a } = r;\n      return (\n        o && (o.ref.current = this),\n        O.default.createElement(Qo, {\n          onGetComponent: n,\n          ...a,\n          processNodeConfigHook: (s, i) => {\n            var h, y;\n            if (i.nodeType !== 'NODE') return s;\n            const { props: c, condition: u } = s;\n            let l = { ...c };\n            const d = ((h = i.value.configure) == null ? void 0 : h.devState) || {},\n              f = (y = i.material) == null ? void 0 : y.value.fixedProps;\n            if (f !== void 0) {\n              if (Dt(f)) l = { ...l, ...f };\n              else if (typeof f === 'function') {\n                const _ = f(l);\n                l = { ...l, ..._ };\n              }\n            }\n            let p = u;\n            return d.condition === !1 && (p = d.condition), { props: ov(l, d.props || {}), condition: p };\n          },\n          ref: this.renderRef,\n        })\n      );\n    }\n  }\n  const Ko = (t) => {\n      if (!t) return null;\n      const e =\n        Object.keys(t).find((r) => r.startsWith('__reactInternalInstance$') || r.startsWith('__reactFiber$')) || '';\n      return e ? t[e] : Ko(t.parentElement);\n    },\n    Zo = (t) => {\n      var e;\n      return t ? ((e = t == null ? void 0 : t.stateNode) != null && e._DESIGN_BOX ? t : Zo(t.return)) : null;\n    },\n    jv = () => {\n      const t = D.useRef(null);\n      return {\n        ref: t,\n        rerender: function (...e) {\n          t.current && t.current.rerender(...e);\n        },\n        getInstancesById(e, r) {\n          var n;\n          return ((n = t.current) == null ? void 0 : n.getInstancesById(e, r)) || [];\n        },\n        getInstanceByDom(e) {\n          var r;\n          return ((r = t.current) == null ? void 0 : r.getInstanceByDom(e)) || null;\n        },\n        getDomsById(e, r) {\n          var n;\n          return ((n = t.current) == null ? void 0 : n.getDomsById(e, r)) || [];\n        },\n        getDomRectById(e, r) {\n          var n;\n          return ((n = t.current) == null ? void 0 : n.getDomRectById(e, r)) || [];\n        },\n      };\n    };\n  var Sv =\n      typeof globalThis < 'u'\n        ? globalThis\n        : typeof window < 'u'\n        ? window\n        : typeof global < 'u'\n        ? global\n        : typeof self < 'u'\n        ? self\n        : {},\n    ta = { exports: {} };\n  (function (t, e) {\n    (function (r, n) {\n      t.exports = n();\n    })(Sv, function () {\n      var r = function () {},\n        n = {},\n        o = {},\n        a = {};\n      function s(f, p) {\n        f = f.push ? f : [f];\n        var h = [],\n          y = f.length,\n          _ = y,\n          S,\n          w,\n          I,\n          M;\n        for (\n          S = function (L, q) {\n            q.length && h.push(L), _--, _ || p(h);\n          };\n          y--;\n\n        ) {\n          if (((w = f[y]), (I = o[w]), I)) {\n            S(w, I);\n            continue;\n          }\n          (M = a[w] = a[w] || []), M.push(S);\n        }\n      }\n      function i(f, p) {\n        if (f) {\n          var h = a[f];\n          if (((o[f] = p), !!h)) for (; h.length; ) h[0](f, p), h.splice(0, 1);\n        }\n      }\n      function c(f, p) {\n        f.call && (f = { success: f }), p.length ? (f.error || r)(p) : (f.success || r)(f);\n      }\n      function u(f, p, h, y) {\n        var _ = document,\n          S = h.async,\n          w = (h.numRetries || 0) + 1,\n          I = h.before || r,\n          M = f.replace(/[\\?|#].*$/, ''),\n          L = f.replace(/^(css|img|module|nomodule)!/, ''),\n          q,\n          Lt,\n          N;\n        if (((y = y || 0), /(^css!|\\.css$)/.test(M)))\n          (N = _.createElement('link')),\n            (N.rel = 'stylesheet'),\n            (N.href = L),\n            (q = 'hideFocus' in N),\n            q && N.relList && ((q = 0), (N.rel = 'preload'), (N.as = 'style'));\n        else if (/(^img!|\\.(png|gif|jpg|svg|webp)$)/.test(M)) (N = _.createElement('img')), (N.src = L);\n        else if (\n          ((N = _.createElement('script')),\n          (N.src = L),\n          (N.async = S === void 0 ? !0 : S),\n          (Lt = 'noModule' in N),\n          /^module!/.test(M))\n        ) {\n          if (!Lt) return p(f, 'l');\n          N.type = 'module';\n        } else if (/^nomodule!/.test(M) && Lt) return p(f, 'l');\n        (N.onload =\n          N.onerror =\n          N.onbeforeload =\n            function (ne) {\n              var F = ne.type[0];\n              if (q)\n                try {\n                  N.sheet.cssText.length || (F = 'e');\n                } catch (it) {\n                  it.code != 18 && (F = 'e');\n                }\n              if (F == 'e') {\n                if (((y += 1), y < w)) return u(f, p, h, y);\n              } else if (N.rel == 'preload' && N.as == 'style') return (N.rel = 'stylesheet');\n              p(f, F, ne.defaultPrevented);\n            }),\n          I(f, N) !== !1 && _.head.appendChild(N);\n      }\n      function l(f, p, h) {\n        f = f.push ? f : [f];\n        var y = f.length,\n          _ = y,\n          S = [],\n          w,\n          I;\n        for (\n          w = function (M, L, q) {\n            if ((L == 'e' && S.push(M), L == 'b'))\n              if (q) S.push(M);\n              else return;\n            y--, y || p(S);\n          },\n            I = 0;\n          I < _;\n          I++\n        )\n          u(f[I], w, h);\n      }\n      function d(f, p, h) {\n        var y, _;\n        if ((p && p.trim && (y = p), (_ = (y ? h : p) || {}), y)) {\n          if (y in n) throw 'LoadJS';\n          n[y] = !0;\n        }\n        function S(w, I) {\n          l(\n            f,\n            function (M) {\n              c(_, M), w && c({ success: w, error: I }, M), i(y, M);\n            },\n            _\n          );\n        }\n        if (_.returnPromise) return new Promise(S);\n        S();\n      }\n      return (\n        (d.ready = function (p, h) {\n          return (\n            s(p, function (y) {\n              c(h, y);\n            }),\n            d\n          );\n        }),\n        (d.done = function (p) {\n          i(p, []);\n        }),\n        (d.reset = function () {\n          (n = {}), (o = {}), (a = {});\n        }),\n        (d.isDefined = function (p) {\n          return p in n;\n        }),\n        d\n      );\n    });\n  })(ta);\n  const ea = ta.exports;\n  class Ov {\n    constructor(e) {\n      g(this, 'assets');\n      g(this, 'loadStatus');\n      g(this, '_onSuccessList', []);\n      g(this, '_onErrorList', []);\n      (this.assets = e), (this.loadStatus = 'INIT');\n    }\n    load() {\n      const e = this.assets || [],\n        r = [];\n      for (let n = 0; n < e.length; n++) {\n        const o = e[n];\n        o.id || (o.id = Z()), r.push(o.id);\n        const a = o.resources.map((s) => s.src);\n        ea(a, o.id, { async: !1 });\n      }\n      if (e.length === 0) {\n        this._onSuccessList.forEach((n) => n());\n        return;\n      }\n      return (\n        setTimeout(() => {\n          ea.ready(r, {\n            success: () => {\n              this._onSuccessList.forEach((n) => n());\n            },\n            error: (n) => {\n              this._onErrorList.forEach((o) => o(n));\n            },\n          });\n        }, 0),\n        this\n      );\n    }\n    onSuccess(e) {\n      return this._onSuccessList.push(e), this;\n    }\n    onError(e) {\n      return this._onErrorList.push(e), this;\n    }\n  }\n  (b.AssetLoader = Ov),\n    (b.ComponentInstanceManager = Yo),\n    (b.DefaultDropPlaceholder = Xo),\n    (b.DefineReactAdapter = qo),\n    (b.DesignRender = _v),\n    (b.ReactAdapter = pv),\n    (b.Render = Qo),\n    (b.getAdapter = Rr),\n    (b.useDesignRender = jv),\n    (b.useRender = gv),\n    Object.defineProperties(b, { __esModule: { value: !0 }, [Symbol.toStringTag]: { value: 'Module' } });\n});\n//# sourceMappingURL=index.umd.js.map\n"
  },
  {
    "path": "packages/layout/src/_dev_/dev.css",
    "content": "* {\n  margin: 0;\n  padding: 0;\n  box-sizing: border-box;\n}\nhtml,\nbody,\n#root {\n  width: 100%;\n  height: 100%;\n}\n"
  },
  {
    "path": "packages/layout/src/_dev_/dev.tsx",
    "content": "/* eslint-disable react-refresh/only-export-components */\nimport React, { useEffect, useRef, useState } from 'react';\nimport ReactDOM from 'react-dom/client';\nimport ReactDOMAll from 'react-dom';\nimport { BasePage, Material } from '@chamn/demo-page';\nimport { Layout, LayoutDragAndDropExtraDataType, LayoutPropsType } from '..';\nimport * as antD from 'antd';\nimport { Sensor } from '../core/dragAndDrop/sensor';\nimport { AssetPackage, CNode, CPage } from '@chamn/model';\n\nimport './dev.css';\nimport { collectVariable, flatObject } from '@chamn/render';\n\n(window as any).React = React;\n(window as any).ReactDOM = ReactDOMAll;\n(window as any).ReactDOMClient = ReactDOM;\n\nconst assets: AssetPackage[] = [\n  {\n    package: 'antd',\n    globalName: 'antd',\n    resources: [\n      {\n        src: 'https://cdn.jsdelivr.net/npm/dayjs@1.11.12/dayjs.min.js',\n      },\n      {\n        src: 'https://cdn.jsdelivr.net/npm/antd@5.20.1/dist/antd.min.js',\n      },\n      {\n        src: 'https://cdn.jsdelivr.net/npm/antd@5.20.1/dist/reset.min.css',\n      },\n    ],\n  },\n];\n\nconst components = {\n  ...antD,\n};\n\nconst beforeInitRender: LayoutPropsType['beforeInitRender'] = async ({ iframe }) => {\n  const subWin = iframe.getWindow();\n  if (!subWin) {\n    return;\n  }\n  subWin.React = React;\n  (subWin as any).ReactDOM = ReactDOMAll;\n  (subWin as any).ReactDOMClient = ReactDOM;\n};\n\nconst customRender: LayoutPropsType['customRender'] = async ({\n  iframe: iframeContainer,\n  assets,\n  page,\n  pageModel,\n  ready,\n}) => {\n  await iframeContainer.loadUrl('/src/_dev_/render.html');\n\n  const iframeWindow = iframeContainer.getWindow()!;\n  const iframeDoc = iframeContainer.getDocument()!;\n  const IframeReact = iframeWindow.React!;\n  const IframeReactDOM = iframeWindow.ReactDOMClient!;\n  const CRender = iframeWindow.CRender!;\n\n  // 从子窗口获取物料对象\n  const componentCollection = collectVariable(assets, iframeWindow);\n  const components = flatObject(componentCollection);\n  const App = IframeReact?.createElement(CRender.DesignRender, {\n    adapter: CRender?.ReactAdapter,\n    page: page,\n    pageModel: pageModel,\n    components,\n    requestAPI: async (params) => {\n      return console.log(222, params);\n    },\n    onMount: (designRenderInstance) => {\n      ready(designRenderInstance);\n    },\n  });\n\n  IframeReactDOM.createRoot(iframeDoc.getElementById('app')!).render(App);\n};\n\nconst App = () => {\n  const [_page] = useState<any>(BasePage);\n  const [ghostView, setGhostView] = useState(<div>213</div>);\n  const [pageModel] = useState<any>(\n    new CPage(BasePage, {\n      materials: [\n        ...Material,\n        {\n          title: '块',\n          componentName: 'CText',\n          snippets: [],\n        } as any,\n        {\n          title: '块',\n          componentName: 'CBlock',\n          disableEditorDragDom: true,\n          snippets: [],\n        } as any,\n      ],\n    })\n  );\n\n  const leftBoxRef = useRef<HTMLDivElement>(null);\n  const layoutRef = useRef<Layout>(null);\n  useEffect(() => {\n    layoutRef.current?.ready(() => {\n      console.log('layoutRef', layoutRef);\n      const boxSensor = new Sensor<LayoutDragAndDropExtraDataType>({\n        name: 'widgetListBox',\n        container: leftBoxRef.current!,\n        mainDocument: document,\n      });\n      boxSensor.setCanDrag(async (eventObj) => {\n        const pageModel = layoutRef.current?.getPageModel();\n\n        const newNode = pageModel?.createNode({\n          id: '11xzxczxczxc1',\n          componentName: 'Button',\n          props: {\n            onClick: {\n              type: 'FUNCTION',\n              value: `function click(e) {\n                console.log(112312312311, e);\n              }`,\n            },\n          },\n          children: ['insertData'],\n        });\n        return {\n          ...eventObj,\n          extraData: {\n            dropType: 'NEW_ADD',\n            dragNode: newNode,\n          },\n        };\n      });\n\n      layoutRef.current?.dnd.registerSensor(boxSensor);\n\n      boxSensor.setCanDrop(async (eventObj) => {\n        const newNode = new CNode({\n          id: 'newAdd',\n          componentName: 'Button',\n          children: ['new add'],\n        });\n        return {\n          ...eventObj,\n          extraData: {\n            dropNode: newNode,\n          },\n        };\n      });\n\n      boxSensor.emitter.on('dragStart', (eventObj) => {\n        setGhostView(<div>{eventObj.extraData?.dragNode?.value.componentName}</div>);\n        if (eventObj.currentSensor === boxSensor) {\n          layoutRef.current?.clearSelectNode();\n        }\n      });\n\n      boxSensor.emitter.on('drop', (eventObj) => {\n        const pageModel = layoutRef.current?.getPageModel();\n        const extraData = eventObj.extraData as LayoutDragAndDropExtraDataType;\n        if (!extraData.dropNode) {\n          console.warn('cancel drop, because drop node is null');\n          return;\n        }\n        if (extraData.dropType === 'NEW_ADD') {\n          pageModel?.addNode(extraData.dragNode as CNode, extraData.dropNode, 'BEFORE');\n        } else {\n          if (extraData.dropNode?.id === extraData.dragNode?.id) {\n            return;\n          }\n          if (extraData.dropPosInfo?.pos === 'before') {\n            pageModel?.moveNodeById(extraData.dragNode?.id || '', extraData?.dropNode?.id || '', 'BEFORE');\n          } else {\n            pageModel?.moveNodeById(extraData.dragNode?.id || '', extraData?.dropNode?.id || '', 'AFTER');\n          }\n        }\n        console.log('选中元素', extraData.dragNode?.id || '', extraData?.dropNode?.id, extraData);\n        layoutRef.current?.selectNode(extraData.dragNode?.id || '');\n\n        console.log(pageModel?.export());\n      });\n      const pageModel = layoutRef.current?.getPageModel();\n      console.log('pageModel?.export()', pageModel?.export());\n    });\n  }, []);\n  return (\n    <div\n      style={{\n        width: '100%',\n        height: '100%',\n        padding: '20px',\n        display: 'flex',\n      }}\n    >\n      <div\n        ref={leftBoxRef}\n        style={{ border: '1px solid rgba(0,0,0, 0.3)', padding: '10px', borderRadius: '2px', width: '300px' }}\n        onClick={() => {\n          layoutRef.current?.selectNode('32');\n        }}\n      >\n        Tool Box\n      </div>\n      <div\n        style={{\n          width: '100%',\n          height: '100%',\n          margin: '0 auto',\n          overflow: 'hidden',\n        }}\n      >\n        <Layout\n          ref={layoutRef}\n          // page={page}\n          pageModel={pageModel}\n          components={components}\n          // selectToolBar={<div>123</div>}\n          assets={assets}\n          ghostView={ghostView}\n          beforeInitRender={beforeInitRender}\n          customRender={customRender}\n          selectToolbarView={\n            <div\n              style={{\n                width: '100px',\n                height: '20px',\n                backgroundColor: 'red',\n              }}\n            >\n              toolBar\n            </div>\n          }\n        />\n      </div>\n    </div>\n  );\n};\n\nReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(<App />);\n"
  },
  {
    "path": "packages/layout/src/_dev_/render.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <link rel=\"icon\" type=\"image/svg+xml\" href=\"/vite.svg\" />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n  <title>Low-code engine</title>\n  <style>\n    #root {\n      height: 100%;\n    }\n  </style>\n  <script>\n    window.React = window.parent.React;\n    window.ReactDOM = window.parent.ReactDOM;\n    window.ReactDOMClient = window.parent.ReactDOMClient;\n  </script>\n</head>\n\n<body>\n  <div id=\"app\"></div>\n  <script type=\"module\" src=\"./render.tsx\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "packages/layout/src/_dev_/render.tsx",
    "content": "import React from 'react';\nimport ReactDOM from 'react-dom/client';\nimport ReactDOMAll from 'react-dom';\nimport * as antd from 'antd';\n\nimport * as CRender from '@chamn/render';\n\nwindow.React = React;\n(window as any).ReactDOM = ReactDOMAll;\n(window as any).ReactDOMClient = ReactDOM;\n\n(window as any).CRender = CRender;\n\n(window as any).antd = antd;\n"
  },
  {
    "path": "packages/layout/src/build-script-env.d.ts",
    "content": "/// <reference types=\"@chamn/build-script/client\" />\n"
  },
  {
    "path": "packages/layout/src/components/DefaultDropPlaceholder/index.tsx",
    "content": "import { CNode, CRootNode } from '@chamn/model';\nimport React from 'react';\n\nexport const DefaultDropPlaceholder = (props: {\n  node: CNode | CRootNode;\n  placeholder?: string;\n  width?: number;\n  height?: number;\n}) => {\n  const { placeholder = 'Drag the component to place it', width = 300, height = 50 } = props;\n  return (\n    <span\n      style={{\n        margin: 0,\n        padding: 0,\n        display: 'flex',\n        alignItems: 'center',\n        justifyContent: 'center',\n        backgroundColor: 'rgba(200,200,200,0.1)',\n        border: '1px solid rgba(0,0,0,0.1)',\n        borderRadius: '2px',\n        fontSize: '14px',\n        color: 'gray',\n        cursor: 'default',\n        width: width,\n        height: height,\n        boxSizing: 'border-box',\n      }}\n    >\n      {placeholder}\n    </span>\n  );\n};\n"
  },
  {
    "path": "packages/layout/src/components/DropAnchor/index.tsx",
    "content": "import React, { useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react';\nimport styles from './style.module.scss';\nimport { animationFrame, isDOM } from '../../utils';\nimport { RenderInstance } from '@chamn/render';\nimport { DragAndDropEventType } from '../../core/dragAndDrop';\nimport clsx from 'clsx';\nimport { DropPosType } from '@chamn/model';\nimport { LayoutDragAndDropExtraDataType } from '../../types/dragAndDrop';\n\nexport type HighlightCanvasRefType = {\n  update: () => void;\n};\n\nexport type DropAnchorPropsType<E = LayoutDragAndDropExtraDataType> = {\n  instance: RenderInstance;\n  toolRenderView?: React.ReactNode;\n  mouseEvent: DragAndDropEventType<E>['dragging'] | null;\n  style?: React.CSSProperties;\n  getRef?: (ref: React.RefObject<HighlightCanvasRefType>) => void;\n  onRefDestroy?: (ref: React.RefObject<HighlightCanvasRefType>) => void;\n  onDropInfoChange?: (dropInfo: DropPosType | null) => void;\n  dropInfo: DropPosType;\n  customDropViewRender?: (props: DropAnchorPropsType<E>) => React.ReactElement;\n};\n\nexport function DropAnchor(props: DropAnchorPropsType<LayoutDragAndDropExtraDataType>) {\n  const {\n    instance,\n    toolRenderView,\n    getRef,\n    onRefDestroy,\n    style,\n    mouseEvent,\n    onDropInfoChange,\n    dropInfo,\n    customDropViewRender,\n  } = props;\n  const [styleObj, setStyleObj] = useState<Record<string, string>>({});\n  const [posClassName, setPosClassName] = useState<string[]>([]);\n  const [rect, setRect] = useState<DOMRect>();\n  const ref = useRef<HighlightCanvasRefType>(null);\n  const [toolBoxSize, setToolBoxSize] = useState({\n    width: 0,\n    height: 0,\n  });\n  const toolBoxRef = useRef<HTMLDivElement>(null);\n  const [targetDom, setTargetDom] = useState<HTMLElement>();\n  useEffect(() => {\n    getRef?.(ref);\n    if (instance?._STATUS === 'DESTROY') {\n      return;\n    }\n    const dom = instance.getDom();\n    if (isDOM(dom)) {\n      setTargetDom(dom as unknown as HTMLElement);\n    }\n    return () => {\n      onRefDestroy?.(ref);\n    };\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n\n  const updateRef = useRef<() => void>();\n  updateRef.current = () => {\n    const toolBoxDom = toolBoxRef.current;\n    const toolRect = toolBoxDom?.getBoundingClientRect();\n    if (toolRect) {\n      setToolBoxSize({\n        width: toolRect.width,\n        height: toolRect.height,\n      });\n    }\n  };\n  useEffect(() => {\n    const handle = animationFrame(() => {\n      updateRef.current?.();\n    });\n\n    return () => {\n      handle();\n    };\n  }, []);\n\n  // 绘制落点\n  const updatePos = useCallback(() => {\n    let instanceDom: HTMLElement | null = null;\n    if (instance?._STATUS === 'DESTROY') {\n      return;\n    }\n\n    const dom = instance.getDom();\n    if (isDOM(dom)) {\n      instanceDom = dom as unknown as HTMLElement;\n      setTargetDom(instanceDom);\n    } else {\n      return;\n    }\n\n    if (!mouseEvent) {\n      onDropInfoChange?.(null);\n      return null;\n    }\n\n    const node = instance?._NODE_MODEL;\n    if (!node) {\n      console.warn('node not exits');\n      return;\n    }\n\n    // target node dom rect\n    const tempRect = instanceDom.getBoundingClientRect();\n    setRect(tempRect);\n    // 绘制矩形高亮落点\n    if (dropInfo.pos === 'current') {\n      const transformStr = `translate3d(${tempRect?.left}px, ${tempRect.top}px, 0)`;\n      setStyleObj({\n        width: `${tempRect.width}px`,\n        height: `${tempRect.height}px`,\n        transform: transformStr,\n      });\n    } else {\n      // 绘制线条高亮落点\n      const space = 2;\n      const transformStr = `translate3d(${tempRect?.left - space}px, ${tempRect.top - space}px, 0)`;\n      const tempObj = {\n        width: tempRect?.width + space * 2 + 'px',\n        height: tempRect?.height + space * 2 + 'px',\n        transform: transformStr,\n      };\n      const toolBoxDom = document.getElementById(instance?._UNIQUE_ID || '');\n      if (toolBoxDom) {\n        toolBoxDom.style.transform = transformStr;\n        toolBoxDom.style.width = tempRect?.width + 'px';\n        toolBoxDom.style.height = tempRect?.height + 'px';\n      }\n      setStyleObj(tempObj);\n    }\n\n    const classNameMap = {\n      horizontal: styles.horizontal,\n      vertical: styles.vertical,\n      before: styles.before,\n      after: styles.after,\n      current: styles.current,\n    };\n\n    let newDropInfo = dropInfo;\n    if (mouseEvent?.extraData.dropPosInfo) {\n      newDropInfo = {\n        ...dropInfo,\n        ...mouseEvent?.extraData.dropPosInfo,\n      };\n    }\n    const classList = [classNameMap[dropInfo.direction], classNameMap[dropInfo.pos]];\n    setPosClassName(classList);\n    onDropInfoChange?.(newDropInfo);\n  }, [dropInfo, instance, mouseEvent, onDropInfoChange]);\n\n  useEffect(() => {\n    updatePos();\n  }, [instance, mouseEvent, updatePos]);\n\n  (ref as any).current = {\n    update() {\n      updatePos();\n    },\n  };\n\n  let innerDropView = (\n    <div\n      style={{\n        width: '100%',\n        height: '100%',\n      }}\n      className={clsx([...posClassName])}\n    ></div>\n  );\n\n  if (customDropViewRender) {\n    const Comp = customDropViewRender;\n    innerDropView = <Comp {...props} />;\n  }\n\n  if (!targetDom || !instance) {\n    return <></>;\n  }\n  return (\n    <div\n      className={clsx([styles.highlightBox])}\n      id={instance?._UNIQUE_ID}\n      style={{\n        ...style,\n        opacity: rect ? 1 : 0,\n      }}\n    >\n      {toolRenderView && (\n        <div\n          ref={toolBoxRef}\n          className={styles.toolBox}\n          style={{\n            top: `-${toolBoxSize.height + 5}px`,\n            opacity: toolBoxSize.width ? 1 : 0,\n          }}\n        >\n          {toolRenderView}\n        </div>\n      )}\n      <div\n        style={{\n          ...styleObj,\n        }}\n      >\n        {innerDropView}\n      </div>\n    </div>\n  );\n}\n\nexport function DropAnchorCanvasCore(\n  {\n    instances,\n    toolRenderView,\n    style,\n    mouseEvent,\n    dropInfos,\n    ...restProps\n  }: Omit<DropAnchorPropsType<LayoutDragAndDropExtraDataType>, 'instance' | 'dropInfo'> & {\n    instances: RenderInstance[];\n    dropInfos: DropPosType[];\n  },\n  ref: React.Ref<HighlightCanvasRefType>\n) {\n  const [_, updateRender] = useState(0);\n  const allBoxRef = useRef<React.RefObject<HighlightCanvasRefType>[]>([]);\n  useImperativeHandle(\n    ref,\n    () => {\n      return {\n        update() {\n          updateRender(_ + 1);\n          // 更新所有的高亮框位置\n          allBoxRef.current.forEach((el) => {\n            el.current?.update();\n          });\n        },\n      };\n    },\n    [updateRender, _]\n  );\n  const onRefDestroy = (ref: React.RefObject<HighlightCanvasRefType>) => {\n    const list = allBoxRef.current || [];\n    allBoxRef.current = list.filter((el) => el !== ref);\n  };\n\n  return (\n    <div className={styles.borderDrawBox}>\n      {instances.map((el, index) => {\n        if (!el) {\n          return null;\n        }\n        return (\n          <DropAnchor\n            {...restProps}\n            mouseEvent={mouseEvent}\n            style={style}\n            key={el?._UNIQUE_ID}\n            instance={el}\n            toolRenderView={toolRenderView}\n            dropInfo={dropInfos[index]}\n            getRef={(ref) => {\n              if (ref.current) {\n                allBoxRef.current.push(ref);\n              }\n            }}\n            onRefDestroy={onRefDestroy}\n          />\n        );\n      })}\n    </div>\n  );\n}\n\nexport const DropAnchorCanvas = React.forwardRef(DropAnchorCanvasCore);\n"
  },
  {
    "path": "packages/layout/src/components/DropAnchor/style.module.scss",
    "content": "$anchorColor: #1890ff;\n.highlightBox {\n  position: absolute;\n  will-change: transform, width, height, left, right;\n}\n\n.borderDrawBox {\n  position: absolute;\n  left: 0;\n  top: 0;\n  height: 100%;\n  width: 100%;\n  z-index: 99;\n  pointer-events: none;\n}\n\n.toolBox {\n  position: absolute;\n  right: 0;\n  top: -20px;\n  border: 1px solid black;\n  box-sizing: border-box;\n}\n\n.horizontal {\n  &.before {\n    border-left: 2px solid $anchorColor;\n  }\n  &.after {\n    border-right: 2px solid $anchorColor;\n  }\n}\n\n.vertical {\n  &.before {\n    border-top: 2px solid $anchorColor;\n  }\n  &.after {\n    border-bottom: 2px solid $anchorColor;\n  }\n}\n\n.current {\n  border: none !important;\n  background-color: rgba($color: #0084ff, $alpha: 0.42);\n}\n"
  },
  {
    "path": "packages/layout/src/components/DropAnchor/util.ts",
    "content": "import { DropPosType } from '@chamn/model';\n\n// todo: 位置计算有缺陷， styles 应该取被 drop 的组件来计算\nexport function judgeVertical(styles: CSSStyleDeclaration, parentStyle: CSSStyleDeclaration) {\n  const { display: parentDisplay, flexDirection } = parentStyle;\n  if (parentDisplay === 'flex' && ['row', 'row-reverse'].includes(flexDirection)) {\n    return false;\n  }\n\n  const { display } = styles;\n  if (['inline', 'inline-block', 'float', 'grid'].includes(display)) {\n    return false;\n  }\n\n  return true;\n}\n\nexport function calculateDropPosInfo(params: {\n  point: { x: number; y: number };\n  dom: HTMLElement;\n  innerClass?: string;\n  isContainer?: boolean;\n}): DropPosType {\n  const { point, dom } = params;\n  let pos: DropPosType['pos'];\n  const targetDomStyle = getComputedStyle(dom);\n  let isVertical = true;\n\n  if (dom.parentElement) {\n    const parentStyle = getComputedStyle(dom.parentElement);\n    isVertical = judgeVertical(targetDomStyle, parentStyle);\n  }\n  const mousePos = point;\n  const targetRect = dom.getBoundingClientRect();\n  const targetDomW = targetRect.width;\n  const targetDomH = targetRect.height;\n  const xCenter = targetRect.x + Math.round(targetDomW / 2);\n  const yCenter = targetRect.y + Math.round(targetDomH / 2);\n\n  const hotAreaSpace = 10;\n  if (params.isContainer) {\n    if (\n      mousePos.y > targetRect.y + hotAreaSpace &&\n      mousePos.y < targetRect.y + targetRect.height - hotAreaSpace &&\n      mousePos.x > targetRect.x + hotAreaSpace &&\n      mousePos.x < targetRect.x + targetRect.width - hotAreaSpace\n    ) {\n      pos = 'current';\n      return {\n        pos,\n        direction: 'horizontal',\n      };\n    }\n  }\n\n  if (isVertical) {\n    if (mousePos.y > yCenter) {\n      pos = 'after';\n    } else {\n      pos = 'before';\n    }\n  } else if (mousePos.x > xCenter) {\n    pos = 'after';\n  } else {\n    pos = 'before';\n  }\n\n  return {\n    pos,\n    direction: isVertical ? 'vertical' : 'horizontal',\n  };\n}\n"
  },
  {
    "path": "packages/layout/src/components/HighlightBox/index.tsx",
    "content": "import React, { useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react';\nimport styles from './style.module.scss';\nimport { animationFrame, isDOM } from '../../utils';\nimport { RenderInstance } from '@chamn/render';\n\nexport type HighlightCanvasRefType = {\n  update: () => void;\n};\n\nexport type HighlightBoxPropsType = {\n  instance: RenderInstance;\n  toolbarView?: React.ReactNode;\n  style?: React.CSSProperties;\n  getRef?: (ref: React.RefObject<HighlightCanvasRefType>) => void;\n  onRefDestroy?: (ref: React.RefObject<HighlightCanvasRefType>) => void;\n  children?: React.ReactElement;\n};\n\nexport const HighlightBox = ({\n  instance,\n  toolbarView,\n  getRef,\n  onRefDestroy,\n  style,\n  children,\n}: HighlightBoxPropsType) => {\n  const [styleObj, setStyleObj] = useState<Record<string, string>>();\n  const ref = useRef<HighlightCanvasRefType>(null);\n\n  const toolBoxRef = useRef<HTMLDivElement>(null);\n  const instanceRef = useRef<RenderInstance>();\n  instanceRef.current = instance;\n  const updateTargetDom = (ins: RenderInstance) => {\n    let dom = ins.getDom();\n    const rootSelector = instance._NODE_MODEL.material?.value.rootSelector;\n\n    if (rootSelector && dom) {\n      // 文本节点 注释节点不存在 querySelector 方法\n      dom = (dom.querySelector?.(rootSelector) as HTMLElement) || dom;\n    }\n    if (isDOM(dom)) {\n      return true;\n    }\n    return false;\n  };\n\n  useEffect(() => {\n    const handle = animationFrame(() => {\n      updatePos();\n    });\n\n    getRef?.(ref);\n\n    if (instance?._STATUS !== 'DESTROY') {\n      updateTargetDom(instance);\n    }\n\n    return () => {\n      handle();\n      onRefDestroy?.(ref);\n    };\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n\n  const updateToolBoxPosition = useCallback((targetRect: DOMRect) => {\n    if (toolBoxRef.current) {\n      const contentDom = toolBoxRef.current?.children?.[0] || toolBoxRef.current;\n\n      const toolBoxRect = contentDom.getBoundingClientRect();\n\n      const height = toolBoxRect?.height || 0;\n      // 取最大宽度宽度\n      const width = Math.max(toolBoxRect?.width, targetRect.width) || 0;\n      const isOutsideViewportY = targetRect.top - height < 0;\n      if (isOutsideViewportY) {\n        // 向下取整 + 整个高度  + outline 2px * 2\n        toolBoxRef.current.style.top = `calc( 100% + ${Math.floor(height)}px + 4px)`;\n      } else {\n        toolBoxRef.current.style.top = 'auto';\n      }\n      toolBoxRef.current.style.width = `${width}px`;\n    }\n  }, []);\n\n  const updatePos = useCallback(() => {\n    const tempInstance = instanceRef.current;\n    let instanceDom: HTMLElement | null | undefined = null;\n    if (tempInstance?._STATUS === 'DESTROY') {\n      return;\n    }\n\n    let dom = tempInstance?.getDom();\n    if (!dom) {\n      return;\n    }\n    const rootSelector = instance._NODE_MODEL.material?.value.rootSelector;\n    if (rootSelector) {\n      // 文本节点 注释节点不存在 querySelector 方法\n      dom = (dom?.querySelector?.(rootSelector) as any) || dom;\n    }\n    if (isDOM(dom)) {\n      instanceDom = dom!;\n    } else {\n      return;\n    }\n\n    const tempRect = instanceDom.getBoundingClientRect();\n\n    const transformStr = `translate3d(${tempRect?.left}px, ${tempRect.top}px, 0)`;\n    const tempObj = {\n      width: tempRect?.width + 'px',\n      height: tempRect?.height + 'px',\n      transform: transformStr,\n    };\n\n    setStyleObj(tempObj);\n    const toolBoxDom = document.getElementById(tempInstance?._UNIQUE_ID || '');\n    if (toolBoxDom) {\n      toolBoxDom.style.transform = transformStr;\n      toolBoxDom.style.width = tempRect?.width + 'px';\n      toolBoxDom.style.height = tempRect?.height + 'px';\n    }\n    // auto detect tool box position\n    updateToolBoxPosition(tempRect);\n  }, [instance._NODE_MODEL.material?.value.rootSelector, updateToolBoxPosition]);\n\n  useEffect(() => {\n    updatePos();\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [instance]);\n\n  (ref as any).current = {\n    update() {\n      updatePos();\n    },\n  };\n\n  if (!styleObj) {\n    return <></>;\n  }\n\n  return (\n    <div\n      className={styles.highlightBox}\n      id={instance?._UNIQUE_ID}\n      style={{\n        ...style,\n        ...styleObj,\n      }}\n    >\n      {toolbarView && (\n        <div ref={toolBoxRef} className={styles.toolBox}>\n          {toolbarView}\n        </div>\n      )}\n      {children}\n    </div>\n  );\n};\n\nexport type HighlightCanvasCoreProps = {\n  instances: RenderInstance[];\n  toolbarView?: React.ReactNode;\n  style?: React.CSSProperties;\n  containerStyle?: React.CSSProperties;\n  children?: React.ReactElement;\n  itemRender?: (props: { instance: RenderInstance; index: number }) => React.ReactElement;\n};\n\nexport const HighlightCanvasCore = (\n  { instances, toolbarView, style, children, containerStyle, itemRender }: HighlightCanvasCoreProps,\n  ref: React.Ref<HighlightCanvasRefType>\n) => {\n  const allBoxRef = useRef<React.RefObject<HighlightCanvasRefType>[]>([]);\n  useImperativeHandle(\n    ref,\n    () => {\n      return {\n        update() {\n          // 更新所有的高亮框位置\n          allBoxRef.current.forEach((el) => {\n            el.current?.update();\n          });\n        },\n      };\n    },\n    []\n  );\n  const onRefDestroy = (ref: React.RefObject<HighlightCanvasRefType>) => {\n    const list = allBoxRef.current || [];\n    allBoxRef.current = list.filter((el) => el !== ref);\n  };\n\n  return (\n    <div className={styles.borderDrawBox} style={containerStyle || {}}>\n      {instances.map((el, index) => {\n        if (!el || el._STATUS === 'DESTROY') {\n          return null;\n        }\n        let child: any = children;\n        if (itemRender) {\n          const Comp = itemRender;\n          child = <Comp instance={el} index={index} />;\n        }\n\n        return (\n          <HighlightBox\n            style={style}\n            key={index}\n            instance={el}\n            toolbarView={toolbarView}\n            getRef={(ref) => {\n              if (ref.current) {\n                allBoxRef.current.push(ref);\n              }\n            }}\n            onRefDestroy={onRefDestroy}\n          >\n            {child}\n          </HighlightBox>\n        );\n      })}\n    </div>\n  );\n};\n\nexport const HighlightCanvas = React.forwardRef(HighlightCanvasCore);\n"
  },
  {
    "path": "packages/layout/src/components/HighlightBox/style.module.scss",
    "content": ".highlightBox {\n  position: absolute;\n  will-change: transform, width, height, left, right;\n  outline: 2px solid rgb(44, 115, 253);\n}\n\n.borderDrawBox {\n  position: absolute;\n  left: 0;\n  z-index: 1;\n  pointer-events: none;\n}\n\n.toolBox {\n  position: absolute;\n  width: 100%;\n  left: 0;\n  top: 0;\n  box-sizing: border-box;\n  transform: translateY(-100%);\n}\n"
  },
  {
    "path": "packages/layout/src/core/dragAndDrop/common.ts",
    "content": "export type Pointer = {\n  x: number;\n  y: number;\n};\n"
  },
  {
    "path": "packages/layout/src/core/dragAndDrop/emitter.ts",
    "content": "import mitt, { Emitter } from 'mitt';\n\nexport class DEmitter<T extends Record<string, unknown> = any> {\n  emitter: Emitter<T>;\n  constructor() {\n    this.emitter = mitt();\n  }\n}\n"
  },
  {
    "path": "packages/layout/src/core/dragAndDrop/index.ts",
    "content": "import { Pointer } from './common';\nimport { SensorEventType, Sensor, SensorEventObjType } from './sensor';\nimport mitt, { Emitter } from 'mitt';\nimport { BaseDragAndDropEventType } from '../../types/dragAndDrop';\nimport { debounce } from 'lodash-es';\n\ntype EmptyFunc = () => void;\n\nexport type DragAndDropEventType<E> = {\n  click: SensorEventObjType;\n  mouseMove: SensorEventObjType;\n  mouseDown: SensorEventObjType;\n  mouseUp: SensorEventObjType;\n} & BaseDragAndDropEventType<E>;\n\n/** 目前只支持一个 iframe 的拖拽，不能多个 iframe 拖拽共存 */\nexport class DragAndDrop<E = Record<string, any>> {\n  senors: Sensor[] = [];\n  senorEventPriorityQueueMap: Record<\n    string,\n    {\n      priority: number;\n      handle: (...args: any) => void;\n    }[]\n  > = {};\n  doc: Document;\n  pointer: Pointer = {\n    x: 0,\n    y: 0,\n  };\n  /** 触发 dragStart 的移动距离 */\n  shakeDistance = 4;\n  eventHandler: EmptyFunc[] = [];\n  /** 鼠标当前进入的感应区 */\n  currentSensor: Sensor | null = null;\n  currentState: 'NORMAL' | 'DRAGGING' | 'CANCEL' = 'NORMAL';\n  dragStartObj: SensorEventType['mouseDown'] | null = null;\n  emitter: Emitter<DragAndDropEventType<E>> = mitt<DragAndDropEventType<E>>();\n  /** 拖动结束后是否可以触发点击事件 */\n  canTriggerClick = true;\n  /** 存储需要被恢复的事件列表 */\n  recoverEventList: { name: string; event: Event }[] = [];\n  /** 鼠标按压状态 */\n  mousePressStatus: 'DOWN' | 'UP' = 'UP';\n  /** 主窗口的 window 对象，有且只能有一个主窗口 */\n  win: Window;\n  globalSenor: Sensor<any>;\n  constructor(options: {\n    win: Window;\n    doc: Document;\n    dragConfig?: {\n      shakeDistance?: number;\n    };\n  }) {\n    this.preventDefaultEvent();\n    this.doc = options.doc;\n    this.win = options.win;\n    if (options.dragConfig?.shakeDistance !== undefined) {\n      this.shakeDistance = options.dragConfig?.shakeDistance;\n    }\n\n    // global sensor, 不需要推入到 sensors 中\n    const sensor = new Sensor({\n      name: 'globalSensor',\n      container: options.doc,\n      mainDocument: document,\n    });\n\n    sensor.setCanDrag(async () => {\n      return null;\n    });\n\n    sensor.emitter.on('mouseDown', async (mouseMoveEventObj) => {\n      this.emitter.emit('mouseDown', mouseMoveEventObj);\n      this.mousePressStatus = 'DOWN';\n      return;\n    });\n\n    sensor.emitter.on('mouseMove', async (mouseMoveEventObj) => {\n      this.emitter.emit('mouseMove', mouseMoveEventObj);\n      if (!(this.currentState === 'DRAGGING' && this.currentSensor === null)) {\n        return;\n      }\n\n      const canDrop = await sensor.canDrop({\n        ...mouseMoveEventObj,\n        extraData: {\n          ...this.dragStartObj!.extraData,\n        },\n      });\n      if (!canDrop) {\n        return;\n      }\n      const { pointer, event } = mouseMoveEventObj;\n      let canDropExtraData = {};\n      if (typeof canDrop !== 'boolean') {\n        canDropExtraData = canDrop.extraData || {};\n      }\n      const draggingEventObj = {\n        from: this.dragStartObj!.event,\n        fromSensor: this.dragStartObj!.sensor,\n        fromPointer: this.dragStartObj!.pointer,\n        extraData: {\n          ...this.dragStartObj!.extraData,\n          ...canDropExtraData,\n        },\n        current: event,\n        currentSensor: sensor,\n        pointer: pointer,\n      };\n      const dragEventName = 'dragging';\n      this.emitter.emit(dragEventName, draggingEventObj);\n      this.batchSensorEmit(dragEventName, draggingEventObj);\n    });\n\n    sensor.emitter.on('mouseUp', (eventObj) => {\n      // 如不是在 业务注册的感应区mouseUp， 需要在全局补偿触发 dragEnd\n      const tempSensor = this.senors.find((s) => {\n        const sensorBox = s.container;\n        const endDom = eventObj.event.target;\n        return sensorBox.contains(endDom as any);\n      });\n      if (tempSensor) {\n        return;\n      }\n      this.mousePressStatus = 'UP';\n      this.currentState = 'NORMAL';\n      this.dragStartObj = null;\n      this.batchSensorEmit('dragEnd', {} as any);\n    });\n\n    this.globalSenor = sensor;\n  }\n\n  preventDefaultEvent() {\n    document.addEventListener('contextmenu', (event) => {\n      event.preventDefault(); // 阻止默认右键菜单\n    });\n  }\n\n  batchSensorEmit(eventName: keyof SensorEventType, eventObj: SensorEventType[keyof SensorEventType]) {\n    const senors = this.senors;\n    senors.forEach((sn) => {\n      sn.emitter.emit(eventName, eventObj);\n    });\n  }\n\n  registerSensor(sensor: Sensor) {\n    sensor.getTargetSensor = this.getTargetSensor.bind(this);\n    this.senors.push(sensor);\n    sensor.emitter.on('click', (eventObj) => {\n      if (this.canTriggerClick) {\n        this.emitter.emit('click', {\n          sensor: eventObj.sensor,\n          event: eventObj.event,\n          pointer: eventObj.pointer,\n        });\n      }\n    });\n\n    const onMouseDown = (eventObj: SensorEventType['mouseDown']) => {\n      this.mousePressStatus = 'DOWN';\n      this.recoverEventList.push({\n        name: 'mousedown',\n        event: eventObj.event,\n      });\n      this.dragStartObj = eventObj;\n    };\n\n    sensor.emitter.on('mouseDown', onMouseDown);\n    this.eventHandler.push(() => {\n      sensor.emitter.off('mouseDown', onMouseDown);\n    });\n\n    // mousemove\n    const onMouseMove = async (mouseMoveEventObj: SensorEventType['mouseMove']) => {\n      if (this.mousePressStatus === 'DOWN') {\n        this.recoverEventList.push({\n          name: 'mousemove',\n          event: mouseMoveEventObj.event,\n        });\n      }\n\n      // 拖动过程中 API 终止\n      if (this.currentState === 'CANCEL') {\n        return;\n      }\n      const { sensor, pointer, event } = mouseMoveEventObj;\n      // 没有任何按键按下的 鼠标移动，直接返回\n      if (event.buttons === 0) {\n        this.resetDrag();\n        return;\n      }\n      this.emitter.emit('mouseMove', mouseMoveEventObj);\n\n      if (this.currentState === 'NORMAL') {\n        if (this.dragStartObj === null) {\n          return;\n        }\n        if (sensor !== this.dragStartObj?.sensor) {\n          return;\n        }\n        const pointer1 = pointer;\n        const pointer2 = this.dragStartObj.pointer;\n        const SHAKE_DISTANCE = this.shakeDistance;\n        const isShaken = Math.pow(pointer1.y - pointer2.y, 2) + Math.pow(pointer1.x - pointer2.x, 2) > SHAKE_DISTANCE;\n        // 小于抖动距离，不是拖拽\n        if (!isShaken) {\n          return;\n        }\n        // 判断元素是否可以拖动\n        const canDrag = await this.dragStartObj.sensor.canDrag(this.dragStartObj);\n        if (!canDrag) {\n          return;\n        }\n        if (typeof canDrag !== 'boolean') {\n          this.dragStartObj = canDrag;\n        }\n        this.currentState = 'DRAGGING';\n        const eventName = 'dragStart';\n        const eventObj = {\n          from: this.dragStartObj.event,\n          fromSensor: this.dragStartObj.sensor,\n          fromPointer: pointer,\n          pointer: this.dragStartObj.pointer,\n          extraData: this.dragStartObj.extraData || {},\n        };\n        this.emitter.emit(eventName, eventObj);\n        this.batchSensorEmit(eventName, eventObj);\n        return;\n      }\n\n      const canDrop = await sensor.canDrop({\n        ...mouseMoveEventObj,\n        extraData: {\n          ...this.dragStartObj!.extraData,\n        },\n      });\n      if (canDrop === false) {\n        return;\n      }\n\n      let canDropExtraData = {};\n      if (typeof canDrop !== 'boolean') {\n        canDropExtraData = canDrop?.extraData || {};\n      }\n      const draggingEventIbj = {\n        from: this.dragStartObj!.event,\n        fromSensor: this.dragStartObj!.sensor,\n        fromPointer: this.dragStartObj!.pointer,\n        extraData: {\n          ...this.dragStartObj!.extraData,\n          ...canDropExtraData,\n        },\n        current: event,\n        currentSensor: sensor,\n        pointer: pointer,\n      };\n      const dragEventName = 'dragging';\n      this.emitter.emit(dragEventName, draggingEventIbj);\n      this.batchSensorEmit(dragEventName, draggingEventIbj);\n    };\n\n    sensor.emitter.on('mouseMove', onMouseMove);\n    this.eventHandler.push(() => {\n      sensor?.emitter.off('mouseMove', onMouseMove);\n    });\n\n    // mouseup\n    const onMouseUp = async ({ sensor, event, pointer }: SensorEventType['mouseUp']) => {\n      this.mousePressStatus = 'UP';\n      // 判断是否需要恢复事件触发\n      if (this.currentState === 'NORMAL') {\n        const recoverEventList = this.recoverEventList;\n        this.recoverEventList = [];\n        recoverEventList.push({\n          name: 'mouseup',\n          event: event,\n        });\n        recoverEventList.forEach((el) => {\n          const newEvent = new Event(el.name, el.event);\n          (newEvent as any).fixed = true;\n          el.event.target?.dispatchEvent(newEvent);\n        });\n      }\n\n      if (this.currentState === 'DRAGGING') {\n        this.canTriggerClick = false;\n        setTimeout(() => {\n          this.canTriggerClick = true;\n        }, 16.66);\n        const canDrop = await sensor.canDrop({\n          sensor,\n          event,\n          pointer,\n          extraData: {\n            ...this.dragStartObj!.extraData,\n          },\n        });\n        if (!(canDrop === false)) {\n          let canDropExtraData = {};\n          if (typeof canDrop !== 'boolean') {\n            canDropExtraData = canDrop?.extraData || {};\n          }\n          const dropEventName = 'drop';\n          const dropEventObj = {\n            from: this.dragStartObj!.event,\n            fromSensor: this.dragStartObj!.sensor,\n            fromPointer: this.dragStartObj!.pointer,\n            extraData: {\n              ...this.dragStartObj!.extraData,\n              ...canDropExtraData,\n            },\n            current: event,\n            currentSensor: sensor,\n            pointer: pointer,\n          };\n          this.emitter.emit(dropEventName, dropEventObj);\n          this.batchSensorEmit(dropEventName, dropEventObj);\n        }\n      }\n\n      const dragEndEventName = 'dragEnd';\n      const dragEndEventObj: any = {\n        from: this.dragStartObj?.event,\n        fromSensor: this.dragStartObj?.sensor,\n        fromPointer: this.dragStartObj?.pointer,\n        extraData: this.dragStartObj?.extraData || {},\n        current: event,\n        currentSensor: sensor,\n        pointer: pointer,\n      };\n      this.emitter.emit(dragEndEventName, dragEndEventObj);\n      this.batchSensorEmit(dragEndEventName, dragEndEventObj);\n\n      this.currentState = 'NORMAL';\n      this.dragStartObj = null;\n    };\n\n    sensor.emitter.on('mouseUp', onMouseUp);\n    this.eventHandler.push(() => {\n      sensor.emitter.off('mouseUp', onMouseUp);\n    });\n\n    sensor.emitter.on('mouseEnter', () => {\n      this.currentSensor = sensor;\n    });\n    sensor.emitter.on('mouseLeave', () => {\n      this.currentSensor = null;\n    });\n    this.eventHandler.push(() => {\n      sensor.emitter.off('mouseUp', onMouseUp);\n    });\n  }\n\n  flushSenorEventPriorityQueueMap = debounce((eventName: string) => {\n    const list = this.senorEventPriorityQueueMap[eventName];\n    this.senorEventPriorityQueueMap[eventName] = [];\n    list.sort((a, b) => {\n      return b.priority - a.priority;\n    });\n    list.forEach((e) => {\n      e.handle();\n    });\n  }, 10);\n\n  cancelDrag() {\n    this.currentState = 'CANCEL';\n  }\n\n  resetDrag() {\n    this.currentState = 'NORMAL';\n  }\n\n  clearSensors() {\n    const oldSensors = this.senors;\n    this.senors = [];\n    oldSensors.forEach((el) => el.destroy());\n  }\n\n  /** 通过 sensor 以及event 判断应该有那个 sensor 触发事件，并修正事件的 出发 dom 以及 mousePos */\n  getTargetSensor(options: { sensor: Sensor; event: MouseEvent }) {\n    const { sensor, event } = options;\n    // 判断坐标是否为负数\n\n    const clientX = event.clientX;\n    const clientY = event.clientY;\n    const newEvent = {\n      clientX,\n      clientY,\n      target: event.target,\n      sourceEvent: event,\n    };\n    let newSensor: Sensor = sensor;\n    if (clientX < 0 || clientY < 0) {\n      const offset = sensor.getOffset();\n\n      const newX = clientX + offset.x;\n      const newY = clientY + offset.y;\n\n      newEvent.clientX = newX;\n      newEvent.clientY = newY;\n\n      const targetDom = document.elementFromPoint(newX, newY);\n      if (targetDom) {\n        newEvent.target = targetDom;\n        const tempSensor = this.findSensorByDom(targetDom);\n        if (tempSensor) {\n          newSensor = tempSensor;\n        } else {\n          newSensor = this.globalSenor;\n        }\n      } else {\n        newSensor = this.globalSenor;\n      }\n    }\n\n    return {\n      sensor: newSensor,\n      event: newEvent as unknown as MouseEvent,\n    };\n  }\n\n  findSensorByDom(dom: Element) {\n    const senors = this.senors;\n    const res = senors.find((el) => {\n      const container = el.container;\n      return container.contains(dom);\n    });\n    return res;\n  }\n}\n\nexport * from './sensor';\nexport * from './emitter';\nexport * from './common';\n"
  },
  {
    "path": "packages/layout/src/core/dragAndDrop/sensor.ts",
    "content": "import { BaseDragAndDropEventType } from '../../types/dragAndDrop';\nimport { addEventListenerReturnCancel } from '../../utils';\nimport { Pointer } from './common';\nimport { DEmitter } from './emitter';\n\nexport type SensorOffsetType = {\n  x: number;\n  y: number;\n};\n\nexport type SensorEventObjType<T extends Record<string, any> = any> = {\n  sensor: Sensor<T>;\n  pointer: Pointer;\n  event: MouseEvent;\n  extraData?: T;\n};\n\nexport type SensorClickEventObjType = Omit<SensorEventObjType, 'pointer'>;\n\nexport type SensorEventType<E extends Record<string, any> = any> = {\n  mouseLeave: SensorEventObjType<E>;\n  mouseEnter: SensorEventObjType<E>;\n  mouseChange: SensorEventObjType<E>;\n  mouseUp: SensorEventObjType<E>;\n  mouseDown: SensorEventObjType<E>;\n  mouseMove: SensorEventObjType<E>;\n  click: SensorEventObjType<E>;\n} & BaseDragAndDropEventType<E>;\n\nexport type SensorEventNameType = keyof SensorEventType<any>;\n\nexport class Sensor<E extends Record<string, any> = any> extends DEmitter<SensorEventType<E>> {\n  /** TODO: 用于处理感应区重叠时，事件触发的优先级, 暂时未实现相关功能 */\n  eventPriority = 0;\n  /** 感应区在主窗口的便宜量 */\n  private offset: SensorOffsetType = {\n    x: 0,\n    y: 0,\n  };\n\n  /** 感应区的容器元素 */\n  container: HTMLElement | Document;\n  /** 在主窗口的感应器区的 dom元素 */\n  offsetDom?: HTMLElement | null;\n\n  canDrag: (params: SensorEventObjType<E>) => Promise<SensorEventObjType<E> | null | undefined | boolean> = (params) =>\n    Promise.resolve(params);\n  canDrop: (params: SensorEventObjType<E>) => Promise<SensorEventObjType<E> | null | undefined | boolean> = (params) =>\n    Promise.resolve(params);\n\n  private eventDisposeQueue: (() => void)[] = [];\n  name: string;\n\n  /** 由 dragAndDrop 传入，用于修正 跨 iframe */\n  getTargetSensor?: (options: { sensor: Sensor; event: MouseEvent }) => { sensor: Sensor; event: MouseEvent };\n\n  constructor(options: {\n    name: string;\n    container: Sensor['container'];\n    offset?: Sensor['offset'];\n    offsetDom?: Sensor['offsetDom'];\n    eventPriority?: number;\n    /** 主窗口的文档对象用于获取dom 元素根据 clientX, clientY */\n    mainDocument: Document;\n    isIframe?: boolean;\n  }) {\n    super();\n    this.name = options.name;\n    this.eventPriority = options.eventPriority || this.eventPriority;\n    this.container = options.container;\n    if (options.offset) {\n      this.offset = options.offset || { x: 0, y: 0 };\n    }\n\n    this.offsetDom = options.offsetDom;\n\n    this.registerEvent();\n    this.registerSyncOffsetEvent();\n  }\n\n  registerSyncOffsetEvent() {\n    const container = this.offsetDom;\n    if (!container) {\n      return;\n    }\n    const handle = window.setInterval(() => {\n      const rect = container.getBoundingClientRect();\n      this.offset = {\n        x: rect.x,\n        y: rect.y,\n      };\n    }, 250);\n\n    this.eventDisposeQueue.push(() => {\n      clearInterval(handle);\n    });\n  }\n\n  registerEvent() {\n    const container = this.container;\n    this.eventDisposeQueue.push(\n      addEventListenerReturnCancel(container, 'mouseenter', (e) => {\n        const fixedSensorInfo = this.getTargetSensor?.({\n          sensor: this,\n          event: e,\n        }) || { sensor: this, event: e };\n        const newSensor = fixedSensorInfo.sensor as this;\n        newSensor.emitter.emit('mouseEnter', {\n          sensor: fixedSensorInfo.sensor,\n          event: fixedSensorInfo.event,\n          pointer: fixedSensorInfo.sensor.getPointer(fixedSensorInfo.event),\n        });\n      })\n    );\n    this.eventDisposeQueue.push(\n      addEventListenerReturnCancel(container, 'mouseleave', (e) => {\n        const fixedSensorInfo = this.getTargetSensor?.({\n          sensor: this,\n          event: e,\n        }) || { sensor: this, event: e };\n        const newSensor = fixedSensorInfo.sensor as this;\n\n        newSensor.emitter.emit('mouseLeave', {\n          sensor: fixedSensorInfo.sensor,\n          event: fixedSensorInfo.event,\n          pointer: fixedSensorInfo.sensor.getPointer(fixedSensorInfo.event),\n        });\n      })\n    );\n    this.eventDisposeQueue.push(\n      addEventListenerReturnCancel(\n        container,\n        'mousedown',\n        (e) => {\n          if (e.button === 2) {\n            // 鼠标右键按下\n            return;\n          }\n          const fixedSensorInfo = this.getTargetSensor?.({\n            sensor: this,\n            event: e,\n          }) || { sensor: this, event: e };\n          const newSensor = fixedSensorInfo.sensor as this;\n\n          newSensor.emitter.emit('mouseChange', {\n            sensor: fixedSensorInfo.sensor,\n            event: fixedSensorInfo.event,\n            pointer: fixedSensorInfo.sensor.getPointer(fixedSensorInfo.event),\n          });\n\n          newSensor.emitter.emit('mouseDown', {\n            sensor: fixedSensorInfo.sensor,\n            event: fixedSensorInfo.event,\n            pointer: fixedSensorInfo.sensor.getPointer(fixedSensorInfo.event),\n          });\n        },\n        true\n      )\n    );\n    this.eventDisposeQueue.push(\n      addEventListenerReturnCancel(\n        container,\n        'mouseup',\n        (e) => {\n          const fixedSensorInfo = this.getTargetSensor?.({\n            sensor: this,\n            event: e,\n          }) || { sensor: this, event: e };\n          const newSensor = fixedSensorInfo.sensor as this;\n\n          newSensor.emitter.emit('mouseChange', {\n            sensor: fixedSensorInfo.sensor,\n            event: fixedSensorInfo.event,\n            pointer: fixedSensorInfo.sensor.getPointer(fixedSensorInfo.event),\n          });\n          newSensor.emitter.emit('mouseUp', {\n            sensor: fixedSensorInfo.sensor,\n            event: fixedSensorInfo.event,\n            pointer: fixedSensorInfo.sensor.getPointer(fixedSensorInfo.event),\n          });\n        },\n        true\n      )\n    );\n\n    this.eventDisposeQueue.push(\n      addEventListenerReturnCancel(\n        container,\n        'mousemove',\n        (e) => {\n          const fixedSensorInfo = this.getTargetSensor?.({\n            sensor: this,\n            event: e,\n          }) || { sensor: this, event: e };\n          const newSensor = fixedSensorInfo.sensor as this;\n\n          newSensor.emitter.emit('mouseMove', {\n            sensor: fixedSensorInfo.sensor,\n            event: fixedSensorInfo.event,\n            pointer: fixedSensorInfo.sensor.getPointer(fixedSensorInfo.event),\n          });\n          newSensor.emitter.emit('mouseChange', {\n            sensor: fixedSensorInfo.sensor,\n            event: fixedSensorInfo.event,\n            pointer: fixedSensorInfo.sensor.getPointer(fixedSensorInfo.event),\n          });\n        },\n        true\n      )\n    );\n\n    this.eventDisposeQueue.push(\n      addEventListenerReturnCancel(\n        container,\n        'click',\n        (e) => {\n          const fixedSensorInfo = this.getTargetSensor?.({\n            sensor: this,\n            event: e,\n          }) || { sensor: this, event: e };\n          const newSensor = fixedSensorInfo.sensor as this;\n          newSensor.emitter.emit('click', {\n            sensor: fixedSensorInfo.sensor,\n            event: fixedSensorInfo.event,\n            pointer: fixedSensorInfo.sensor.getPointer(fixedSensorInfo.event),\n          });\n        },\n        true\n      )\n    );\n  }\n\n  getPointer(e: { clientX: number; clientY: number }) {\n    return {\n      x: this.offset.x + e.clientX,\n      y: this.offset.y + e.clientY,\n    };\n  }\n\n  updateOffset(offset: SensorOffsetType) {\n    this.offset = offset;\n  }\n\n  getOffset() {\n    return this.offset;\n  }\n\n  setCanDrag(cb: typeof this.canDrag) {\n    this.canDrag = cb;\n  }\n\n  setCanDrop(cb: typeof this.canDrop) {\n    this.canDrop = cb;\n  }\n\n  destroy() {\n    this.eventDisposeQueue.forEach((el) => el());\n  }\n}\n"
  },
  {
    "path": "packages/layout/src/core/iframeContainer/index.tsx",
    "content": "import * as CRender from '@chamn/render';\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport ReactDOMClient from 'react-dom/client';\n\nexport class IFrameContainer {\n  iframe: HTMLIFrameElement | null;\n  iframeStatus: 'INIT' | 'LOADED' | 'LOADED_FAILED';\n  readyQueue: (() => void)[] = [];\n  errorQueue: ((e: { msg: string }) => void)[] = [];\n  loadError: any;\n  containerDom?: HTMLDivElement;\n  constructor() {\n    this.iframe = this.createIframe();\n    this.iframeStatus = 'INIT';\n  }\n\n  createIframe() {\n    const iframe = document.createElement('iframe');\n    this.iframe = iframe;\n    iframe.style.cssText =\n      'width: 100%; height: 100%; border: none; padding: 0; margin: 0; box-sizing: border-box; overflow: hidden';\n\n    return iframe;\n  }\n\n  getHTMLTemplate() {\n    const template = `\n    <!doctype html>\n    <html class=\"chameleon-design-mode\">\n      <head>\n        <meta charset=\"utf-8\"/>\n        <style>\n          html,body,#app {\n            margin: 0;\n            padding: 0;\n            box-sizing: border-box;\n            width: 100%;\n            height: 100%;\n          }\n          * {\n            margin: 0;\n            padding: 0;\n            box-sizing: border-box;\n          }\n          img,audio,video {\n            user-select: none;\n            -webkit-user-drag: none;\n          }\n        </style>\n      </head>\n      <body>\n        <div id=\"app\"><div>\n      </body>\n    </html>    `;\n    return template;\n  }\n\n  load(containerDom: HTMLDivElement) {\n    this.containerDom = containerDom;\n    if (this.iframe) {\n      containerDom.appendChild(this.iframe);\n    } else {\n      console.warn('iframe not exits');\n      return;\n    }\n    const iframeDoc = this.getDocument();\n    const iframeWin = this.getWindow();\n    if (!(iframeDoc && iframeWin)) {\n      return;\n    }\n\n    const loaded = () => {\n      this.iframeStatus = 'LOADED';\n      const queue = this.readyQueue;\n      this.readyQueue = [];\n      queue.forEach((cb) => cb());\n      iframeDoc.removeEventListener('load', loaded);\n    };\n\n    const p = new Promise((resolve) => {\n      this.iframe!.addEventListener('load', () => {\n        loaded();\n        resolve('finish');\n      });\n    });\n\n    const tpl = this.getHTMLTemplate();\n    iframeDoc.write(tpl);\n    iframeDoc.close();\n\n    return p;\n  }\n\n  loadUrl(url: string) {\n    return new Promise((resolve) => {\n      const handler = (e: Event) => {\n        resolve(e);\n        this.iframe?.removeEventListener('load', handler);\n      };\n      this.iframe?.addEventListener('load', handler);\n      this.iframe?.contentWindow?.location.replace(url);\n    });\n  }\n\n  ready(cb: () => void) {\n    if (this.iframeStatus === 'LOADED') {\n      cb();\n      return;\n    }\n\n    if (this.iframeStatus === 'INIT') {\n      this.readyQueue.push(cb);\n    }\n  }\n\n  onLoadFailed(cb: (e: { msg: string }) => void) {\n    if (this.iframeStatus === 'LOADED_FAILED') {\n      cb({ msg: this.loadError });\n      return;\n    }\n    if (this.iframeStatus === 'INIT') {\n      this.errorQueue.push(cb);\n    }\n  }\n\n  getWindow():\n    | (Window & {\n        CRender?: typeof CRender;\n        React?: typeof React;\n        ReactDOM?: typeof ReactDOM;\n        ReactDOMClient?: typeof ReactDOMClient;\n      })\n    | undefined\n    | null {\n    return this.iframe?.contentWindow;\n  }\n\n  getDocument() {\n    return this.iframe?.contentDocument;\n  }\n\n  injectJS(jsUrl: string) {\n    const document = this.getDocument();\n    if (!document) {\n      return false;\n    }\n\n    const scriptTag = document.createElement('script');\n    scriptTag.src = jsUrl;\n\n    return new Promise((resolve, reject) => {\n      scriptTag.onload = resolve;\n      scriptTag.onerror = reject;\n      document.head.appendChild(scriptTag);\n    });\n  }\n\n  injectJsText(jsText: string) {\n    const document = this.getDocument();\n    if (!document) {\n      return false;\n    }\n\n    const scriptTag = document.createElement('script');\n    scriptTag.textContent = jsText;\n\n    document.head.appendChild(scriptTag);\n\n    return true;\n  }\n\n  injectStyleText(styleText: string) {\n    const document = this.getDocument();\n    if (!document) {\n      return false;\n    }\n\n    const styleTag = document.createElement('style');\n    styleTag.textContent = styleText;\n    document.head.appendChild(styleTag);\n\n    return true;\n  }\n\n  destroy() {\n    this.iframe?.parentNode?.removeChild(this.iframe);\n    this.iframe = null;\n  }\n}\n"
  },
  {
    "path": "packages/layout/src/index.module.scss",
    "content": ".layoutContainer {\n  position: relative;\n  width: 100%;\n  height: 100%;\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n  display: flex;\n  justify-content: center;\n  box-shadow: 0 1px 4px 0 rgb(31 50 88 / 13%);\n}\n\n.elementHighlightBox {\n  position: absolute;\n  right: 0;\n  top: 0;\n  padding: 10px;\n}\n\n.iframeBox {\n  position: relative;\n}\n"
  },
  {
    "path": "packages/layout/src/index.tsx",
    "content": "/* eslint-disable react-refresh/only-export-components */\nimport React from 'react';\nimport { RenderInstance } from '@chamn/render';\nimport { DesignRender, DesignRenderProp } from '@chamn/render';\nimport { IFrameContainer } from './core/iframeContainer';\nimport { addEventListenerReturnCancel } from './utils';\nimport { HighlightCanvas, HighlightCanvasCoreProps, HighlightCanvasRefType } from './components/HighlightBox';\nimport { DragAndDrop, DragAndDropEventType } from './core/dragAndDrop';\nimport { Sensor } from './core/dragAndDrop/sensor';\nimport { DropAnchorCanvas, DropAnchorPropsType } from './components/DropAnchor';\nimport {\n  AdvanceCustom,\n  AssetPackage,\n  CNode,\n  CPage,\n  CPageDataType,\n  CRootNode,\n  DropPosType,\n  InnerComponentNameEnum,\n  getRandomStr,\n} from '@chamn/model';\nimport { Pointer } from './core/dragAndDrop/common';\nimport { calculateDropPosInfo } from './components/DropAnchor/util';\nimport { DragAndDropEventObj, LayoutDragAndDropExtraDataType } from './types/dragAndDrop';\n\nimport styles from './index.module.scss';\nimport intersection from 'lodash-es/intersection';\n\nexport type LayoutDragEvent<T = LayoutDragAndDropExtraDataType> = DragAndDropEventObj<T>;\n\nexport enum LayoutMode {\n  EDIT = 'EDIT',\n  /** 不触发任何编辑器的选择、拖拽、高亮、hover 交互 */\n  PREVIEW = 'PREVIEW',\n}\n\nexport type LayoutPropsType = Omit<DesignRenderProp, 'adapter' | 'ref'> & {\n  /** 渲染模式 */\n  mode?: LayoutMode;\n  renderJSUrl?: string;\n  /** 编辑模式下需要额外加在的资源信息 */\n  assets?: AssetPackage[];\n  onSelectNode?: (node: CNode | CRootNode | null, event: MouseEvent | null | undefined) => Promise<boolean | void>;\n  onHoverNode?: (node: CNode | CRootNode | null, dragNode: CNode | CRootNode, event: MouseEvent) => void;\n  nodeCanDrag?: (event: LayoutDragEvent) => ReturnType<Required<AdvanceCustom>['canDragNode']>;\n  nodeCanDrop?: (event: LayoutDragEvent) => ReturnType<Required<AdvanceCustom>['canDropNode']>;\n  onNodeDragStart?: (event: LayoutDragEvent) => ReturnType<Required<AdvanceCustom>['onDragStart']>;\n  onNodeDragging?: (event: LayoutDragEvent) => ReturnType<Required<AdvanceCustom>['onDragging']>;\n  onNodeDraEnd?: (event: LayoutDragEvent) => ReturnType<Required<AdvanceCustom>['onDragEnd']>;\n  onNodeDrop?: (event: LayoutDragEvent) => ReturnType<Required<AdvanceCustom>['onDrop']>;\n  onNodeNewAdd?: (event: LayoutDragEvent) => ReturnType<Required<AdvanceCustom>['onNewAdd']>;\n  selectToolbarView?: React.ReactNode;\n  selectBoxStyle?: React.CSSProperties;\n  hoverBoxStyle?: React.CSSProperties;\n  hoverToolBarView?: React.ReactNode;\n  selectRectViewRender?: (props: {\n    instance: RenderInstance;\n    index: number;\n    isLock: boolean;\n  }) => ReturnType<Required<AdvanceCustom>['selectRectViewRender']>;\n  hoverRectViewRender?: (props: {\n    instance: RenderInstance;\n    index: number;\n    isLock: boolean;\n  }) => ReturnType<Required<AdvanceCustom>['hoverRectViewRender']>;\n  dropViewRender?: (props: {\n    instance: RenderInstance;\n    index: number;\n    isLock: boolean;\n  }) => ReturnType<Required<AdvanceCustom>['dropViewRender']>;\n  ghostView?: React.ReactNode;\n  /** 在 iframe 渲染 render 之前做一些事*/\n  beforeInitRender?: (options: {\n    iframe: IFrameContainer;\n    pageModel?: CPage;\n    page?: CPageDataType;\n    assets: AssetPackage[];\n  }) => Promise<any>;\n  // 自定义 render\n  customRender?: (options: {\n    iframe: IFrameContainer;\n    pageModel?: CPage;\n    page?: CPageDataType;\n    assets: AssetPackage[];\n    renderJSUrl?: string;\n    beforeInitRender?: () => void;\n    ready: (designRender: DesignRender) => void;\n  }) => void;\n  pluginCtx?: any;\n};\n\nexport type LayoutStateType = {\n  ready: boolean;\n  isDragging: boolean;\n  mousePointer: Pointer | null;\n  currentSelectInstance: RenderInstance | null;\n  currentSelectId: string;\n  selectComponentInstances: RenderInstance[];\n  selectLockStyle: React.CSSProperties;\n  hoverComponentInstances: RenderInstance[];\n  dropComponentInstances: RenderInstance[];\n  dropPosInfos: DropPosType[];\n  dropEvent: DragAndDropEventType<LayoutDragAndDropExtraDataType>['dragging'] | null;\n  dropInfo: DropPosType | null;\n  canDrop: boolean;\n  /** 是否可以选中节点 */\n  canSelectNode: boolean;\n  pointerEventsForHightLightBox: 'auto' | 'none';\n};\n\nconst SELECT_LOCK_STYLE: React.CSSProperties = {\n  backgroundColor: 'rgba(0,0,0,0.2)',\n};\n\nexport class Layout extends React.Component<LayoutPropsType, LayoutStateType> {\n  designRenderRef: React.MutableRefObject<DesignRender | null>;\n  iframeContainer: IFrameContainer;\n  eventExposeHandler: (() => void)[];\n  state: LayoutStateType;\n  highlightCanvasRef: React.RefObject<HighlightCanvasRefType>;\n  dnd!: DragAndDrop;\n  highlightHoverCanvasRef: React.RefObject<HighlightCanvasRefType>;\n  highlightDropAnchorCanvasRef: React.RefObject<HighlightCanvasRefType>;\n  readyCbList: ((layoutInstance: Layout) => void)[] = [];\n  assets: AssetPackage[] = [];\n  dragStartNode: CNode | CRootNode | null = null;\n  realTimeSelectNodeInstanceTimer = 0;\n  iframeDomId: string;\n  /** 在 layout 层取消拖动行为，实际上 senor 的拖动行为仍然发生 */\n  isCancelDrag: boolean;\n  /** 渲染模式  */\n  mode: LayoutMode = LayoutMode.EDIT;\n\n  constructor(props: LayoutPropsType) {\n    super(props);\n    this.iframeDomId = getRandomStr();\n    this.designRenderRef = React.createRef<DesignRender | null>();\n    this.iframeContainer = new IFrameContainer();\n    this.eventExposeHandler = [];\n    this.isCancelDrag = false;\n    this.mode = props.mode ?? LayoutMode.EDIT;\n\n    this.state = {\n      isDragging: false,\n      ready: false,\n      mousePointer: null,\n      currentSelectInstance: null,\n      currentSelectId: '',\n      selectComponentInstances: [],\n      selectLockStyle: {},\n      hoverComponentInstances: [],\n      dropComponentInstances: [],\n      dropPosInfos: [],\n      dropEvent: null,\n      dropInfo: null,\n      canDrop: true,\n      canSelectNode: true,\n      pointerEventsForHightLightBox: 'none',\n    };\n    this.highlightCanvasRef = React.createRef<HighlightCanvasRefType>();\n    this.highlightHoverCanvasRef = React.createRef<HighlightCanvasRefType>();\n    this.highlightDropAnchorCanvasRef = React.createRef<HighlightCanvasRefType>();\n\n    const dnd = new DragAndDrop({\n      doc: document,\n      win: window,\n    });\n\n    this.dnd = dnd;\n  }\n\n  componentDidMount(): void {\n    this.init();\n  }\n\n  disposeRealTimeUpdate = () => {\n    if (this.realTimeSelectNodeInstanceTimer) {\n      clearInterval(this.realTimeSelectNodeInstanceTimer);\n      this.realTimeSelectNodeInstanceTimer = 0;\n    }\n  };\n\n  /** 重新创建 layout 中的 iframe */\n  reload() {\n    return this.init();\n  }\n\n  setMode(newMode: LayoutMode) {\n    this.mode = newMode;\n    if (this.mode === LayoutMode.PREVIEW) {\n      // 取消高亮\n      this.clearSelectNode();\n    }\n  }\n\n  init() {\n    this.dnd.clearSensors();\n    this.iframeContainer.destroy();\n    this.iframeContainer = new IFrameContainer();\n\n    (window as any).___CHAMELEON_DESIGNER_RENDER___ = this.designRenderRef;\n    const iframeContainer = this.iframeContainer;\n\n    iframeContainer.load(document.getElementById(this.iframeDomId)! as any);\n    iframeContainer.onLoadFailed((e) => {\n      console.error('iframe canvas load failed', e);\n    });\n    iframeContainer.ready(async () => {\n      if (this.props.beforeInitRender) {\n        await this.props.beforeInitRender({\n          pageModel: this.props.pageModel,\n          page: this.props.page,\n          assets: this.props.assets || [],\n          iframe: iframeContainer,\n        });\n      } else {\n        throw new Error('Must pass beforeInitRender methods');\n      }\n      const innerBeforeInitRender = async () => {\n        const subWin = iframeContainer.getWindow();\n        (subWin as any).__C_ENGINE_DESIGNER_PLUGIN_CTX__ = this.props.pluginCtx;\n      };\n      await innerBeforeInitRender();\n\n      if (this.props.customRender) {\n        this.props.customRender({\n          pageModel: this.props.pageModel,\n          page: this.props.page,\n          assets: this.props.assets || [],\n          iframe: iframeContainer,\n          renderJSUrl: this.props.renderJSUrl,\n          beforeInitRender: innerBeforeInitRender,\n          ready: (designRenderInstance) => {\n            this.designRenderRef.current = designRenderInstance;\n\n            this.registerDragAndDropEvent();\n            this.registerSelectEvent();\n            this.registerHoverEvent();\n            this.registerEventLimit();\n            this.readyOk();\n          },\n        });\n      } else {\n        throw new Error('Must pass customRender methods');\n      }\n    });\n  }\n\n  /** 禁止节点选中 */\n  banSelectNode() {\n    this.setState({\n      canSelectNode: false,\n    });\n  }\n\n  /** 恢复节点选中 */\n  recoverSelectNode() {\n    this.setState({\n      canSelectNode: true,\n    });\n  }\n\n  getPageModel() {\n    return this.designRenderRef?.current?.getPageModel();\n  }\n\n  private readyOk() {\n    this.setState({\n      ready: true,\n    });\n    const readyCbList = this.readyCbList;\n    this.readyCbList = [];\n    while (readyCbList.length) {\n      const cb = readyCbList.shift();\n      cb?.(this);\n    }\n  }\n\n  registerSelectEvent() {\n    const iframeDoc = this.iframeContainer.getDocument();\n    const subWin = this.iframeContainer.getWindow();\n\n    if (!iframeDoc || !subWin) {\n      return;\n    }\n    this.eventExposeHandler.push(\n      addEventListenerReturnCancel(\n        iframeDoc.body,\n        'click',\n        async (e) => {\n          if (this.mode === LayoutMode.PREVIEW) {\n            return;\n          }\n          if (!this.designRenderRef.current) {\n            return;\n          }\n          const componentInstance = this.designRenderRef.current.getInstanceByDom(e.target as unknown as HTMLElement);\n          if (!componentInstance) {\n            return;\n          }\n\n          const instanceList = this.designRenderRef.current.getInstancesById(componentInstance._NODE_ID || '');\n\n          if (componentInstance._NODE_MODEL.nodeType !== 'NODE') {\n            return;\n          }\n\n          const res = await this.props.onSelectNode?.(componentInstance._NODE_MODEL, e as any);\n          if (res === false) {\n            return;\n          }\n          if (!this.state.canSelectNode) {\n            return;\n          }\n          this.setState({\n            currentSelectId: componentInstance._NODE_ID,\n            currentSelectInstance: componentInstance,\n            selectComponentInstances: [...instanceList],\n            hoverComponentInstances: [],\n          });\n        },\n        true\n      )\n    );\n  }\n\n  registerHoverEvent() {\n    const iframeDoc = this.iframeContainer.getDocument();\n    if (!iframeDoc) {\n      return;\n    }\n    const hoverInstance = (e: MouseEvent) => {\n      if (this.mode === LayoutMode.PREVIEW) {\n        return;\n      }\n      if (!e.target) {\n        return;\n      }\n\n      const targetDom = e.target as HTMLElement;\n      const instance = this.designRenderRef.current?.getInstanceByDom(targetDom);\n\n      this.props.onHoverNode?.(instance?._NODE_MODEL || null, this.dragStartNode!, e);\n\n      if (instance?._NODE_ID === this.state.selectComponentInstances[0]?._NODE_ID) {\n        this.setState({\n          hoverComponentInstances: [],\n        });\n        return;\n      }\n\n      const instanceList = this.designRenderRef.current?.getInstancesById(instance?._NODE_ID || '') || [];\n\n      this.setState({\n        hoverComponentInstances: instanceList,\n      });\n    };\n    this.eventExposeHandler.push(addEventListenerReturnCancel(iframeDoc.body, 'mouseover', hoverInstance, true));\n\n    this.eventExposeHandler.push(\n      addEventListenerReturnCancel(\n        iframeDoc.body,\n        'mouseleave',\n        () => {\n          this.setState({\n            hoverComponentInstances: [],\n          });\n        },\n        true\n      )\n    );\n  }\n\n  /**\n   * 添加需要限制的事件触发的列表\n   * 默认禁止  ['mousedown'] 事件派发\n   * @returns\n   */\n  registerEventLimit() {\n    const iframeDoc = this.iframeContainer.getDocument();\n    const subWin = this.iframeContainer.getWindow();\n\n    // 禁用右键菜单\n    iframeDoc?.addEventListener('contextmenu', function (event) {\n      event.preventDefault();\n    });\n\n    if (!iframeDoc || !subWin) {\n      return;\n    }\n    ['mousedown', 'mouseup'].forEach((ev: any) => {\n      this.eventExposeHandler.push(\n        addEventListenerReturnCancel<'mousedown'>(\n          iframeDoc.body,\n          ev,\n          async (e) => {\n            if (this.mode === LayoutMode.PREVIEW) {\n              return;\n            }\n            const targetComponentInstance = this.designRenderRef.current?.getInstanceByDom(e.target as HTMLElement);\n            const targetNode = targetComponentInstance?._NODE_MODEL;\n            if (targetNode) {\n              const disableEditorDragDom = targetNode.material?.value.disableEditorDragDom;\n              if (disableEditorDragDom === true) {\n                this.cancelDrag();\n                return;\n              }\n              if (typeof disableEditorDragDom === 'object') {\n                const targetDom = e.target as HTMLElement;\n                const classList = targetDom?.classList || [];\n                const id = targetDom?.id;\n                const hitClass = intersection(classList, disableEditorDragDom.class || []).length;\n                const hitId = intersection([id], disableEditorDragDom.id || []).length;\n                if (hitClass || hitId) {\n                  this.cancelDrag();\n                  return;\n                }\n              }\n            }\n          },\n          true\n        )\n      );\n    });\n  }\n\n  cancelDrag(_event?: LayoutDragEvent<LayoutDragAndDropExtraDataType>) {\n    // 本次拖动取消后续拖动事件\n    this.isCancelDrag = true;\n    this.resetDrag();\n    this.dnd.cancelDrag();\n  }\n\n  /** 注册 iframe 中的感应区事件 */\n  registerDragAndDropEvent() {\n    const dnd = this.dnd;\n    const iframeDoc = this.iframeContainer.getDocument()!;\n\n    const sensor = new Sensor<LayoutDragAndDropExtraDataType>({\n      name: 'layout',\n      container: iframeDoc,\n      offsetDom: document.getElementById(this.iframeDomId),\n      mainDocument: document,\n    });\n\n    sensor.setCanDrag(async (eventObj) => {\n      if (this.mode === LayoutMode.PREVIEW) {\n        return;\n      }\n      const startInstance = this.designRenderRef.current?.getInstanceByDom(eventObj.event.target as HTMLElement);\n      // 木有可选中元素结束\n      if (!startInstance) {\n        return null;\n      }\n\n      const isContainDragStartEl = this.state.currentSelectInstance?._NODE_MODEL?.contains(\n        startInstance?._NODE_ID || ''\n      );\n      let startNode = startInstance?._NODE_MODEL;\n      const targetDragNode = startNode;\n      if (isContainDragStartEl && this.state.currentSelectInstance) {\n        startNode = this.state.currentSelectInstance?._NODE_MODEL;\n      }\n      const canDragRes = {\n        ...eventObj,\n        from: eventObj.event,\n        fromPointer: eventObj.pointer,\n        fromSensor: sensor,\n        extraData: {\n          ...(eventObj.extraData || {}),\n          originDragNode: startNode,\n          dragNode: targetDragNode,\n          dragNodeUID: startInstance?._UNIQUE_ID,\n        },\n      };\n      if (this.props.nodeCanDrag) {\n        const res = await this.props.nodeCanDrag(canDragRes);\n        if (res === false) {\n          return false;\n        }\n        if (typeof res === 'object') {\n          canDragRes.extraData = {\n            ...canDragRes.extraData,\n            dragNode: res.dragNode ?? canDragRes.extraData.dragNode,\n          };\n        }\n      }\n\n      return canDragRes;\n    });\n\n    sensor.setCanDrop(async (eventObj) => {\n      const dropInstance = this.designRenderRef.current?.getInstanceByDom(eventObj.event.target as HTMLElement);\n      if (!dropInstance) {\n        this.setState({\n          dropComponentInstances: [],\n        });\n        return;\n      }\n      // TODO: 如果落点元素是拖动元素的子元素则不允许放置\n      const isContainDragStartEl = this.state.currentSelectInstance?._NODE_MODEL?.contains(\n        dropInstance?._NODE_ID || ''\n      );\n\n      if (isContainDragStartEl) {\n        return;\n      }\n      const dropNode = dropInstance._NODE_MODEL;\n      const isContainer =\n        dropNode.isContainer() || dropNode.value?.componentName === InnerComponentNameEnum.ROOT_CONTAINER;\n\n      const originalEvent = eventObj.event;\n      const dropInstanceDom = dropInstance.getDom();\n      const dropInfo = calculateDropPosInfo({\n        point: {\n          x: originalEvent.clientX,\n          y: originalEvent.clientY,\n        },\n        dom: dropInstanceDom as HTMLElement,\n        isContainer: Boolean(isContainer),\n      });\n\n      const dropRes = {\n        ...eventObj,\n        from: eventObj.event,\n        fromPointer: eventObj.pointer,\n        fromSensor: sensor,\n        extraData: {\n          ...(eventObj.extraData || {}),\n          dropPosInfo: dropInfo,\n          dropNode: dropInstance?._NODE_MODEL,\n          dropNodeUID: dropInstance?._UNIQUE_ID,\n        },\n      };\n\n      if (this.props.nodeCanDrop) {\n        const res = await this.props.nodeCanDrop?.(dropRes);\n        if (res === false) {\n          return false;\n        }\n        if (typeof res === 'object') {\n          dropRes.extraData = {\n            ...dropRes.extraData,\n            ...res,\n          };\n        }\n      }\n\n      return dropRes;\n    });\n\n    dnd.registerSensor(sensor);\n    const { onSelectNode } = this.props;\n    sensor.emitter.on('dragStart', async (eventObj) => {\n      if (this.mode === LayoutMode.PREVIEW || this.isCancelDrag) {\n        return;\n      }\n      this.setState({\n        isDragging: true,\n      });\n\n      const { currentSelectInstance } = this.state;\n      const extraData = eventObj.extraData as LayoutDragAndDropExtraDataType;\n\n      const dragStartNode = extraData.dragNode;\n      const startInstance: RenderInstance | undefined = (\n        this.designRenderRef.current?.getInstancesById(dragStartNode?.id || '') || []\n      ).shift();\n      this.dragStartNode = dragStartNode || null;\n\n      const currentSelectDom = this.designRenderRef.current?.getDomsById(currentSelectInstance?._NODE_ID || '');\n      const dragStartDom = this.designRenderRef.current?.getDomsById(dragStartNode?.id || '');\n      // 新增节点\n      if (extraData?.dropType === 'NEW_ADD') {\n        this.setState({\n          currentSelectId: '',\n          currentSelectInstance: null,\n          selectComponentInstances: [],\n          hoverComponentInstances: [],\n        });\n        // 清空之前的选中\n        onSelectNode?.(null, eventObj.current);\n      } else if (currentSelectDom?.length && dragStartDom?.length) {\n        // dom 不存在\n        if (!startInstance) {\n          return;\n        }\n\n        // 如果当前选中的dom 不包含 拖动开始的元素\n        if (!currentSelectDom[0].contains(dragStartDom[0])) {\n          // 可以终止拖拽开始\n          if (this.props.onNodeDragStart) {\n            const res = await this.props.onNodeDragStart(eventObj);\n            if (res === false) {\n              this.cancelDrag(eventObj);\n              return;\n            }\n          }\n          this.setState({\n            currentSelectId: startInstance._NODE_ID,\n            currentSelectInstance: startInstance,\n            selectComponentInstances:\n              this.designRenderRef.current?.getInstancesById(startInstance?._NODE_ID || '') || [],\n            hoverComponentInstances: [],\n          });\n          onSelectNode?.(startInstance?._NODE_MODEL || null, eventObj.current);\n        } else {\n          this.dragStartNode = currentSelectInstance?._NODE_MODEL || null;\n          // 可以终止拖拽开始\n          if (this.props.onNodeDragStart) {\n            eventObj.extraData.dragNode = this.dragStartNode!;\n            eventObj.extraData.dragNodeUID = currentSelectInstance?._UNIQUE_ID;\n            const res = await this.props.onNodeDragStart(eventObj);\n            if (res === false) {\n              this.cancelDrag(eventObj);\n              return;\n            }\n          }\n          this.setState({\n            hoverComponentInstances: [],\n          });\n        }\n      } else if (!currentSelectDom?.length) {\n        // dom 不存在\n        if (!startInstance) {\n          return;\n        }\n        // 可以终止拖拽开始\n        if (this.props.onNodeDragStart) {\n          const res = await this.props.onNodeDragStart(eventObj);\n          if (res === false) {\n            this.cancelDrag(eventObj);\n            return;\n          }\n        }\n        // 没有选中元素时，当前拖动的元素为选中元素\n        this.setState({\n          currentSelectId: startInstance._NODE_ID,\n          currentSelectInstance: startInstance,\n          selectComponentInstances: this.designRenderRef.current?.getInstancesById(startInstance?._NODE_ID || '') || [],\n          hoverComponentInstances: [],\n        });\n        onSelectNode?.(startInstance?._NODE_MODEL || null, eventObj.current);\n      } else {\n        this.setState({\n          hoverComponentInstances: [],\n        });\n      }\n    });\n\n    sensor.emitter.on('dragging', async (e) => {\n      if (!this.designRenderRef.current || this.isCancelDrag || this.mode === LayoutMode.PREVIEW) {\n        return;\n      }\n      const extraData = e.extraData;\n      const res = await this.props.onNodeDragging?.(e);\n      if (res === false) {\n        this.cancelDrag(e);\n        return;\n      }\n      const componentInstance = (\n        this.designRenderRef.current.getInstancesById(extraData.dropNode?.id || '', extraData.dropNodeUID) || []\n      ).shift();\n\n      if (!componentInstance) {\n        this.setState({\n          dropComponentInstances: [],\n          dropPosInfos: [],\n          dropEvent: null,\n        });\n        return;\n      }\n      this.setState({\n        dropComponentInstances: [componentInstance],\n        dropPosInfos: [extraData.dropPosInfo!],\n        dropEvent: e,\n      });\n    });\n\n    sensor.emitter.on('dragEnd', (e) => {\n      this.resetDrag();\n      this.isCancelDrag = false;\n      this.props.onNodeDraEnd?.(e);\n    });\n\n    sensor.emitter.on('drop', async (e) => {\n      if (!this.designRenderRef.current || this.isCancelDrag) {\n        return;\n      }\n\n      const { dragNode } = e.extraData;\n\n      if (dragNode) {\n        const res = await this.props.onNodeNewAdd?.(e);\n        if (res === false) {\n          // 禁止添加\n          this.resetDrag();\n          return;\n        }\n      }\n\n      this.props.onNodeDrop?.(e);\n    });\n\n    // 监听所有感应区的鼠标移动事件\n    const onMouseMove = (e: { pointer: any }) => {\n      if (this.mode === LayoutMode.PREVIEW) {\n        this.setState({\n          mousePointer: null,\n          selectLockStyle: {},\n        });\n        return;\n      }\n      if (this.state.isDragging) {\n        this.setState({\n          mousePointer: e.pointer,\n          selectLockStyle: SELECT_LOCK_STYLE,\n        });\n      } else {\n        this.setState({\n          mousePointer: null,\n          selectLockStyle: {},\n        });\n      }\n    };\n\n    sensor.emitter.on('mouseMove', onMouseMove);\n    this.dnd.emitter.on('mouseMove', onMouseMove);\n  }\n\n  selectNode(nodeId: string) {\n    let instanceList = this.designRenderRef.current?.getInstancesById(nodeId) || [];\n    instanceList = instanceList.filter((el) => el?._STATUS !== 'DESTROY');\n    if (!instanceList.length) {\n      this.setState({\n        currentSelectId: '',\n        currentSelectInstance: null,\n        selectComponentInstances: [],\n        hoverComponentInstances: [],\n      });\n      return;\n    }\n    const instance = instanceList[0];\n    const dom = instance.getDom();\n    if (dom) {\n      dom.scrollIntoView?.({\n        block: 'nearest',\n      });\n    }\n    this.setState({\n      currentSelectId: instance._NODE_ID,\n      currentSelectInstance: instance,\n      selectComponentInstances: [...instanceList].filter((el) => {\n        let res: boolean | undefined;\n        const ins = this.designRenderRef.current?.renderRef?.current?.dynamicComponentInstanceMap.get(el._NODE_ID);\n\n        if (ins) {\n          res = ins._CONDITION;\n        }\n        return res !== false;\n      }),\n      hoverComponentInstances: [],\n    });\n    this.props.onSelectNode?.(instance?._NODE_MODEL as CNode, null);\n  }\n\n  clearSelectNode() {\n    this.setState({\n      currentSelectId: '',\n      currentSelectInstance: null,\n      selectComponentInstances: [],\n    });\n    // 清空之前的选中\n    this.props.onSelectNode?.(null, null);\n  }\n\n  resetDrag = () => {\n    this.dragStartNode = null;\n    this.setState({\n      isDragging: false,\n      mousePointer: null,\n      dropEvent: null,\n      dropComponentInstances: [],\n      selectLockStyle: {},\n    });\n  };\n\n  componentWillUnmount(): void {\n    this.eventExposeHandler.forEach((el) => el());\n    this.iframeContainer.iframe?.parentNode?.removeChild(this.iframeContainer.iframe);\n    this.disposeRealTimeUpdate();\n  }\n\n  async ready(cb?: (layoutInstance: Layout) => void) {\n    if (this.state.ready) {\n      cb?.(this);\n      return this;\n    } else {\n      return new Promise((resolve) => {\n        this.readyCbList.push((layoutInstance) => {\n          cb?.(layoutInstance);\n          resolve(layoutInstance);\n        });\n      });\n    }\n  }\n\n  selectRectViewItemRender: HighlightCanvasCoreProps['itemRender'] = (props) => {\n    if (this.mode === LayoutMode.PREVIEW) {\n      return <></>;\n    }\n    const { selectRectViewRender } = this.props;\n    const Comp = selectRectViewRender;\n    if (!Comp) {\n      return <></>;\n    }\n    return <Comp instance={props.instance} index={props.index} isLock={false} />;\n  };\n\n  hoverRectViewItemRender: HighlightCanvasCoreProps['itemRender'] = (props) => {\n    if (this.mode === LayoutMode.PREVIEW) {\n      return <></>;\n    }\n    const { hoverRectViewRender } = this.props;\n    const Comp = hoverRectViewRender;\n    if (!Comp) {\n      return <></>;\n    }\n    return <Comp instance={props.instance} index={props.index} isLock={false} />;\n  };\n\n  dropViewItemRender: DropAnchorPropsType['customDropViewRender'] = (props) => {\n    const { dropViewRender } = this.props;\n    const Comp = dropViewRender;\n    if (!Comp) {\n      return <></>;\n    }\n    return <Comp {...props} instance={props.instance} index={0} isLock={false} />;\n  };\n\n  render() {\n    const {\n      selectComponentInstances,\n      hoverComponentInstances,\n      dropComponentInstances,\n      dropPosInfos,\n      dropEvent,\n      selectLockStyle,\n      isDragging,\n      mousePointer,\n    } = this.state;\n    const { iframeDomId } = this;\n    const {\n      selectToolbarView,\n      hoverToolBarView,\n      selectBoxStyle = {},\n      hoverBoxStyle = {},\n      ghostView = <>Ghost</>,\n      selectRectViewRender,\n      hoverRectViewRender,\n      dropViewRender,\n    } = this.props;\n\n    let selectRectViewItemRender: HighlightCanvasCoreProps['itemRender'];\n    if (selectRectViewRender) {\n      selectRectViewItemRender = this.selectRectViewItemRender;\n    }\n    let hoverRectViewItemRender: HighlightCanvasCoreProps['itemRender'];\n    if (hoverRectViewRender) {\n      hoverRectViewItemRender = this.hoverRectViewItemRender;\n    }\n    let dropViewItemRender;\n\n    if (dropViewRender) {\n      dropViewItemRender = this.dropViewItemRender;\n    }\n    return (\n      <div className={styles.layoutContainer} id={iframeDomId}>\n        {/* 左上角添加显示元素名功能， hover */}\n        <HighlightCanvas\n          key={'highlightHoverCanvasRef'}\n          ref={this.highlightHoverCanvasRef}\n          instances={hoverComponentInstances}\n          style={{\n            pointerEvents: 'none',\n            position: 'absolute',\n            left: 0,\n            top: 0,\n            outline: '1px dashed rgba(0,0,255, .8)',\n            whiteSpace: 'nowrap',\n            ...hoverBoxStyle,\n          }}\n          toolbarView={hoverToolBarView}\n          itemRender={hoverRectViewItemRender}\n        />\n        {/* TODO:  选中框， 添加锁定功能 */}\n        <HighlightCanvas\n          ref={this.highlightCanvasRef}\n          instances={selectComponentInstances}\n          style={{\n            ...selectBoxStyle,\n            ...selectLockStyle,\n          }}\n          toolbarView={selectToolbarView}\n          itemRender={selectRectViewItemRender}\n        />\n\n        <DropAnchorCanvas\n          ref={this.highlightDropAnchorCanvasRef}\n          instances={dropComponentInstances}\n          mouseEvent={dropEvent}\n          dropInfos={dropPosInfos}\n          customDropViewRender={dropViewItemRender}\n        />\n        {isDragging && mousePointer && (\n          <div\n            style={{\n              position: 'fixed',\n              left: mousePointer.x - 5 + 'px',\n              top: mousePointer.y - 8 + 'px',\n              cursor: 'move',\n              pointerEvents: 'none',\n              zIndex: 999,\n            }}\n          >\n            {ghostView}\n          </div>\n        )}\n      </div>\n    );\n  }\n}\n\nexport * from './core/dragAndDrop';\nexport * from './core/iframeContainer';\nexport * from './utils';\nexport * from './types';\n"
  },
  {
    "path": "packages/layout/src/render.ts",
    "content": "import * as CRender from '@chamn/render';\n\nexport default CRender;\n"
  },
  {
    "path": "packages/layout/src/types/dragAndDrop.ts",
    "content": "import { Pointer } from '../core/dragAndDrop/common';\nimport { Sensor } from '../core/dragAndDrop/sensor';\nimport { DragAndDropEventExtraData } from '@chamn/model';\n\nexport type DragAndDropEventObj<T = Record<string, any>> = {\n  from: MouseEvent;\n  fromSensor: Sensor;\n  fromPointer: Pointer;\n  current?: MouseEvent;\n  currentSensor?: Sensor;\n  pointer: Pointer;\n  extraData: T;\n};\n\nexport type BaseDragAndDropEventType<T = Record<string, any>> = {\n  dragStart: DragAndDropEventObj<T>;\n  dragging: Required<DragAndDropEventObj<T>>;\n  dragEnd: Required<DragAndDropEventObj<T>>;\n  drop: Required<DragAndDropEventObj<T>>;\n};\n\nexport type LayoutDragAndDropExtraDataType = DragAndDropEventExtraData;\n"
  },
  {
    "path": "packages/layout/src/types/index.ts",
    "content": "export * from './dragAndDrop';\n"
  },
  {
    "path": "packages/layout/src/typing.d.ts",
    "content": "declare module '*.scss' {\n  const content: { [key: string]: any };\n  export = content;\n}\n"
  },
  {
    "path": "packages/layout/src/utils/index.ts",
    "content": "export function addEventListenerReturnCancel<K extends keyof HTMLElementEventMap>(\n  dom: HTMLElement | Document | Window,\n  type: K,\n  listener: (ev: MouseEvent) => any,\n  options?: boolean | AddEventListenerOptions\n) {\n  dom.addEventListener(\n    type,\n    (e) => {\n      // 跳过修复后的事件触发\n      if ((e as any)?.fixed) {\n        return;\n      }\n      listener(e as MouseEvent);\n    },\n    options\n  );\n  return () => {\n    dom.removeEventListener(type, listener as any);\n  };\n}\n\nexport const isDOM = (dom: unknown) => {\n  let cb;\n  if (typeof HTMLElement === 'object') {\n    cb = function (obj: unknown) {\n      return obj instanceof HTMLElement;\n    };\n  } else {\n    cb = function (obj: any) {\n      return obj && typeof obj === 'object' && obj.nodeType === 1 && typeof obj.nodeName === 'string';\n    };\n  }\n  return cb(dom);\n};\n\nexport const animationFrame = (stepCb: () => void) => {\n  let lastTimestamp = 0;\n  const interval = 0; // 间隔 100ms\n  let handle = true;\n  const innerCb = (timestamp: number) => {\n    if (timestamp - lastTimestamp >= interval) {\n      lastTimestamp = timestamp;\n      if (handle) {\n        stepCb();\n      }\n    }\n    requestAnimationFrame(innerCb);\n  };\n  requestAnimationFrame(innerCb);\n  return () => {\n    handle = false;\n  };\n};\n"
  },
  {
    "path": "packages/layout/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ESNext\",\n    \"useDefineForClassFields\": true,\n    \"lib\": [\n      \"DOM\",\n      \"DOM.Iterable\",\n      \"ESNext\"\n    ],\n    \"allowJs\": false,\n    \"skipLibCheck\": true,\n    \"esModuleInterop\": false,\n    \"allowSyntheticDefaultImports\": true,\n    \"strict\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": false,\n    \"noEmit\": true,\n    \"jsx\": \"react\"\n  },\n  \"include\": [\n    \"src\"\n  ],\n}"
  },
  {
    "path": "packages/material/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "packages/material/.prettierrc.json",
    "content": "{\n  \"trailingComma\": \"es5\",\n  \"tabWidth\": 2,\n  \"semi\": true,\n  \"singleQuote\": true\n}"
  },
  {
    "path": "packages/material/CHANGELOG.md",
    "content": "# Change Log\n\nAll notable changes to this project will be documented in this file.\nSee [Conventional Commits](https://conventionalcommits.org) for commit guidelines.\n\n## [0.10.4](https://github.com/ByteCrazy/chameleon/compare/v0.10.3...v0.10.4) (2026-01-17)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.10.3](https://github.com/ByteCrazy/chameleon/compare/v0.10.2...v0.10.3) (2026-01-17)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.10.2](https://github.com/ByteCrazy/chameleon/compare/v0.10.1...v0.10.2) (2026-01-17)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.10.1](https://github.com/ByteCrazy/chameleon/compare/v0.10.0...v0.10.1) (2026-01-11)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.10.0](https://github.com/hlerenow/chameleon/compare/v0.9.3...v0.10.0) (2025-12-25)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* 优化构建配置，修复 ES 模块外部化问题 ([866f0ae](https://github.com/hlerenow/chameleon/commit/866f0ae39472625137d73d143625d88d873f6c00))\n\n## [0.9.3](https://github.com/ByteCrazy/chameleon/compare/v0.9.2...v0.9.3) (2025-07-20)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.9.2](https://github.com/ByteCrazy/chameleon/compare/v0.9.1...v0.9.2) (2025-07-13)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.9.1](https://github.com/ByteCrazy/chameleon/compare/v0.9.0...v0.9.1) (2025-07-13)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.9.0](https://github.com/ByteCrazy/chameleon/compare/v0.8.6...v0.9.0) (2025-07-13)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.8.6](https://github.com/ByteCrazy/chameleon/compare/v0.8.5...v0.8.6) (2025-06-21)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.8.5](https://github.com/ByteCrazy/chameleon/compare/v0.8.4...v0.8.5) (2025-04-13)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.8.4](https://github.com/ByteCrazy/chameleon/compare/v0.8.3...v0.8.4) (2025-04-13)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.8.3](https://github.com/ByteCrazy/chameleon/compare/v0.8.2...v0.8.3) (2025-04-12)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.8.2](https://github.com/ByteCrazy/chameleon/compare/v0.8.1...v0.8.2) (2025-04-11)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.8.1](https://github.com/ByteCrazy/chameleon/compare/v0.8.0...v0.8.1) (2025-04-10)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.8.0](https://github.com/ByteCrazy/chameleon/compare/v0.7.0...v0.8.0) (2025-04-10)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.7.0](https://github.com/ByteCrazy/chameleon/compare/v0.6.0...v0.7.0) (2025-04-06)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.6.0](https://github.com/ByteCrazy/chameleon/compare/v0.5.2...v0.6.0) (2025-03-30)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.5.2](https://github.com/ByteCrazy/chameleon/compare/v0.5.1...v0.5.2) (2025-03-30)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.5.1](https://github.com/ByteCrazy/chameleon/compare/v0.5.0...v0.5.1) (2025-03-30)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.5.0](https://github.com/ByteCrazy/chameleon/compare/v0.4.0...v0.5.0) (2025-03-30)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.4.0](https://github.com/ByteCrazy/chameleon/compare/v0.3.21...v0.4.0) (2025-03-29)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.3.21](https://github.com/ByteCrazy/chameleon/compare/v0.3.20...v0.3.21) (2025-03-26)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, material:** 🐛 fixed RGLEL cycle update item ([f045818](https://github.com/ByteCrazy/chameleon/commit/f045818d6d1e1b4dbcf107d727976f6d92600b94))\n\n## [0.3.20](https://github.com/ByteCrazy/chameleon/compare/v0.3.19...v0.3.20) (2025-03-26)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **material:** 🐛 fixed meterial meta not export ([aaea9bd](https://github.com/ByteCrazy/chameleon/commit/aaea9bd8fb95aa007b3b6ccd06e4b3171af35926))\n\n## [0.3.19](https://github.com/ByteCrazy/chameleon/compare/v0.3.18...v0.3.19) (2025-03-26)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.3.18](https://github.com/ByteCrazy/chameleon/compare/v0.3.17...v0.3.18) (2025-03-26)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.3.17](https://github.com/ByteCrazy/chameleon/compare/v0.3.16...v0.3.17) (2025-03-25)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, material, model, render:** 🐛 fixed node update value material is undefined ([969174d](https://github.com/ByteCrazy/chameleon/commit/969174da968aec4a1ee1fec7ca44a5e459be56bc))\n\n## [0.3.16](https://github.com/ByteCrazy/chameleon/compare/v0.3.15...v0.3.16) (2025-03-24)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.3.15](https://github.com/ByteCrazy/chameleon/compare/v0.3.14...v0.3.15) (2025-03-23)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.3.14](https://github.com/ByteCrazy/chameleon/compare/v0.3.13...v0.3.14) (2025-03-23)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.3.13](https://github.com/ByteCrazy/chameleon/compare/v0.3.12...v0.3.13) (2025-03-23)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.3.12](https://github.com/ByteCrazy/chameleon/compare/v0.3.11...v0.3.12) (2025-03-23)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.3.11](https://github.com/ByteCrazy/chameleon/compare/v0.3.10...v0.3.11) (2025-03-22)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.3.10](https://github.com/ByteCrazy/chameleon/compare/v0.3.9...v0.3.10) (2025-03-22)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.3.9](https://github.com/ByteCrazy/chameleon/compare/v0.3.8...v0.3.9) (2025-03-22)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.3.8](https://github.com/ByteCrazy/chameleon/compare/v0.3.7...v0.3.8) (2025-03-16)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.3.7](https://github.com/ByteCrazy/chameleon/compare/v0.3.6...v0.3.7) (2025-03-16)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.3.6](https://github.com/ByteCrazy/chameleon/compare/v0.3.5...v0.3.6) (2025-03-09)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.3.5](https://github.com/ByteCrazy/chameleon/compare/v0.3.4...v0.3.5) (2025-03-09)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.3.4](https://github.com/ByteCrazy/chameleon/compare/v0.3.3...v0.3.4) (2025-02-16)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.3.3](https://github.com/ByteCrazy/chameleon/compare/v0.3.2...v0.3.3) (2025-02-16)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.3.2](https://github.com/ByteCrazy/chameleon/compare/v0.3.1...v0.3.2) (2025-02-16)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.3.1](https://github.com/ByteCrazy/chameleon/compare/v0.3.0...v0.3.1) (2025-02-16)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.3.0](https://github.com/ByteCrazy/chameleon/compare/v0.2.4...v0.3.0) (2025-02-15)\n\n### ✨ Features | 新功能\n\n* **docs-app, engine, engine-website-app, layout, material, model, render:** 🎸 do ActionFlowSetter 50% ([6399466](https://github.com/ByteCrazy/chameleon/commit/6399466c7253436591e071df25828a357bb7089e))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, engine-website-app, material, model, render:** 🐛 fixed GRL hidden offsetY loop add size ([3df132b](https://github.com/ByteCrazy/chameleon/commit/3df132b6493026b42435d4868d77915b9f7316b2))\n\n## [0.2.4](https://github.com/ByteCrazy/chameleon/compare/v0.2.3...v0.2.4) (2024-12-08)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, material:** 🐛 fixed RGL init layout not correcnt and upgrade gridstack ([6b66b47](https://github.com/ByteCrazy/chameleon/commit/6b66b47b37e1fcd96602132bb44373a01d5de946))\n\n## [0.2.3](https://github.com/ByteCrazy/chameleon/compare/v0.2.2...v0.2.3) (2024-12-08)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.2.2](https://github.com/ByteCrazy/chameleon/compare/v0.2.1...v0.2.2) (2024-12-07)\n\n### ✨ Features | 新功能\n\n* **material:** 🎸 optimize RGL ([518402f](https://github.com/ByteCrazy/chameleon/commit/518402ff0d173e60fd31430222aea8e5856fa0da))\n\n## [0.2.1](https://github.com/ByteCrazy/chameleon/compare/v0.2.0...v0.2.1) (2024-12-07)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, engine-website-app, material, model, render:** 🎸 fixed RGL component and optimize project struct ([99f0679](https://github.com/ByteCrazy/chameleon/commit/99f0679d93a2fd696034ebed8f1abcd9d9e601d4))\n\n## [0.2.0](https://github.com/ByteCrazy/chameleon/compare/v0.1.1...v0.2.0) (2024-12-07)\n\n### ✨ Features | 新功能\n\n* **build-script, demo-page, engine, engine-website-app, layout, material, model, render:** 🎸 upgrade vite to 6.0 ([bcac2b1](https://github.com/ByteCrazy/chameleon/commit/bcac2b15b83b41a7042ca37368c1b45302ad81d5))\n\n## [0.1.1](https://github.com/ByteCrazy/chameleon/compare/v0.1.0...v0.1.1) (2024-11-11)\n\n### ✨ Features | 新功能\n\n* **material:** 🎸 optimize material gridItem UI ([34b8b8e](https://github.com/ByteCrazy/chameleon/commit/34b8b8e1b09aac22e538eff44488dc2021a5add6))\n\n## [0.1.0](https://github.com/ByteCrazy/chameleon/compare/v0.0.46...v0.1.0) (2024-09-07)\n\n### 👷 Continuous Integration | CI 配置\n\n* **build-script, docs-website, material, render:** 🎡 not build docs ([37aa10a](https://github.com/ByteCrazy/chameleon/commit/37aa10abf0324f3465a6921a9a014875e9500f8f))\n\n### ✨ Features | 新功能\n\n* **demo-page, engine, engine-website-app, material:** 🎸 add designerSizer and fixed GridItem bug ([4665aed](https://github.com/ByteCrazy/chameleon/commit/4665aed300d54c77be4abcb9a8cc0f1710ac2145))\n* **docs-app, engine, layout, material, model, render:** 🎸 optimize GL layout ([02274b4](https://github.com/ByteCrazy/chameleon/commit/02274b432903dc247c5613873f14715dd806decd))\n* **engine-website-app, material:** 🎸 optimize RGL ([dd04999](https://github.com/ByteCrazy/chameleon/commit/dd04999a1509e29d6d738ea3a4250a146bfc295e))\n* **engine, material:** 🎸 add GRL meterial ([ae15c34](https://github.com/ByteCrazy/chameleon/commit/ae15c34a3f2736db61a933dc7d09166d9a619473))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **demo-page, docs-app, engine, engine-website-app, layout, material:** 🐛 fixed material meta not correct ([55b755c](https://github.com/ByteCrazy/chameleon/commit/55b755ce0c833e594b46447b2d6608cf56f7a593))\n* **engine, engine-website-app, material:** 🐛 fixed ReactGridLayout edit mode lable not correct ([9801839](https://github.com/ByteCrazy/chameleon/commit/9801839d9ff6a6548b0e23f1e31850cd56e7fff0))\n* **material:** 🐛 fixed GridItem copy ([70f9f68](https://github.com/ByteCrazy/chameleon/commit/70f9f6811da3dd46707e0a7304182b807eb745c9))\n* **material:** 🐛 remove inner pkg version ([eefcf7e](https://github.com/ByteCrazy/chameleon/commit/eefcf7e11e8405e1d96fdf0c1abdd8959473db1c))\n\n## [0.0.46](https://github.com/ByteCrazy/chameleon/compare/v0.0.45...v0.0.46) (2024-06-30)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.45](https://github.com/ByteCrazy/chameleon/compare/v0.0.44...v0.0.45) (2024-06-30)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.44](https://github.com/ByteCrazy/chameleon/compare/v0.0.43...v0.0.44) (2024-06-30)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.43](https://github.com/ByteCrazy/chameleon/compare/v0.0.42...v0.0.43) (2024-06-29)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.42](https://github.com/ByteCrazy/chameleon/compare/v0.0.41...v0.0.42) (2024-06-01)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.41](https://github.com/ByteCrazy/chameleon/compare/v0.0.40...v0.0.41) (2024-05-26)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, layout, material, model, render:** 🎸 optimize build script ([0a60cc4](https://github.com/ByteCrazy/chameleon/commit/0a60cc4f5e5635d9e544b5141eaed5b8433901a5))\n\n## [0.0.40](https://github.com/ByteCrazy/chameleon/compare/v0.0.39...v0.0.40) (2024-04-28)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.39](https://github.com/ByteCrazy/chameleon/compare/v0.0.38...v0.0.39) (2024-04-27)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **build-script, demo-page, engine, layout, material, model, render:** 🐛 fix flatObject collectVariable refeer pos ([1c4163a](https://github.com/ByteCrazy/chameleon/commit/1c4163aeeb89176a1ff5b50f76930889df7751fe))\n\n## [0.0.38](https://github.com/ByteCrazy/chameleon/compare/v0.0.37...v0.0.38) (2024-04-27)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.37](https://github.com/ByteCrazy/chameleon/compare/v0.0.36...v0.0.37) (2024-04-27)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.36](https://github.com/ByteCrazy/chameleon/compare/v0.0.35...v0.0.36) (2024-04-27)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.35](https://github.com/ByteCrazy/chameleon/compare/v0.0.34...v0.0.35) (2024-04-27)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.34](https://github.com/ByteCrazy/chameleon/compare/v0.0.33...v0.0.34) (2024-04-27)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.33](https://github.com/ByteCrazy/chameleon/compare/v0.0.32...v0.0.33) (2024-04-27)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.32](https://github.com/ByteCrazy/chameleon/compare/v0.0.31...v0.0.32) (2024-01-30)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.31](https://github.com/ByteCrazy/chameleon/compare/v0.0.30...v0.0.31) (2023-10-17)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.30](https://github.com/ByteCrazy/chameleon/compare/v0.0.29...v0.0.30) (2023-10-17)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.29](https://github.com/ByteCrazy/chameleon/compare/v0.0.28...v0.0.29) (2023-10-17)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.28](https://github.com/ByteCrazy/chameleon/compare/v0.0.27...v0.0.28) (2023-08-29)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.27](https://github.com/ByteCrazy/chameleon/compare/v0.0.26...v0.0.27) (2023-08-28)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.26](https://github.com/ByteCrazy/chameleon/compare/v0.0.25...v0.0.26) (2023-08-27)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.25](https://github.com/ByteCrazy/chameleon/compare/v0.0.24...v0.0.25) (2023-08-27)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.24](https://github.com/ByteCrazy/chameleon/compare/v0.0.23...v0.0.24) (2023-08-27)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.23](https://github.com/ByteCrazy/chameleon/compare/v0.0.22...v0.0.23) (2023-08-26)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.22](https://github.com/ByteCrazy/chameleon/compare/v0.0.21...v0.0.22) (2023-08-24)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.21](https://github.com/ByteCrazy/chameleon/compare/v0.0.20...v0.0.21) (2023-08-19)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.20](https://github.com/ByteCrazy/chameleon/compare/v0.0.19...v0.0.20) (2023-08-19)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.19](https://github.com/ByteCrazy/chameleon/compare/v0.0.18...v0.0.19) (2023-08-17)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.18](https://github.com/ByteCrazy/chameleon/compare/v0.0.17...v0.0.18) (2023-08-14)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.17](https://github.com/ByteCrazy/chameleon/compare/v0.0.16...v0.0.17) (2023-08-06)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.16](https://github.com/ByteCrazy/chameleon/compare/v0.0.15...v0.0.16) (2023-08-06)\n\n### ✨ Features | 新功能\n\n* **build-script-example, demo-page, engine, layout, material, model, render:** 🎸 support direct import render from package on dev mode ([6bb38e2](https://github.com/ByteCrazy/chameleon/commit/6bb38e29678a8a4d08a7fcf9548fa548399259b0))\n\n## [0.0.15](https://github.com/ByteCrazy/chameleon/compare/v0.0.14...v0.0.15) (2023-07-01)\n\n### ✨ Features | 新功能\n\n* **docs-website, engine, layout, material, render:** 🎸 support inject thridLib on $context ([ca2f074](https://github.com/ByteCrazy/chameleon/commit/ca2f07492b713c32e5a41d1f250f7888763cb665))\n\n## [0.0.14](https://github.com/ByteCrazy/chameleon/compare/v0.0.13...v0.0.14) (2023-05-10)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.13](https://github.com/ByteCrazy/chameleon/compare/v0.0.12...v0.0.13) (2023-05-03)\n\n### ✨ Features | 新功能\n\n* **build-script, build-script-example, demo-page, engine, layout, material, model, render:** 🎸 config code style config ([2325504](https://github.com/ByteCrazy/chameleon/commit/23255048fa4a3d4fc8f5cfa1312db5abd6cac70d))\n\n## [0.0.12](https://github.com/ByteCrazy/chameleon/compare/v0.0.11...v0.0.12) (2023-04-23)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **build-script, build-script-example, engine, layout, material, model, render:** 🐛 fix can not find vite/client.d.ts problem ([8a53540](https://github.com/ByteCrazy/chameleon/commit/8a53540f75737707f69f93d2f701203dbf88084a))\n\n## [0.0.11](https://github.com/ByteCrazy/chameleon/compare/v0.0.10...v0.0.11) (2023-04-21)\n\n### ✨ Features | 新功能\n\n* **build-script, build-script-example, demo-page, engine, layout, material, model, render:** 🎸 optimize build-script output file format ([eda44d2](https://github.com/ByteCrazy/chameleon/commit/eda44d255bd3c5af19013bdd61342e1f0817413a))\n\n## [0.0.10](https://github.com/ByteCrazy/chameleon/compare/v0.0.9...v0.0.10) (2023-04-17)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.9](https://github.com/ByteCrazy/chameleon/compare/v0.0.8...v0.0.9) (2023-04-17)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.8](https://github.com/ByteCrazy/chameleon/compare/v0.0.7...v0.0.8) (2023-04-16)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.7](https://github.com/ByteCrazy/chameleon/compare/v0.0.6...v0.0.7) (2023-03-29)\n\n### ✨ Features | 新功能\n\n* **build-script, build-script-example, demo-page, engine, layout, material, model, render:** 🎸 external monaco eitor and remove style panel ([494d898](https://github.com/ByteCrazy/chameleon/commit/494d898fd75dabe84d867ff45e84ae63f9b59c5e))\n\n## [0.0.6](https://github.com/ByteCrazy/chameleon/compare/v0.0.5...v0.0.6) (2023-03-28)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.5](https://github.com/ByteCrazy/chameleon/compare/v0.0.4...v0.0.5) (2023-03-28)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, layout, material, model, render:** 🎸 complete package info ([86b095d](https://github.com/ByteCrazy/chameleon/commit/86b095da6c955946300591b1775f59c1a141c065))\n\n## [0.0.4](https://github.com/ByteCrazy/chameleon/compare/v0.0.3...v0.0.4) (2023-03-27)\n\n**Note:** Version bump only for package @chamn/material\n\n## [0.0.2](https://github.com/ByteCrazy/chameleon/compare/v0.0.1...v0.0.2) (2023-03-27)\n\n**Note:** Version bump only for package @chamn/material\n"
  },
  {
    "path": "packages/material/LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "packages/material/__tests__/demo.test.ts",
    "content": "test('adds 1 + 2 to equal 3', () => {\n  expect(3).toBe(3);\n});\n"
  },
  {
    "path": "packages/material/build.config.ts",
    "content": "import pgk from './package.json';\nimport pluginExternal from 'vite-plugin-external';\n\nconst GLOBAL_LIB_NAME = 'ChamnCommonComponents';\n\nconst envDefine = {\n  __PACKAGE_VERSION__: JSON.stringify(pgk.version),\n  __PACKAGE_NAME__: JSON.stringify(pgk.name),\n  'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),\n  __GLOBAL_LIB_NAME__: JSON.stringify(GLOBAL_LIB_NAME),\n};\n// 开发模式默认读取 index.html 作为开发模式入口\n// entry 作为打包库入口\nconst LIB_NAME = process.env.LIB_NAME;\nlet buildConfig: any = {\n  entry: './src/index.tsx',\n  vite: {\n    define: envDefine,\n    plugins: [\n      pluginExternal({\n        externals: {\n          react: 'React',\n          'react-dom/client': 'ReactDOM',\n        },\n      }),\n    ],\n  },\n};\n\nif (LIB_NAME) {\n  const libConfig = {\n    entry: './src/index.tsx',\n    libName: GLOBAL_LIB_NAME,\n    // formats 会根据构建模式自动设置：生产模式包含 umd，开发模式只有 cjs 和 es\n    global: {\n      react: 'React',\n      'react-dom/client': 'ReactDOM',\n      'react-dom': 'ReactDOM',\n    },\n    // 额外的 vite 配置\n    vite: {\n      define: envDefine,\n    },\n  };\n\n  const metaConfig = {\n    entry: './src/meta.tsx',\n    libName: `${GLOBAL_LIB_NAME}Meta`,\n    formats: ['es', 'cjs'],\n    fileName: 'meta',\n    external: ['react'],\n    global: {\n      react: 'React',\n    },\n    // 额外的 vite 配置\n    vite: {\n      build: {\n        emptyOutDir: false,\n        assetFileNames: (assetInfo) => {\n          if (assetInfo.name === 'style.css' && LIB_NAME !== 'index')\n            return `${LIB_NAME}.css`;\n          return assetInfo.name;\n        },\n      },\n      define: envDefine,\n    },\n  };\n\n  buildConfig = libConfig;\n  if (process.env.LIB_NAME === 'meta') {\n    buildConfig = metaConfig;\n  }\n}\n\nexport default buildConfig;\n"
  },
  {
    "path": "packages/material/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n  <title>Chameleon Material develop</title>\n  <script src=\"/node_modules/react/umd/react.development.js\"></script>\n  <script src=\"/node_modules/react-dom/umd/react-dom.development.js\"></script>\n</head>\n\n<body>\n  <div id=\"root\"></div>\n  <script>\n    console.log(window.React)\n    console.log(window.ReactDOM)\n  </script>\n  <script type=\"module\" src=\"./src/_dev_/index.tsx\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "packages/material/jest.config.js",
    "content": "/* eslint-disable no-undef */\n/*\n * For a detailed explanation regarding each configuration property, visit:\n * https://jestjs.io/docs/configuration\n */\n\nmodule.exports = {\n  // All imported modules in your tests should be mocked automatically\n  // automock: false,\n\n  // Stop running tests after `n` failures\n  // bail: 0,\n\n  // The directory where Jest should store its cached dependency information\n  // cacheDirectory: \"/private/var/folders/l9/th_r5d_12wxdj16859_mctjw0000gn/T/jest_dx\",\n\n  // Automatically clear mock calls, instances, contexts and results before every test\n  // clearMocks: false,\n\n  // Indicates whether the coverage information should be collected while executing the test\n  // collectCoverage: false,\n\n  // An array of glob patterns indicating a set of files for which coverage information should be collected\n  // collectCoverageFrom: undefined,\n\n  // The directory where Jest should output its coverage files\n  // coverageDirectory: undefined,\n\n  // An array of regexp pattern strings used to skip coverage collection\n  // coveragePathIgnorePatterns: [\n  //   \"/node_modules/\"\n  // ],\n\n  // Indicates which provider should be used to instrument code for coverage\n  // coverageProvider: \"babel\",\n\n  // A list of reporter names that Jest uses when writing coverage reports\n  // coverageReporters: [\n  //   \"json\",\n  //   \"text\",\n  //   \"lcov\",\n  //   \"clover\"\n  // ],\n\n  // An object that configures minimum threshold enforcement for coverage results\n  // coverageThreshold: undefined,\n\n  // A path to a custom dependency extractor\n  // dependencyExtractor: undefined,\n\n  // Make calling deprecated APIs throw helpful error messages\n  // errorOnDeprecated: false,\n\n  // The default configuration for fake timers\n  // fakeTimers: {\n  //   \"enableGlobally\": false\n  // },\n\n  // Force coverage collection from ignored files using an array of glob patterns\n  // forceCoverageMatch: [],\n\n  // A path to a module which exports an async function that is triggered once before all test suites\n  // globalSetup: undefined,\n\n  // A path to a module which exports an async function that is triggered once after all test suites\n  // globalTeardown: undefined,\n\n  // A set of global variables that need to be available in all test environments\n  // globals: {},\n\n  // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.\n  // maxWorkers: \"50%\",\n\n  // An array of directory names to be searched recursively up from the requiring module's location\n  // moduleDirectories: [\n  //   \"node_modules\"\n  // ],\n\n  // An array of file extensions your modules use\n  // moduleFileExtensions: [\n  //   \"js\",\n  //   \"mjs\",\n  //   \"cjs\",\n  //   \"jsx\",\n  //   \"ts\",\n  //   \"tsx\",\n  //   \"json\",\n  //   \"node\"\n  // ],\n\n  // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module\n  // moduleNameMapper: {},\n\n  // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader\n  // modulePathIgnorePatterns: [],\n\n  // Activates notifications for test results\n  // notify: false,\n\n  // An enum that specifies notification mode. Requires { notify: true }\n  // notifyMode: \"failure-change\",\n\n  // A preset that is used as a base for Jest's configuration\n  preset: 'ts-jest',\n\n  // Run tests from one or more projects\n  // projects: undefined,\n\n  // Use this configuration option to add custom reporters to Jest\n  // reporters: undefined,\n\n  // Automatically reset mock state before every test\n  // resetMocks: false,\n\n  // Reset the module registry before running each individual test\n  // resetModules: false,\n\n  // A path to a custom resolver\n  // resolver: undefined,\n\n  // Automatically restore mock state and implementation before every test\n  // restoreMocks: false,\n\n  // The root directory that Jest should scan for tests and modules within\n  // rootDir: undefined,\n\n  // A list of paths to directories that Jest should use to search for files in\n  // roots: [\n  //   \"<rootDir>\"\n  // ],\n\n  // Allows you to use a custom runner instead of Jest's default test runner\n  // runner: \"jest-runner\",\n\n  // The paths to modules that run some code to configure or set up the testing environment before each test\n  // setupFiles: [],\n\n  // A list of paths to modules that run some code to configure or set up the testing framework before each test\n  // setupFilesAfterEnv: [],\n\n  // The number of seconds after which a test is considered as slow and reported as such in the results.\n  // slowTestThreshold: 5,\n\n  // A list of paths to snapshot serializer modules Jest should use for snapshot testing\n  // snapshotSerializers: [],\n\n  // The test environment that will be used for testing\n  testEnvironment: 'jsdom',\n\n  // Options that will be passed to the testEnvironment\n  // testEnvironmentOptions: {},\n\n  // Adds a location field to test results\n  // testLocationInResults: false,\n\n  // The glob patterns Jest uses to detect test files\n  // testMatch: [\n  //   \"**/__tests__/**/*.[jt]s?(x)\",\n  //   \"**/?(*.)+(spec|test).[tj]s?(x)\"\n  // ],\n\n  // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped\n  // testPathIgnorePatterns: [\n  //   \"/node_modules/\"\n  // ],\n\n  // The regexp pattern or array of patterns that Jest uses to detect test files\n  // testRegex: [],\n\n  // This option allows the use of a custom results processor\n  // testResultsProcessor: undefined,\n\n  // This option allows use of a custom test runner\n  // testRunner: \"jest-circus/runner\",\n\n  // A map from regular expressions to paths to transformers\n  // transform: undefined,\n\n  // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation\n  // transformIgnorePatterns: [\n  //   \"/node_modules/\",\n  //   \"\\\\.pnp\\\\.[^\\\\/]+$\"\n  // ],\n\n  // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them\n  // unmockedModulePathPatterns: undefined,\n\n  // Indicates whether each individual test should be reported during the run\n  // verbose: undefined,\n\n  // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode\n  // watchPathIgnorePatterns: [],\n\n  // Whether to use watchman for file crawling\n  // watchman: true,\n};\n"
  },
  {
    "path": "packages/material/package.json",
    "content": "{\n  \"name\": \"@chamn/material\",\n  \"private\": false,\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"type\": \"module\",\n  \"files\": [\n    \"dist\"\n  ],\n  \"version\": \"0.10.4\",\n  \"main\": \"dist/index.cjs.js\",\n  \"module\": \"dist/index.es.js\",\n  \"types\": \"dist/index.d.ts\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./dist/index.d.ts\",\n      \"module-sync\": \"./dist/index.es.js\",\n      \"import\": \"./dist/index.es.js\",\n      \"require\": \"./dist/index.cjs.js\"\n    },\n    \"./dist/meta\": {\n      \"types\": \"./dist/meta.d.ts\",\n      \"module-sync\": \"./dist/meta.es.js\",\n      \"import\": \"./dist/meta.es.js\",\n      \"require\": \"./dist/meta.cjs.js\"\n    },\n    \"./dist/*\": \"./dist/*\"\n  },\n  \"scripts\": {\n    \"start\": \"cross-env DEV=true build-script\",\n    \"dev\": \"cross-env DEV=true build-script\",\n    \"build\": \"pnpm run build:lib && pnpm run build:meta\",\n    \"build:lib\": \"cross-env LIB_NAME=comp  build-script --build\",\n    \"build:meta\": \"cross-env LIB_NAME=meta build-script --build\",\n    \"lint\": \"eslint ./src\",\n    \"prettier\": \"prettier --write ./src\",\n    \"test\": \"jest\"\n  },\n  \"peerDependencies\": {\n    \"react\": \">=16.9.0\",\n    \"react-dom\": \">=16.9.0\"\n  },\n  \"dependencies\": {\n    \"antd\": \"^5.23.2\",\n    \"axios\": \"^1.8.4\",\n    \"clsx\": \"^2.1.1\",\n    \"gridstack\": \"^11.1.1\",\n    \"loadjs\": \"^4.3.0\",\n    \"lodash-es\": \"^4.17.21\",\n    \"react\": \"^18.2.0\",\n    \"react-dom\": \"^18.2.0\",\n    \"vite\": \"^6.2.2\"\n  },\n  \"devDependencies\": {\n    \"@ant-design/icons\": \"^5.0.1\",\n    \"@chamn/build-script\": \"workspace:*\",\n    \"@chamn/engine\": \"workspace:*\",\n    \"@chamn/model\": \"workspace:*\",\n    \"@chamn/render\": \"workspace:*\",\n    \"@monaco-editor/react\": \"^4.5.1\",\n    \"@types/loadjs\": \"^4.0.4\",\n    \"@types/lodash-es\": \"^4.17.12\",\n    \"@types/node\": \"~18.15.9\",\n    \"@types/react\": \"^18.0.28\",\n    \"@types/react-dom\": \"^18.0.11\",\n    \"@vitejs/plugin-react\": \"^3.1.0\",\n    \"cross-env\": \"^7.0.3\",\n    \"react-router-dom\": \"^6.10.0\",\n    \"sass\": \"^1.62.0\",\n    \"vite-plugin-external\": \"^6.0.1\"\n  },\n  \"config\": {},\n  \"gitHead\": \"dc3e55fdeb903a8012f6ebd3ebc018ed61ad89db\"\n}\n"
  },
  {
    "path": "packages/material/src/_dev_/editor.tsx",
    "content": "import { Button, message, Modal } from 'antd';\nimport React, { useCallback, useEffect, useState } from 'react';\nimport ReactDOM from 'react-dom';\nimport ReactDOMClient from 'react-dom/client';\nimport '@chamn/engine/dist/style.css';\nimport customMaterial from '../meta';\nimport {\n  Engine,\n  DesignerSizer,\n  EnginContext,\n  InnerComponentMeta,\n  plugins,\n  LayoutPropsType,\n} from '@chamn/engine';\nimport { RollbackOutlined } from '@ant-design/icons';\nimport { EmptyPage } from '@chamn/model';\nimport pkg from '../../package.json';\nimport { DesignerPluginInstance } from '@chamn/engine/dist/plugins/Designer/type';\nimport { collectVariable, flatObject, getThirdLibs } from '@chamn/render';\nimport renderAsURL from '../../node_modules/@chamn/render/dist/index.umd.js?url';\nimport { loader } from '@monaco-editor/react';\nimport * as componentLibs from '../components/index';\n\nloader.config({\n  paths: {\n    vs: 'https://cdn.jsdelivr.net/npm/monaco-editor@0.37.1/min/vs',\n  },\n});\nloader.init();\n\nconst { DisplaySourceSchema, DEFAULT_PLUGIN_LIST } = plugins;\n\nconst customRender: LayoutPropsType['customRender'] = async ({\n  iframe: iframeContainer,\n  assets,\n  page,\n  pageModel,\n  beforeInitRender,\n  ready,\n}) => {\n  await iframeContainer.loadUrl('/src/_dev_/render.html');\n  beforeInitRender?.();\n  const iframeWindow = iframeContainer.getWindow()!;\n  const iframeDoc = iframeContainer.getDocument()!;\n  (iframeWindow as any).ChamnCustomComponent = componentLibs;\n\n  const IframeReact = iframeWindow.React!;\n  const IframeReactDOM = iframeWindow.ReactDOMClient!;\n  const CRender = iframeWindow.CRender!;\n\n  console.log(\n    '[parent View] load render.umd.js is success',\n    iframeWindow.CRender\n  );\n\n  // 从子窗口获取物料对象\n  const componentCollection = collectVariable(assets, iframeWindow);\n  const components = flatObject(componentCollection);\n  const thirdLibs = getThirdLibs(componentCollection, page?.thirdLibs || []);\n\n  const App = IframeReact?.createElement(CRender.DesignRender, {\n    adapter: CRender?.ReactAdapter,\n    page: page,\n    pageModel: pageModel,\n    components,\n    $$context: {\n      thirdLibs,\n    },\n    onMount: (designRenderInstance) => {\n      ready(designRenderInstance);\n    },\n  });\n\n  IframeReactDOM.createRoot(iframeDoc.getElementById('app')!).render(App);\n};\n\nconst win = window as any;\nwin.React = React;\nwin.ReactDOM = ReactDOM;\nwin.ReactDOMClient = ReactDOMClient;\n\nconst assetPackagesList = [\n  {\n    package: pkg.name,\n    globalName: 'ChamnCustomComponent',\n    resources: [],\n  },\n];\n\nexport const Editor = () => {\n  const [ready, setReady] = useState(false);\n  const [page, setPage] = useState(EmptyPage);\n\n  useEffect(() => {\n    // 从本地获取 page schema\n    const localPage = localStorage.getItem('pageSchema');\n    if (localPage) {\n      setPage(JSON.parse(localPage));\n    }\n    setReady(true);\n  }, []);\n\n  const onReady = useCallback(async (ctx: EnginContext) => {\n    const designer = await ctx.pluginManager.get<DesignerPluginInstance>(\n      'Designer'\n    );\n    const reloadPage = async () => {\n      setTimeout(() => {\n        const designerExports = designer?.export;\n        designerExports?.reload();\n      }, 0);\n    };\n\n    // 获取 引擎 工作台对象\n    const workbench = ctx.engine.getWorkbench();\n    workbench?.openLeftPanel('ComponentLib');\n    // 自定义顶部 bar\n    workbench?.replaceTopBarView(\n      <div\n        style={{\n          width: '100%',\n          height: '100%',\n          display: 'flex',\n          alignItems: 'center',\n          justifyContent: 'flex-end',\n          paddingRight: '10px',\n        }}\n      >\n        <div className=\"logo\">Chameleon EG</div>\n        <div\n          style={{\n            height: '100%',\n            display: 'flex',\n            alignItems: 'center',\n            justifyContent: 'center',\n            marginRight: '10px',\n          }}\n        >\n          {ctx && <DesignerSizer ctx={ctx} />}\n        </div>\n        <a\n          target=\"_blank\"\n          href=\"https://github.com/hlerenow/chameleon\"\n          rel=\"noreferrer\"\n        >\n          <Button style={{ marginRight: '10px' }}>Github </Button>\n        </a>\n\n        <Button\n          style={{ marginRight: '10px' }}\n          onClick={async () => {\n            const res = await ctx.pluginManager.get('History');\n            res?.export.preStep();\n          }}\n        >\n          <RollbackOutlined />\n        </Button>\n        <Button\n          style={{ marginRight: '10px' }}\n          onClick={async () => {\n            const res = await ctx.pluginManager.get('History');\n            res?.export.nextStep();\n          }}\n        >\n          <RollbackOutlined\n            style={{\n              transform: 'rotateY(180deg)',\n            }}\n          />\n        </Button>\n\n        <DisplaySourceSchema pageModel={ctx.engine.pageModel} engineCtx={ctx}>\n          <Button style={{ marginRight: '10px' }}>Source Code</Button>\n        </DisplaySourceSchema>\n\n        <Button\n          style={{ marginRight: '10px' }}\n          onClick={() => {\n            reloadPage();\n          }}\n        >\n          Refresh Page\n        </Button>\n        <Button\n          style={{ marginRight: '10px' }}\n          onClick={() => {\n            const src = '/#/preview';\n\n            Modal.info({\n              closable: true,\n              icon: null,\n              width: 'calc(100vw - 100px)',\n              centered: true,\n              title: (\n                <div>\n                  Preview\n                  <Button\n                    size=\"small\"\n                    style={{\n                      float: 'right',\n                      marginRight: '30px',\n                    }}\n                    onClick={() => {\n                      window.open(src);\n                    }}\n                  >\n                    Open in new window\n                  </Button>\n                </div>\n              ),\n              content: (\n                <div\n                  style={{\n                    width: '100%',\n                    height: 'calc(100vh - 200px)',\n                  }}\n                >\n                  <iframe\n                    style={{\n                      border: '1px solid #e7e7e7',\n                      width: '100%',\n                      height: '100%',\n                      borderRadius: '4px',\n                      overflow: 'hidden',\n                    }}\n                    src={src}\n                  />\n                </div>\n              ),\n              footer: null,\n            });\n          }}\n        >\n          Preview\n        </Button>\n        <Button\n          type=\"primary\"\n          onClick={() => {\n            const newPage = ctx.engine.pageModel.export();\n            localStorage.setItem('pageSchema', JSON.stringify(newPage));\n            message.success('Save successfully');\n          }}\n        >\n          Save\n        </Button>\n      </div>\n    );\n  }, []);\n\n  if (!ready) {\n    return <>loading...</>;\n  }\n  return (\n    <Engine\n      plugins={DEFAULT_PLUGIN_LIST}\n      schema={page}\n      assetPackagesList={assetPackagesList}\n      // 传入组件物料, 这里使用内置的基础物料以及 测试物料信\n      material={[...InnerComponentMeta, ...customMaterial.meta]}\n      // 传入组件物料对应的 js 运行库，只能使用 umd 模式的 js\n      onReady={onReady}\n      beforePluginRun={({ pluginManager }) => {\n        pluginManager.customPlugin('Designer', (pluginInstance) => {\n          pluginInstance.ctx.config.customRender = customRender;\n          return pluginInstance;\n        });\n      }}\n      renderJSUrl={renderAsURL}\n    />\n  );\n};\n"
  },
  {
    "path": "packages/material/src/_dev_/index.scss",
    "content": "html,\nbody,\n#root {\n  margin: 0;\n  padding: 0;\n  box-sizing: border-box;\n  width: 100%;\n  height: 100%;\n}\n.logo {\n  height: 100%;\n  font-size: 20px;\n  display: flex;\n  align-items: center;\n  margin-left: 20px;\n  font-weight: bolder;\n  margin-right: auto;\n}\n"
  },
  {
    "path": "packages/material/src/_dev_/index.tsx",
    "content": "import * as ReactDOMClient from 'react-dom/client';\nimport { RouterProvider } from 'react-router-dom';\nimport { router } from './router';\nimport './index.scss';\n\nReactDOMClient.createRoot(\n  document.getElementById('root') as HTMLElement\n).render(<RouterProvider router={router} />);\n"
  },
  {
    "path": "packages/material/src/_dev_/preview.tsx",
    "content": "import React from 'react';\nimport { useEffect, useState } from 'react';\nimport {\n  ReactAdapter,\n  Render,\n  useRender,\n  AssetLoader,\n  collectVariable,\n  flatObject,\n} from '@chamn/render';\nimport { AssetPackage, CPageDataType } from '@chamn/model';\nimport * as componentLibs from '../components/index';\nconst loadAssets = async (assets: AssetPackage[]) => {\n  // 注入组件物料资源\n  const assetLoader = new AssetLoader(assets);\n  try {\n    await assetLoader.load();\n    // 从子窗口获取物料对象\n    const componentCollection = collectVariable(assets, window);\n    const components = flatObject(componentCollection);\n    return components;\n  } catch {\n    return null;\n  }\n};\n\nexport const Preview = () => {\n  const [page, setPage] = useState<CPageDataType>();\n  const renderHandle = useRender();\n  const [loading, setLoading] = useState(true);\n  const [pageComponents, setPageComponents] = useState(componentLibs);\n  const loadPageAssets = async (assets: AssetPackage[]) => {\n    console.log('🚀 ~ loadPageAssets ~ assets:', assets);\n    const components = await loadAssets(assets);\n    console.log('🚀 ~ loadPageAssets ~ components:', components);\n    if (components) {\n      setPageComponents(components as any);\n      setLoading(false);\n    }\n  };\n  useEffect(() => {\n    const localPage = localStorage.getItem('pageSchema');\n    console.log('🚀 ~ useEffect ~ localPage:', localPage);\n    if (localPage) {\n      const page: CPageDataType = JSON.parse(localPage);\n      setPage(page);\n      setLoading(false);\n      loadPageAssets(page.assets || []);\n    }\n  }, []);\n\n  if (loading) {\n    return (\n      <>Not find page info on local, please ensure you save it on editor</>\n    );\n  }\n  return (\n    <div className=\"App\" style={{ overflow: 'auto', height: '100%' }}>\n      <Render\n        page={page}\n        components={{\n          ...pageComponents,\n        }}\n        render={renderHandle}\n        adapter={ReactAdapter}\n      />\n    </div>\n  );\n};\n"
  },
  {
    "path": "packages/material/src/_dev_/react.ts",
    "content": ""
  },
  {
    "path": "packages/material/src/_dev_/render.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n  <title>Chameleon Material develop render</title>\n  <style>\n    html,\n    body {\n      width: 100%;\n      height: 100%;\n    }\n\n    * {\n      margin: 0;\n      padding: 0;\n    }\n\n    #root {\n      width: 100%;\n      height: 100%;\n    }\n\n    * {\n      user-select: none;\n      -webkit-user-drag: none;\n    }\n  </style>\n  <script>\n    window.React = window.parent.React;\n    window.ReactDOM = window.parent.ReactDOM;\n    window.ReactDOMClient = window.parent.ReactDOMClient || window.parent.ReactDOM;\n\n  </script>\n</head>\n\n<body>\n  <div id=\"app\"></div>\n  <script type=\"module\" src=\"./render.tsx\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "packages/material/src/_dev_/render.tsx",
    "content": "import renderAsURL from '../../node_modules/@chamn/render/dist/index.umd.js?url';\nimport gridstackURL from '../../node_modules/gridstack/dist/gridstack-all.js?url';\nimport loadjs from 'loadjs';\nimport * as componentLibs from '../components/index';\n\n(window as any).ChamnCustomComponent = componentLibs;\n\nloadjs([renderAsURL, gridstackURL], () => {\n  console.log('load render.umd.js success');\n});\n"
  },
  {
    "path": "packages/material/src/_dev_/router.tsx",
    "content": "import React from 'react';\nimport { createHashRouter } from 'react-router-dom';\nimport { Editor } from './editor';\nimport { Preview } from './preview';\n\nexport const router: any = createHashRouter([\n  {\n    path: '/',\n    element: <Editor />,\n  },\n  {\n    path: '/preview',\n    element: <Preview />,\n  },\n]);\n"
  },
  {
    "path": "packages/material/src/components/ReactGridLayout/GridItem.tsx",
    "content": "import React, { CSSProperties, forwardRef, useImperativeHandle } from 'react';\nimport { useContext, useEffect, useMemo, useRef } from 'react';\n\nimport { CNode } from '@chamn/model';\nimport { GridContext } from './context';\nimport styles from './style.module.scss';\nimport clsx from 'clsx';\nimport { DEFAULT_RESPONSIVE_SIZE } from './config';\n\ntype PosAndSizeInfoType = {\n  w: number;\n  h: number;\n  x: number;\n  y: number;\n};\n\ntype ResponsiveItemInfo = {\n  label: string;\n  info: PosAndSizeInfoType;\n};\n\nexport type GridItemPropsType = {\n  children: any;\n  responsive: ResponsiveItemInfo[];\n  node?: CNode;\n  dev?: boolean;\n  style?: CSSProperties;\n  $SET_DOM?: (dom: HTMLElement) => void;\n  onGetRef?: (\n    ref: React.MutableRefObject<GridItemRefType | undefined>\n  ) => object;\n};\n\nexport type GridItemRefType = {\n  getCurrentPosAndSizeInfo: () => ResponsiveItemInfo;\n};\n\nexport const GridItem = forwardRef<GridItemRefType, GridItemPropsType>(\n  ({ dev, onGetRef, $SET_DOM, ...props }, ref) => {\n    const ctx = useContext(GridContext);\n    const refDom = useRef<HTMLDivElement>(null);\n    const id = useMemo(() => {\n      return props?.node?.id || Math.random().toString(32).slice(3, 9);\n    }, [props?.node?.id]);\n\n    /** 配合设计器使用 */\n    if (refDom.current) {\n      $SET_DOM?.(refDom.current);\n    }\n\n    useEffect(() => {\n      if (ctx.ready) {\n        ctx.gridStack?.makeWidget(refDom.current!);\n      }\n      // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [ctx.ready]);\n\n    const currentSizeAndPosInfo = useMemo<ResponsiveItemInfo>(() => {\n      const currentResponsiveLabel = ctx.currentBreakpoint.label;\n      const targetItem = props.responsive.find(\n        (el) => el.label === currentResponsiveLabel\n      );\n      const targetItemDefault = props.responsive.find(\n        (el) => el.label === DEFAULT_RESPONSIVE_SIZE\n      );\n      if (!ctx.ready) {\n        return {\n          info: {},\n        } as any;\n      }\n      return {\n        ...(targetItem || targetItemDefault || ({ info: {} } as any)),\n        label: ctx.currentBreakpoint.label,\n      };\n    }, [ctx.currentBreakpoint.label, ctx.ready, props.responsive]);\n\n    const currentSizeAndPosInfoRef = useRef<any>(currentSizeAndPosInfo);\n    currentSizeAndPosInfoRef.current = currentSizeAndPosInfo;\n\n    useEffect(() => {\n      if (!ctx.ready) {\n        return;\n      }\n      ctx.gridStack?.update(id, {\n        ...currentSizeAndPosInfo?.info,\n      });\n    }, [ctx, id]);\n    const specialRef = useRef<GridItemRefType>();\n\n    specialRef.current = {\n      getCurrentPosAndSizeInfo: () => {\n        return {\n          ...(currentSizeAndPosInfoRef.current || {}),\n        };\n      },\n    };\n    // 配合  ReactGridItemMeta 文件使用\n    onGetRef?.(specialRef);\n\n    useImperativeHandle(\n      ref,\n      () => {\n        return {\n          getCurrentPosAndSizeInfo: () => {\n            return {\n              ...currentSizeAndPosInfo,\n            };\n          },\n        };\n      },\n      [currentSizeAndPosInfo]\n    );\n\n    if (!ctx.ready) {\n      return <></>;\n    }\n    return (\n      <div\n        className=\"grid-stack-item\"\n        id={id}\n        ref={refDom}\n        data-grid-id={id}\n        gs-w={currentSizeAndPosInfo.info.w}\n        gs-h={currentSizeAndPosInfo.info.h}\n        gs-x={currentSizeAndPosInfo.info.x}\n        gs-y={currentSizeAndPosInfo.info.y}\n        style={{\n          overflow: 'hidden',\n        }}\n      >\n        {dev && (\n          <div className={clsx(['grid-drag-handler', styles.dragIcon])}>\n            <svg\n              style={{\n                transform: 'rotate(45deg)',\n              }}\n              width={16}\n              height={16}\n              xmlns=\"http://www.w3.org/2000/svg\"\n              fill=\"none\"\n              viewBox=\"0 0 24 24\"\n              strokeWidth=\"1.5\"\n              stroke=\"currentColor\"\n            >\n              <path\n                strokeLinecap=\"round\"\n                strokeLinejoin=\"round\"\n                d=\"M3.75 3.75v4.5m0-4.5h4.5m-4.5 0L9 9M3.75 20.25v-4.5m0 4.5h4.5m-4.5 0L9 15M20.25 3.75h-4.5m4.5 0v4.5m0-4.5L15 9m5.25 11.25h-4.5m4.5 0v-4.5m0 4.5L15 15\"\n              />\n            </svg>\n          </div>\n        )}\n        <div\n          {...props}\n          style={{\n            ...props.style,\n            width: '100%',\n            height: '100%',\n            margin: 0,\n            padding: 0,\n            boxSizing: 'border-box',\n          }}\n        >\n          {props.children}\n        </div>\n      </div>\n    );\n  }\n);\n"
  },
  {
    "path": "packages/material/src/components/ReactGridLayout/config.ts",
    "content": "export const breakpoints = [\n  { w: 567, label: 'xs' },\n  { w: 768, label: 'sm' },\n  { w: 992, label: 'md' },\n  { w: 1200, label: 'lg' },\n  { w: 9999, label: 'xl' },\n];\n\nexport const DEFAULT_RESPONSIVE_SIZE = 'lg';\nexport const defaultItemResponsive = [\n  {\n    label: DEFAULT_RESPONSIVE_SIZE,\n    info: {\n      w: 24,\n      h: 3,\n    },\n  },\n];\n"
  },
  {
    "path": "packages/material/src/components/ReactGridLayout/context.ts",
    "content": "import { GridStack } from 'gridstack';\nimport { createContext } from 'react';\nimport { GridItemInstance, ResponsivePoint } from './type';\nimport { breakpoints } from './config';\n\nexport function getDefaultContextValue() {\n  return {\n    ready: false,\n    gridStack: null,\n    gridItemsMap: {},\n    breakpoints,\n    currentBreakpoint: {},\n  } as any;\n}\n\nexport type GridContextType = {\n  ready: boolean;\n  gridStack: GridStack | null;\n  gridItemsMap: Record<string, GridItemInstance>;\n  addGridItem: (item: GridItemInstance) => void;\n  onMount: ((gridStack: GridStack) => void)[];\n  breakpoints: ResponsivePoint[];\n  currentBreakpoint: ResponsivePoint;\n};\n\nexport const GridContext = createContext<GridContextType>(\n  getDefaultContextValue()\n);\n"
  },
  {
    "path": "packages/material/src/components/ReactGridLayout/edit/layoutWrap.tsx",
    "content": "import { useRef } from 'react';\nimport { GridLayout, ReactGridLayoutPropsType } from '..';\nimport { GridStack, GridStackElementHandler } from 'gridstack';\nimport { breakpoints } from '../config';\nimport { GridItemPropsType } from '../GridItem';\n\ntype ChangeLayoutEvent = {\n  detail: {\n    el: HTMLElement;\n    grid: GridStack;\n    x: number;\n    y: number;\n    w: number;\n    h: number;\n  }[];\n};\n\nexport const LayoutWrap = (\n  props: ReactGridLayoutPropsType & {\n    targetComp: typeof GridLayout;\n  }\n) => {\n  const { targetComp: Comp, ...restProps } = props;\n  const ref = useRef<GridStack>();\n\n  const initEditLogic = (grid: GridStack) => {\n    const updateGridItemLayout: GridStackElementHandler = (changeLayout) => {\n      const { detail }: ChangeLayoutEvent = changeLayout as any;\n      const sunWinW = props.subWin!.innerWidth;\n      const pointInfo = breakpoints.find((el) => el.w >= sunWinW);\n      detail.forEach((item) => {\n        const nodeId = item.el.getAttribute('data-grid-id');\n        const node = props.ctx.engine.pageModel.getNode(String(nodeId));\n        if (node) {\n          const plainProps: GridItemPropsType = node.getPlainProps();\n          const newResponsive = plainProps.responsive;\n          let targetItem = newResponsive.find(\n            (el) => el.label === pointInfo!.label\n          );\n          if (!targetItem) {\n            targetItem = {\n              label: pointInfo!.label,\n              info: {} as any,\n            };\n            newResponsive.push(targetItem);\n          }\n          targetItem.info = {\n            x: item.x,\n            y: item.y,\n            w: item.w,\n            h: item.h,\n          };\n\n          node.updateValue({\n            props: plainProps,\n          });\n        }\n      });\n    };\n    grid.on('change', updateGridItemLayout);\n  };\n\n  return (\n    <Comp\n      {...restProps}\n      animate={true}\n      staticGrid={false}\n      subWin={props.subWin}\n      onMount={(grid) => {\n        ref.current = grid;\n        initEditLogic(grid);\n        restProps.onMount?.(grid);\n      }}\n    />\n  );\n};\n"
  },
  {
    "path": "packages/material/src/components/ReactGridLayout/index.tsx",
    "content": "import React, { useCallback, useRef } from 'react';\nimport { ColumnOptions, GridStack } from 'gridstack';\nimport 'gridstack/dist/gridstack.min.css';\nimport 'gridstack/dist/gridstack-extra.min.css';\nimport './layout.scss';\nimport { useEffect, useMemo, useState } from 'react';\nimport { EnginContext } from '@chamn/engine';\nimport {\n  getDefaultContextValue,\n  GridContext,\n  GridContextType,\n} from './context';\nimport { breakpoints } from './config';\nimport { debounce } from 'lodash-es';\nimport { ResponsivePoint } from './type';\n\nexport type ReactGridLayoutPropsType = {\n  children: any;\n  onMount?: (grid: GridStack) => void;\n  ctx: EnginContext;\n  subWin?: Window;\n  staticGrid?: boolean;\n  animate?: boolean;\n  layout?: ColumnOptions;\n  $SET_DOM?: (dom: HTMLElement) => void;\n  breakpoints?: ResponsivePoint[];\n  onBreakpointChange?: (breakpoint: { w: number; label: string }) => void;\n};\n\nexport const GridLayout = ({\n  subWin,\n  staticGrid,\n  animate,\n  onMount,\n  $SET_DOM,\n  ...props\n}: ReactGridLayoutPropsType) => {\n  const [ctx, setCtx] = useState<GridContextType>(getDefaultContextValue());\n  const id = useMemo(() => {\n    return Math.random().toString(32).slice(3, 9);\n  }, []);\n  const gridRef = useRef<GridStack>();\n  const refDom = useRef<HTMLDivElement>(null);\n\n  const init = useCallback(async () => {\n    const tempGridStack: typeof GridStack =\n      (subWin as any)?.GridStack || GridStack;\n\n    const grid = tempGridStack.init(\n      {\n        cellHeight: '30px',\n        margin: 3,\n        column: 24,\n        float: true,\n        minRow: 3,\n        animate: true,\n        staticGrid: staticGrid ?? true,\n        columnOpts: {\n          layout: 'scale',\n        },\n        draggable: {\n          handle: '.grid-drag-handler',\n        },\n      },\n      id\n    );\n    if (!grid) {\n      return;\n    }\n    gridRef.current = grid;\n    onMount?.(grid);\n    setCtx((oldVal) => {\n      return {\n        ...oldVal,\n        gridStack: grid,\n        ready: true,\n      };\n    });\n\n    ctx.onMount?.forEach((el) => {\n      el(grid);\n    });\n  }, [ctx.onMount, id, onMount, staticGrid, subWin]);\n\n  /** 配合设计器使用 */\n  if (refDom.current) {\n    $SET_DOM?.(refDom.current);\n  }\n\n  useEffect(() => {\n    if (props.breakpoints) {\n      gridRef.current?.destroy(false);\n      init();\n    }\n  }, [init, props.breakpoints, props.layout]);\n\n  const responseJudge = debounce(() => {\n    const sunWinW = (subWin ?? window).innerWidth;\n    const pointInfo = breakpoints.find((el) => el.w >= sunWinW);\n\n    if (!pointInfo) {\n      return;\n    }\n\n    setCtx((oldVal) => {\n      props.onBreakpointChange?.(pointInfo);\n      if (pointInfo.w === ctx.currentBreakpoint.w) {\n        return oldVal;\n      }\n      return {\n        ...oldVal,\n        currentBreakpoint: pointInfo,\n      };\n    });\n  }, 50);\n\n  useEffect(() => {\n    window.addEventListener('resize', responseJudge);\n    subWin?.addEventListener('resize', responseJudge);\n    responseJudge();\n    return () => {\n      window.removeEventListener('resize', responseJudge);\n      subWin?.removeEventListener('resize', responseJudge);\n    };\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n\n  const finalCtx = useMemo(() => {\n    return {\n      ...ctx,\n      breakpoints: props.breakpoints ?? breakpoints,\n    };\n  }, [ctx, props.breakpoints]);\n\n  return (\n    <GridContext.Provider value={finalCtx}>\n      <div id={id} className=\"grid-stack\" ref={refDom}>\n        {props.children}\n      </div>\n    </GridContext.Provider>\n  );\n};\n"
  },
  {
    "path": "packages/material/src/components/ReactGridLayout/item.meta.tsx",
    "content": "/* eslint-disable react-hooks/rules-of-hooks */\nimport { CMaterialType } from '@chamn/model';\nimport { snippetsGridItem } from './snippets';\nimport { useEffect, useState } from 'react';\nimport { DesignerPluginInstance } from '@chamn/engine/dist/plugins/Designer/type';\nimport { breakpoints } from './config';\nimport { DesignerCtx } from '@chamn/engine/dist/plugins/Designer/components/Canvas';\nimport { debounce } from 'lodash-es';\nimport { GridItemPropsType } from './GridItem';\n\nconst GRID_ITEM_INSTANCE_MAP: any = {};\n\nexport const ReactGridItemMeta: CMaterialType = {\n  componentName: 'GridItem',\n  title: '高级布局容器',\n  category: '高级布局',\n  groupName: '内置组件',\n  props: [\n    {\n      name: 'responsive',\n      title: 'Responsive',\n      setters: [\n        {\n          componentName: 'ArraySetter',\n          initialValue: breakpoints,\n          props: {\n            collapse: {\n              open: true,\n            },\n            item: {\n              initialValue: { w: 0, label: 'customSize' },\n              setters: [\n                {\n                  componentName: 'ShapeSetter',\n                  initialValue: {\n                    with: 0,\n                    c: 0,\n                  },\n                  props: {\n                    collapse: {\n                      open: true,\n                    },\n                    elements: [\n                      {\n                        name: 'label',\n                        title: 'label',\n                        setters: [\n                          {\n                            componentName: 'StringSetter',\n                            props: {\n                              disabled: true,\n                            },\n                          },\n                        ],\n                        valueType: 'number',\n                      },\n                      {\n                        name: 'info',\n                        title: 'info',\n                        valueType: 'object',\n                        setters: [\n                          {\n                            componentName: 'ShapeSetter',\n                            initialValue: {\n                              with: 0,\n                              label: '',\n                            },\n                            props: {\n                              collapse: false,\n                              elements: [\n                                {\n                                  name: 'w',\n                                  title: 'width',\n                                  setters: ['NumberSetter', 'ExpressionSetter'],\n                                  valueType: 'number',\n                                },\n                                {\n                                  name: 'h',\n                                  title: 'height',\n                                  setters: ['NumberSetter', 'ExpressionSetter'],\n                                  valueType: 'number',\n                                },\n                                {\n                                  name: 'x',\n                                  title: 'offsetX',\n                                  setters: ['NumberSetter', 'ExpressionSetter'],\n                                  valueType: 'number',\n                                },\n                                {\n                                  name: 'y',\n                                  title: 'offsetY',\n                                  setters: ['NumberSetter', 'ExpressionSetter'],\n                                  valueType: 'number',\n                                },\n                              ],\n                            },\n                          },\n                        ],\n                      },\n                    ],\n                  },\n                },\n              ],\n            },\n          },\n        },\n      ],\n      valueType: 'number',\n    },\n  ],\n  isContainer: true,\n  npm: {\n    name: 'GridItem',\n    package: __PACKAGE_NAME__ || '',\n    version: __PACKAGE_VERSION__,\n    destructuring: true,\n    exportName: 'GridItem',\n  },\n  disableEditorDragDom: true,\n  advanceCustom: {\n    rightPanel: {\n      advanceOptions: {\n        render: false,\n        loop: false,\n      },\n    },\n    autoGetDom: false,\n    toolbarViewRender: ({ node, context, toolBarItemList }) => {\n      // 引擎自带的 显示隐藏，与编辑模式冲突，这里隐藏，不允许隐藏\n      toolBarItemList.splice(1, 1);\n      const [posInfo, setPostInfo] = useState({\n        label: '',\n        w: 0,\n        h: 0,\n        x: 0,\n        y: 0,\n      });\n\n      const getNodePosAndSizeInfo = debounce(async () => {\n        const compInsRef = GRID_ITEM_INSTANCE_MAP[node.id];\n        if (!compInsRef) {\n          return;\n        }\n        const posInfo = compInsRef.current?.getCurrentPosAndSizeInfo();\n        setPostInfo({\n          label: posInfo.label,\n          x: posInfo.info?.x,\n          y: posInfo.info?.y,\n          w: posInfo.info?.w,\n          h: posInfo.info?.h,\n        });\n      }, 100);\n\n      const registerResize = async () => {\n        node.onChange(getNodePosAndSizeInfo);\n        const ctx = context as DesignerCtx;\n        const designer = await ctx.pluginManager.get<DesignerPluginInstance>(\n          'Designer'\n        );\n\n        const subWin = designer?.export.getDesignerWindow();\n        subWin?.addEventListener('resize', getNodePosAndSizeInfo);\n        window?.addEventListener('resize', getNodePosAndSizeInfo);\n      };\n      const removeListener = async () => {\n        const ctx = context as DesignerCtx;\n        const designer = await ctx.pluginManager.get<DesignerPluginInstance>(\n          'Designer'\n        );\n\n        const subWin = designer?.export.getDesignerWindow();\n        subWin?.removeEventListener('resize', getNodePosAndSizeInfo);\n        window.removeEventListener('resize', getNodePosAndSizeInfo);\n      };\n      useEffect(() => {\n        getNodePosAndSizeInfo();\n        registerResize();\n        return () => {\n          removeListener();\n        };\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n      }, []);\n      return (\n        <div\n          style={{\n            display: 'flex',\n            float: 'right',\n            zIndex: 999,\n            pointerEvents: 'all',\n          }}\n        >\n          <div\n            style={{\n              background: 'white',\n              marginRight: '5px',\n              fontSize: '12px',\n              padding: '0 10px',\n              display: 'flex',\n              alignItems: 'center',\n              justifyContent: 'center',\n            }}\n          >\n            <b\n              style={{\n                paddingRight: '2px',\n                color: 'red',\n              }}\n            >\n              {posInfo.label}\n            </b>\n            | w: {posInfo.w} | h: {posInfo.h} | x: {posInfo.x} | y: {posInfo.y}\n          </div>\n          {toolBarItemList}\n        </div>\n      );\n    },\n    onDragStart: async () => {\n      return false;\n    },\n    wrapComponent: (Comp, options) => {\n      return (props: any) => {\n        return (\n          <Comp\n            {...props}\n            {...options}\n            dev={true}\n            onGetRef={(ref: any) => {\n              GRID_ITEM_INSTANCE_MAP[options.node.id] = ref;\n            }}\n          />\n        );\n      };\n    },\n    canDropNode: async (_node, params) => {\n      const { dropNode } = params;\n      if (!dropNode) {\n        return false;\n      }\n      if (dropNode.value.componentName === 'GridLayout') {\n        return true;\n      }\n\n      return false;\n    },\n    onCopy: async (node) => {\n      const newProps: GridItemPropsType = node.getPlainProps();\n      const newResponsive = newProps.responsive.map((el) => {\n        return {\n          ...el,\n          x: '',\n          y: '',\n        };\n      });\n      newProps.responsive = newResponsive;\n      node.updateValue({\n        props: newProps,\n      });\n      return true;\n    },\n    onNewAdd: async (_node, params) => {\n      const { dropNode } = params;\n      if (!dropNode) {\n        return false;\n      }\n      if (dropNode.value.componentName === 'GridLayout') {\n        return true;\n      }\n\n      return false;\n    },\n  },\n\n  snippets: snippetsGridItem,\n};\n\nexport default [ReactGridItemMeta];\n"
  },
  {
    "path": "packages/material/src/components/ReactGridLayout/layout.scss",
    "content": "@use 'sass:math';\n\n$columns-list: 24;\n\n// Function to round to 3 decimal places\n@function fixed($float) {\n  @return math.div(math.round($float * 1000), 1000);\n}\n\n// Loop through each column value\n@each $columns in $columns-list {\n  .gs-#{$columns} > .grid-stack-item {\n    width: fixed(math.div(100%, $columns));\n\n    @for $i from 1 through $columns - 1 {\n      &[gs-x='#{$i}'] {\n        left: fixed(math.div(100%, $columns) * $i);\n      }\n      &[gs-w='#{$i+1}'] {\n        width: fixed(math.div(100%, $columns) * ($i + 1));\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/material/src/components/ReactGridLayout/meta.tsx",
    "content": "import { CMaterialType } from '@chamn/model';\nimport { snippets } from './snippets';\nimport { LayoutWrap } from './edit/layoutWrap';\nimport { useEffect, useState, useCallback, useRef } from 'react';\nimport { EnginContext } from '@chamn/engine';\nimport { DesignerPluginInstance } from '@chamn/engine/dist/plugins/Designer/type';\nimport { GridStack } from 'gridstack';\nimport { breakpoints } from './config';\n\nexport const ReactGridLayoutMeta: CMaterialType = {\n  componentName: 'GridLayout',\n  title: '高级布局画布',\n  props: [\n    {\n      name: 'breakpoints',\n      title: 'Breakpoints',\n      setters: [\n        {\n          componentName: 'ArraySetter',\n          initialValue: breakpoints,\n          props: {\n            collapse: {\n              open: true,\n            },\n            item: {\n              initialValue: { w: 0, label: '' },\n              setters: [\n                {\n                  componentName: 'ShapeSetter',\n                  initialValue: {\n                    with: 0,\n                    c: 0,\n                  },\n                  props: {\n                    collapse: {\n                      open: true,\n                    },\n                    elements: [\n                      {\n                        name: 'label',\n                        title: 'label',\n                        setters: ['StringSetter'],\n                        valueType: 'string',\n                      },\n                      {\n                        name: 'w',\n                        title: 'width',\n                        setters: [\n                          {\n                            componentName: 'NumberSetter',\n                            props: {\n                              suffix: 'px',\n                            },\n                          },\n                        ],\n                        valueType: 'number',\n                      },\n                    ],\n                  },\n                },\n              ],\n            },\n          },\n        },\n      ],\n      valueType: 'array',\n    },\n  ],\n  isContainer: true,\n  category: '高级布局',\n  groupName: '内置组件',\n  npm: {\n    name: 'GridLayout',\n    package: __PACKAGE_NAME__ || '',\n    version: __PACKAGE_VERSION__,\n    destructuring: true,\n    exportName: 'GridLayout',\n  },\n  snippets: snippets,\n  advanceCustom: {\n    autoGetDom: false,\n    wrapComponent: (Comp, options) => {\n      return (props: any) => {\n        const [iframeWindow, setIframeWindow] = useState();\n        const designerRef = useRef<DesignerPluginInstance>();\n        useEffect(() => {\n          const ctx: EnginContext = options.ctx;\n          ctx.pluginManager\n            .onPluginReadyOk('Designer')\n            .then((ins: DesignerPluginInstance) => {\n              designerRef.current = ins;\n              const win = ins.export.getDesignerWindow();\n              setIframeWindow(win as any);\n            });\n        }, []);\n\n        const onGridMount = useCallback((grid: GridStack) => {\n          grid.on('dragstart', () => {\n            designerRef.current?.export.getLayoutRef().current?.banSelectNode();\n          });\n          grid.on('dragstop', (event) => {\n            setTimeout(() => {\n              designerRef.current?.export\n                .getLayoutRef()\n                .current?.recoverSelectNode();\n              const nodeId = (event.target as any)?.getAttribute(\n                'data-grid-id'\n              );\n              designerRef.current?.export\n                .getLayoutRef()\n                .current?.selectNode(nodeId);\n            }, 0);\n          });\n        }, []);\n\n        if (!iframeWindow) {\n          return <></>;\n        }\n\n        return (\n          <LayoutWrap\n            {...props}\n            {...options}\n            targetComp={Comp}\n            subWin={iframeWindow}\n            onMount={onGridMount}\n          />\n        );\n      };\n    },\n    rightPanel: {\n      visual: false,\n    },\n  },\n};\n\nexport default [ReactGridLayoutMeta];\n"
  },
  {
    "path": "packages/material/src/components/ReactGridLayout/snippets.ts",
    "content": "import { SnippetsType } from '@chamn/model';\nimport { breakpoints, defaultItemResponsive } from './config';\n\nexport const snippets: SnippetsType[] = [\n  {\n    title: '高级布局画布',\n    snapshotText: 'RGL',\n    description: '高级布局画布',\n    schema: {\n      props: {\n        breakpoints: breakpoints,\n      },\n    },\n  },\n];\n\nexport const snippetsGridItem: SnippetsType[] = [\n  {\n    title: '高级布局容器',\n    snapshotText: 'RGEL',\n    description: '高级布局容器',\n    schema: {\n      props: {\n        responsive: [...defaultItemResponsive],\n      },\n    },\n  },\n];\n"
  },
  {
    "path": "packages/material/src/components/ReactGridLayout/style.module.scss",
    "content": ":global(.grid-stack-item) {\n  .dragIcon {\n    display: none;\n    position: absolute;\n    margin: 0;\n    padding: 0;\n    top: 8px;\n    right: 6px;\n    z-index: 999;\n    box-sizing: border-box;\n    padding: 4px;\n    background-color: rgba(0, 0, 0, 0.1);\n    align-items: center;\n    justify-content: center;\n  }\n  &:hover {\n    .dragIcon {\n      display: flex;\n      cursor: move;\n    }\n  }\n}\n"
  },
  {
    "path": "packages/material/src/components/ReactGridLayout/type.ts",
    "content": "export type GridItemInstance = {\n  updatePosInfo: () => void;\n};\n\nexport type ResponsivePoint = {\n  w: number;\n  label: string;\n};\n"
  },
  {
    "path": "packages/material/src/components/index.ts",
    "content": "export * from './ReactGridLayout';\nexport * from './ReactGridLayout/GridItem';\n"
  },
  {
    "path": "packages/material/src/index.tsx",
    "content": "/* eslint-disable react-refresh/only-export-components */\nexport * from './components/index';\n"
  },
  {
    "path": "packages/material/src/meta.tsx",
    "content": "import { CSetter } from '@chamn/engine';\nimport { CMaterialType } from '@chamn/model';\n\nconst moduleFiles = import.meta.glob('./components/**/*/*meta.ts*', {\n  eager: true,\n});\n\nconst metaList: CMaterialType[] = [];\n\nObject.keys(moduleFiles).forEach((key: any) => {\n  const el: any = moduleFiles[key];\n  metaList.push(...el.default);\n});\n\nconst setterFiles = import.meta.glob('./setter/*/index.tsx', {\n  eager: true,\n});\n\nlet setterMap: Record<string, CSetter<any>> = {};\nObject.keys(setterFiles).forEach((key: any) => {\n  const el: any = setterFiles[key];\n  setterMap = {\n    ...setterMap,\n    ...el,\n  };\n});\n\nexport default {\n  meta: metaList,\n  setter: setterMap,\n  version: __PACKAGE_VERSION__,\n  package: __PACKAGE_NAME__,\n  globalName: __GLOBAL_LIB_NAME__,\n};\n"
  },
  {
    "path": "packages/material/src/vite-env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n\n/** package version */\ndeclare const __PACKAGE_VERSION__: string;\ndeclare const __PACKAGE_NAME__: string;\ndeclare const __GLOBAL_LIB_NAME__: string;\n\ndeclare module '*.scss' {\n  const content: { [key: string]: any };\n  export = content;\n}\n"
  },
  {
    "path": "packages/material/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ESNext\",\n    \"useDefineForClassFields\": true,\n    \"lib\": [\n      \"DOM\",\n      \"DOM.Iterable\",\n      \"ESNext\"\n    ],\n    \"allowJs\": false,\n    \"skipLibCheck\": true,\n    \"esModuleInterop\": false,\n    \"allowSyntheticDefaultImports\": true,\n    \"strict\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"noEmit\": true,\n    \"jsx\": \"react-jsx\",\n  },\n  \"include\": [\n    \"src\"\n  ]\n}"
  },
  {
    "path": "packages/model/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "packages/model/CHANGELOG.md",
    "content": "# Change Log\n\nAll notable changes to this project will be documented in this file.\nSee [Conventional Commits](https://conventionalcommits.org) for commit guidelines.\n\n## [0.10.4](https://github.com/ByteCrazy/chameleon/compare/v0.10.3...v0.10.4) (2026-01-17)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.10.3](https://github.com/ByteCrazy/chameleon/compare/v0.10.2...v0.10.3) (2026-01-17)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.10.2](https://github.com/ByteCrazy/chameleon/compare/v0.10.1...v0.10.2) (2026-01-17)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.10.1](https://github.com/ByteCrazy/chameleon/compare/v0.10.0...v0.10.1) (2026-01-11)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.10.0](https://github.com/hlerenow/chameleon/compare/v0.9.3...v0.10.0) (2025-12-25)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* 优化构建配置，修复 ES 模块外部化问题 ([866f0ae](https://github.com/hlerenow/chameleon/commit/866f0ae39472625137d73d143625d88d873f6c00))\n\n## [0.9.3](https://github.com/ByteCrazy/chameleon/compare/v0.9.2...v0.9.3) (2025-07-20)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.9.2](https://github.com/ByteCrazy/chameleon/compare/v0.9.1...v0.9.2) (2025-07-13)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.9.1](https://github.com/ByteCrazy/chameleon/compare/v0.9.0...v0.9.1) (2025-07-13)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.9.0](https://github.com/ByteCrazy/chameleon/compare/v0.8.6...v0.9.0) (2025-07-13)\n\n### ✨ Features | 新功能\n\n* **engine, model:** 🎸 support EmptyValueSetter ([da1e36f](https://github.com/ByteCrazy/chameleon/commit/da1e36f9915282b3f7cb40672cf2a000bd4162e5))\n\n## [0.8.6](https://github.com/ByteCrazy/chameleon/compare/v0.8.5...v0.8.6) (2025-06-21)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.8.5](https://github.com/ByteCrazy/chameleon/compare/v0.8.4...v0.8.5) (2025-04-13)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.8.4](https://github.com/ByteCrazy/chameleon/compare/v0.8.3...v0.8.4) (2025-04-13)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.8.3](https://github.com/ByteCrazy/chameleon/compare/v0.8.2...v0.8.3) (2025-04-12)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.8.2](https://github.com/ByteCrazy/chameleon/compare/v0.8.1...v0.8.2) (2025-04-11)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.8.1](https://github.com/ByteCrazy/chameleon/compare/v0.8.0...v0.8.1) (2025-04-10)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.8.0](https://github.com/ByteCrazy/chameleon/compare/v0.7.0...v0.8.0) (2025-04-10)\n\n### ✨ Features | 新功能\n\n* **engine, model, render:** 🎸 fixed cycle update when update value in mount ([9b30922](https://github.com/ByteCrazy/chameleon/commit/9b30922ffac4a5b4fb1fe123c4604cc0fff63911))\n\n## [0.7.0](https://github.com/ByteCrazy/chameleon/compare/v0.6.0...v0.7.0) (2025-04-06)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.6.0](https://github.com/ByteCrazy/chameleon/compare/v0.5.2...v0.6.0) (2025-03-30)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.5.2](https://github.com/ByteCrazy/chameleon/compare/v0.5.1...v0.5.2) (2025-03-30)\n\n### ✨ Features | 新功能\n\n* **model:** 🎸 support export sxtra ([272de8d](https://github.com/ByteCrazy/chameleon/commit/272de8d989c3085830bf37469885c7a92abaf0da))\n\n## [0.5.1](https://github.com/ByteCrazy/chameleon/compare/v0.5.0...v0.5.1) (2025-03-30)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.5.0](https://github.com/ByteCrazy/chameleon/compare/v0.4.0...v0.5.0) (2025-03-30)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.4.0](https://github.com/ByteCrazy/chameleon/compare/v0.3.21...v0.4.0) (2025-03-29)\n\n### ✨ Features | 新功能\n\n* **model:** 🎸 update model define, add extra T ([f098ad2](https://github.com/ByteCrazy/chameleon/commit/f098ad26dc7525749d280445202971ede12490f4))\n\n## [0.3.21](https://github.com/ByteCrazy/chameleon/compare/v0.3.20...v0.3.21) (2025-03-26)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.3.20](https://github.com/ByteCrazy/chameleon/compare/v0.3.19...v0.3.20) (2025-03-26)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.3.19](https://github.com/ByteCrazy/chameleon/compare/v0.3.18...v0.3.19) (2025-03-26)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.3.18](https://github.com/ByteCrazy/chameleon/compare/v0.3.17...v0.3.18) (2025-03-26)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.3.17](https://github.com/ByteCrazy/chameleon/compare/v0.3.16...v0.3.17) (2025-03-25)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, material, model, render:** 🐛 fixed node update value material is undefined ([969174d](https://github.com/ByteCrazy/chameleon/commit/969174da968aec4a1ee1fec7ca44a5e459be56bc))\n\n## [0.3.16](https://github.com/ByteCrazy/chameleon/compare/v0.3.15...v0.3.16) (2025-03-24)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.3.15](https://github.com/ByteCrazy/chameleon/compare/v0.3.14...v0.3.15) (2025-03-23)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.3.14](https://github.com/ByteCrazy/chameleon/compare/v0.3.13...v0.3.14) (2025-03-23)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.3.13](https://github.com/ByteCrazy/chameleon/compare/v0.3.12...v0.3.13) (2025-03-23)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.3.12](https://github.com/ByteCrazy/chameleon/compare/v0.3.11...v0.3.12) (2025-03-23)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.3.11](https://github.com/ByteCrazy/chameleon/compare/v0.3.10...v0.3.11) (2025-03-22)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.3.10](https://github.com/ByteCrazy/chameleon/compare/v0.3.9...v0.3.10) (2025-03-22)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.3.9](https://github.com/ByteCrazy/chameleon/compare/v0.3.8...v0.3.9) (2025-03-22)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.3.8](https://github.com/ByteCrazy/chameleon/compare/v0.3.7...v0.3.8) (2025-03-16)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.3.7](https://github.com/ByteCrazy/chameleon/compare/v0.3.6...v0.3.7) (2025-03-16)\n\n### ✨ Features | 新功能\n\n* **demo-page, engine, model, render:** 🎸 support inject eng inner env to runtime ([baa5c11](https://github.com/ByteCrazy/chameleon/commit/baa5c11d389019a7e4e4b8e000433a99038b4ae3))\n\n## [0.3.6](https://github.com/ByteCrazy/chameleon/compare/v0.3.5...v0.3.6) (2025-03-09)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.3.5](https://github.com/ByteCrazy/chameleon/compare/v0.3.4...v0.3.5) (2025-03-09)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, model:** 🐛 update select setter name ([054fd48](https://github.com/ByteCrazy/chameleon/commit/054fd48f49ec1d1bdb79d7bac44acedb601d6fdf))\n\n## [0.3.4](https://github.com/ByteCrazy/chameleon/compare/v0.3.3...v0.3.4) (2025-02-16)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.3.3](https://github.com/ByteCrazy/chameleon/compare/v0.3.2...v0.3.3) (2025-02-16)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.3.2](https://github.com/ByteCrazy/chameleon/compare/v0.3.1...v0.3.2) (2025-02-16)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.3.1](https://github.com/ByteCrazy/chameleon/compare/v0.3.0...v0.3.1) (2025-02-16)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.3.0](https://github.com/ByteCrazy/chameleon/compare/v0.2.4...v0.3.0) (2025-02-15)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, engine-website-app, material, model, render:** 🐛 fixed GRL hidden offsetY loop add size ([3df132b](https://github.com/ByteCrazy/chameleon/commit/3df132b6493026b42435d4868d77915b9f7316b2))\n\n### ✨ Features | 新功能\n\n* **demo-page, engine, model, render:** 🎸 action node use link struct ([599ece4](https://github.com/ByteCrazy/chameleon/commit/599ece4927523f7c0e330cac722c9c9e6976ea3c))\n* **demo-page, engine, model, render:** 🎸 add event panel ([e8c5648](https://github.com/ByteCrazy/chameleon/commit/e8c5648017b40cbae42c576267d1e3b9d9660918))\n* **demo-page, engine, model, render:** 🎸 support TActionLogicItem prop ([e1b9d1e](https://github.com/ByteCrazy/chameleon/commit/e1b9d1e150ae810750249322ddf906b62eee9969))\n* **demo-page, model, render:** 🎸 optimize afterResponse ([b4060ba](https://github.com/ByteCrazy/chameleon/commit/b4060ba0a0a01960ae5f8dffea159e2d5ea680b6))\n* **demo-page, model, render:** 🎸 support ASSIGN_VALUE node ([74c395b](https://github.com/ByteCrazy/chameleon/commit/74c395baec55911de734e47d4a270f9cf016ee21))\n* **demo-page, model:** 🎸 define action and event type ([f7ca5d3](https://github.com/ByteCrazy/chameleon/commit/f7ca5d3b8ad7610f840845f555bfcdb2adddae0a))\n* **docs-app, engine, engine-website-app, layout, material, model, render:** 🎸 do ActionFlowSetter 50% ([6399466](https://github.com/ByteCrazy/chameleon/commit/6399466c7253436591e071df25828a357bb7089e))\n* **engine, model:** 🎸 add  call node method node ([1a5e79c](https://github.com/ByteCrazy/chameleon/commit/1a5e79c49964c589da167b64985327a3cbb03da8))\n* **engine, model:** 🎸 add run code node ([7ac6182](https://github.com/ByteCrazy/chameleon/commit/7ac61829586a19d4fcdc1765fa878d6a16008858))\n* **engine, model:** 🎸 request API 70% ([cee1228](https://github.com/ByteCrazy/chameleon/commit/cee1228c2a3265320cb32579f6f7b532b6962908))\n* **model, render:** 🎸 add acion logic type defined ([a7c32a3](https://github.com/ByteCrazy/chameleon/commit/a7c32a33f80a00458fe16fbc4d0c34631c103b61))\n* **model, render:** 🎸 optimize render react adapter code struct ([d9fa3d0](https://github.com/ByteCrazy/chameleon/commit/d9fa3d0a72e4d7e04e4e86e62d2a4f6f31bd808e))\n\n## [0.2.4](https://github.com/ByteCrazy/chameleon/compare/v0.2.3...v0.2.4) (2024-12-08)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.2.3](https://github.com/ByteCrazy/chameleon/compare/v0.2.2...v0.2.3) (2024-12-08)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.2.2](https://github.com/ByteCrazy/chameleon/compare/v0.2.1...v0.2.2) (2024-12-07)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.2.1](https://github.com/ByteCrazy/chameleon/compare/v0.2.0...v0.2.1) (2024-12-07)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, engine-website-app, material, model, render:** 🎸 fixed RGL component and optimize project struct ([99f0679](https://github.com/ByteCrazy/chameleon/commit/99f0679d93a2fd696034ebed8f1abcd9d9e601d4))\n\n## [0.2.0](https://github.com/ByteCrazy/chameleon/compare/v0.1.1...v0.2.0) (2024-12-07)\n\n### ✨ Features | 新功能\n\n* **build-script, demo-page, engine, engine-website-app, layout, material, model, render:** 🎸 upgrade vite to 6.0 ([bcac2b1](https://github.com/ByteCrazy/chameleon/commit/bcac2b15b83b41a7042ca37368c1b45302ad81d5))\n\n## [0.1.1](https://github.com/ByteCrazy/chameleon/compare/v0.1.0...v0.1.1) (2024-11-11)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.1.0](https://github.com/ByteCrazy/chameleon/compare/v0.0.46...v0.1.0) (2024-09-07)\n\n### 👷 Continuous Integration | CI 配置\n\n* **engine, engine-website-app, layout, model:** 🎡 update github ci ([259e9db](https://github.com/ByteCrazy/chameleon/commit/259e9db229576cd09c5aae1f28a2c228927011c7))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **model:** 🐛 fixed export meta repeat ([614bedd](https://github.com/ByteCrazy/chameleon/commit/614beddb464f6e66d1d77bcccd1f9b996702c344))\n\n### ✨ Features | 新功能\n\n* **docs-app, engine, layout, material, model, render:** 🎸 optimize GL layout ([02274b4](https://github.com/ByteCrazy/chameleon/commit/02274b432903dc247c5613873f14715dd806decd))\n* **engine, model:** 🎸 add advanceOptions property ([d75f178](https://github.com/ByteCrazy/chameleon/commit/d75f178fa70d4c49b451dff9dddfb30d4c061196))\n* **engine, model:** 🎸 add hotKey plugin and fixed reloadPage event not trigge ([1cbf1e1](https://github.com/ByteCrazy/chameleon/commit/1cbf1e1a345d94c3b758b26cfbf1ecde69ce051c))\n\n## [0.0.46](https://github.com/ByteCrazy/chameleon/compare/v0.0.45...v0.0.46) (2024-06-30)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.0.45](https://github.com/ByteCrazy/chameleon/compare/v0.0.44...v0.0.45) (2024-06-30)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **model:** 🐛 fixed addMaterials method logic ([b7b1d14](https://github.com/ByteCrazy/chameleon/commit/b7b1d14737b2b25362809fc15a7b9ae25cba18fc))\n\n## [0.0.44](https://github.com/ByteCrazy/chameleon/compare/v0.0.43...v0.0.44) (2024-06-30)\n\n### ✨ Features | 新功能\n\n* **engine, model, render:** 🎸 optimize addMaterials and fixed inner mat schema ([aa77803](https://github.com/ByteCrazy/chameleon/commit/aa77803c75b203ee2a098fbee143f4a9581e15fa))\n\n## [0.0.43](https://github.com/ByteCrazy/chameleon/compare/v0.0.42...v0.0.43) (2024-06-29)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.0.42](https://github.com/ByteCrazy/chameleon/compare/v0.0.41...v0.0.42) (2024-06-01)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.0.41](https://github.com/ByteCrazy/chameleon/compare/v0.0.40...v0.0.41) (2024-05-26)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, layout, material, model, render:** 🎸 optimize build script ([0a60cc4](https://github.com/ByteCrazy/chameleon/commit/0a60cc4f5e5635d9e544b5141eaed5b8433901a5))\n\n## [0.0.40](https://github.com/ByteCrazy/chameleon/compare/v0.0.39...v0.0.40) (2024-04-28)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.0.39](https://github.com/ByteCrazy/chameleon/compare/v0.0.38...v0.0.39) (2024-04-27)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **build-script, demo-page, engine, layout, material, model, render:** 🐛 fix flatObject collectVariable refeer pos ([1c4163a](https://github.com/ByteCrazy/chameleon/commit/1c4163aeeb89176a1ff5b50f76930889df7751fe))\n\n## [0.0.38](https://github.com/ByteCrazy/chameleon/compare/v0.0.37...v0.0.38) (2024-04-27)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.0.37](https://github.com/ByteCrazy/chameleon/compare/v0.0.36...v0.0.37) (2024-04-27)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.0.36](https://github.com/ByteCrazy/chameleon/compare/v0.0.35...v0.0.36) (2024-04-27)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.0.35](https://github.com/ByteCrazy/chameleon/compare/v0.0.34...v0.0.35) (2024-04-27)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.0.34](https://github.com/ByteCrazy/chameleon/compare/v0.0.33...v0.0.34) (2024-04-27)\n\n### ✨ Features | 新功能\n\n* **docs-website, engine, model:** 🎸 add update assetsPackageList logic ([2bce2f4](https://github.com/ByteCrazy/chameleon/commit/2bce2f4758b203695ec119d6e201e3186d7fab84))\n\n## [0.0.33](https://github.com/ByteCrazy/chameleon/compare/v0.0.32...v0.0.33) (2024-04-27)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.0.32](https://github.com/ByteCrazy/chameleon/compare/v0.0.31...v0.0.32) (2024-01-30)\n\n### ✨ Features | 新功能\n\n* **docs-website, engine, model, render:** 🎸 add updateMaterials、updatePage methods ([39ed2b6](https://github.com/ByteCrazy/chameleon/commit/39ed2b692a8a7379a79b96cb3fd8cdb76a4f01f2))\n\n## [0.0.31](https://github.com/ByteCrazy/chameleon/compare/v0.0.30...v0.0.31) (2023-10-17)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.0.30](https://github.com/ByteCrazy/chameleon/compare/v0.0.29...v0.0.30) (2023-10-17)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.0.29](https://github.com/ByteCrazy/chameleon/compare/v0.0.28...v0.0.29) (2023-10-17)\n\n### ✨ Features | 新功能\n\n* **demo-page, engine, model:** 🎸 add ant color setter ([830de46](https://github.com/ByteCrazy/chameleon/commit/830de46babdf40a740248c37d8e6dc2043db1434))\n* **demo-page, engine, model:** 🎸 add ColorSetter ([e72b7f1](https://github.com/ByteCrazy/chameleon/commit/e72b7f15dd8bba498b776226d5debd07c8fd4234))\n* **demo-page, engine, model:** 🎸 add radio setter ([e6f9b1d](https://github.com/ByteCrazy/chameleon/commit/e6f9b1d9dba7cd3a9a82116bf5a1068c06a5a1d6))\n* **engine, model, render:** 🎸 cssEditor support cssText ([3dc74b4](https://github.com/ByteCrazy/chameleon/commit/3dc74b4895d414a718c00911a39d8491fafcfaee))\n* **engine, model:** 🎸 optimize style editor ([9c660ce](https://github.com/ByteCrazy/chameleon/commit/9c660ce694a32059450f19c824bcde9889049db8))\n* **engine, model:** 🎸 style varible、c s scss editor support styles text ([c988fcf](https://github.com/ByteCrazy/chameleon/commit/c988fcf17a2dbbc486e809a03c642726f02cf547))\n\n## [0.0.28](https://github.com/ByteCrazy/chameleon/compare/v0.0.27...v0.0.28) (2023-08-29)\n\n### ✨ Features | 新功能\n\n* **model, render:** 🎸 node support custom dropPlaceholder ([dabdbc0](https://github.com/ByteCrazy/chameleon/commit/dabdbc00b375d5b8491ff3498075caac45473a98))\n\n## [0.0.27](https://github.com/ByteCrazy/chameleon/compare/v0.0.26...v0.0.27) (2023-08-28)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.0.26](https://github.com/ByteCrazy/chameleon/compare/v0.0.25...v0.0.26) (2023-08-27)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.0.25](https://github.com/ByteCrazy/chameleon/compare/v0.0.24...v0.0.25) (2023-08-27)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.0.24](https://github.com/ByteCrazy/chameleon/compare/v0.0.23...v0.0.24) (2023-08-27)\n\n### ✨ Features | 新功能\n\n* **engine, model, render:** 🎸 fixed node maybe is null on rightPanel ([aee551e](https://github.com/ByteCrazy/chameleon/commit/aee551e77454f61900318003f9e3a3ffb1ef9427))\n\n## [0.0.23](https://github.com/ByteCrazy/chameleon/compare/v0.0.22...v0.0.23) (2023-08-26)\n\n### ✨ Features | 新功能\n\n* **engine, model:** 🎸 support custom rightPanel ([803d731](https://github.com/ByteCrazy/chameleon/commit/803d731819faa03430b2a17a154d3ff1e0daca28))\n* **engine, model:** 🎸 support inject custom setter ([a49ec4a](https://github.com/ByteCrazy/chameleon/commit/a49ec4ae4a98b42cf1cfb768990b64c981539881))\n\n## [0.0.22](https://github.com/ByteCrazy/chameleon/compare/v0.0.21...v0.0.22) (2023-08-24)\n\n### ✨ Features | 新功能\n\n* **demo-page, engine, layout, model:** 🎸 add disableEditorDragDom config ([1779f44](https://github.com/ByteCrazy/chameleon/commit/1779f44aff20370ea3336e72352032c6416f7dd3))\n\n## [0.0.21](https://github.com/ByteCrazy/chameleon/compare/v0.0.20...v0.0.21) (2023-08-19)\n\n### ✨ Features | 新功能\n\n* **engine, layout, model, render:** 🎸 optimize wrapComponent config ([d5916a7](https://github.com/ByteCrazy/chameleon/commit/d5916a7e6ee3cf79a32d4d23663b6873d86fe671))\n\n## [0.0.20](https://github.com/ByteCrazy/chameleon/compare/v0.0.19...v0.0.20) (2023-08-19)\n\n### ✨ Features | 新功能\n\n* **model, render:** 🎸 support wrapComponent meterial config ([73fa19c](https://github.com/ByteCrazy/chameleon/commit/73fa19cd698bd61ef8079ae71dfd284600796ad2))\n\n## [0.0.19](https://github.com/ByteCrazy/chameleon/compare/v0.0.18...v0.0.19) (2023-08-17)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.0.18](https://github.com/ByteCrazy/chameleon/compare/v0.0.17...v0.0.18) (2023-08-14)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.0.17](https://github.com/ByteCrazy/chameleon/compare/v0.0.16...v0.0.17) (2023-08-06)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.0.16](https://github.com/ByteCrazy/chameleon/compare/v0.0.15...v0.0.16) (2023-08-06)\n\n### ✨ Features | 新功能\n\n* **build-script-example, demo-page, engine, layout, material, model, render:** 🎸 support direct import render from package on dev mode ([6bb38e2](https://github.com/ByteCrazy/chameleon/commit/6bb38e29678a8a4d08a7fcf9548fa548399259b0))\n* **demo-page, engine, layout, model:** 🎸 support  advanceCustom drag and drop hooks ([fb21e15](https://github.com/ByteCrazy/chameleon/commit/fb21e1501f6829e4f13a35ea7be942ff72b0be91))\n* **demo-page, engine, layout, model:** 🎸 support toolbarViewRender and ghostViewRedner ([43ced37](https://github.com/ByteCrazy/chameleon/commit/43ced371cfb7dbb58a96fc15e1bf635092307fa8))\n* **demo-page, engine, model:** 🎸 support onDelete onCopy ([b6a76bb](https://github.com/ByteCrazy/chameleon/commit/b6a76bb244bbe2c050de17157bda97cb7ad21abc))\n* **engine, layout, model:** 🎸 add customDropViewRender prop ([cea7f1e](https://github.com/ByteCrazy/chameleon/commit/cea7f1e3c2b0a8a13aef7cdcb0191593414615aa))\n* **engine, layout, model:** 🎸 finish layout transform ([73ead35](https://github.com/ByteCrazy/chameleon/commit/73ead357b9aded2b5ee2b545da6f2b3677ce8393))\n* **layout, model:** 🎸 optimize advanceCustom type definition ([0f529d1](https://github.com/ByteCrazy/chameleon/commit/0f529d1a8d8adbfd0970ba13a75a116a26e4d2f8))\n* **layout, model:** 🎸 optimize advanceCustom type definition ([8cd11aa](https://github.com/ByteCrazy/chameleon/commit/8cd11aa9fb0dca0b605d9ed8504c2e827109dc8f))\n* **layout, model:** 🎸 update meterial types definition ([fcf4e82](https://github.com/ByteCrazy/chameleon/commit/fcf4e827942afe4df8ad4210a0a3134138a5b10e))\n\n## [0.0.15](https://github.com/ByteCrazy/chameleon/compare/v0.0.14...v0.0.15) (2023-07-01)\n\n### ✨ Features | 新功能\n\n* **demo-page, docs-website, engine, model, render:** 🎸 optimize assets load ([3ee6d58](https://github.com/ByteCrazy/chameleon/commit/3ee6d58a88e5af3fc631723240783d5c97a273b0))\n\n## [0.0.14](https://github.com/ByteCrazy/chameleon/compare/v0.0.13...v0.0.14) (2023-05-10)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.0.13](https://github.com/ByteCrazy/chameleon/compare/v0.0.12...v0.0.13) (2023-05-03)\n\n### ✨ Features | 新功能\n\n* add-nearby-component ([85a301c](https://github.com/ByteCrazy/chameleon/commit/85a301c3d95e785c116ab38c0b3a452ceb33742f))\n* **build-script, build-script-example, demo-page, engine, layout, material, model, render:** 🎸 config code style config ([2325504](https://github.com/ByteCrazy/chameleon/commit/23255048fa4a3d4fc8f5cfa1312db5abd6cac70d))\n* **build-script, demo-page, engine, layout, model, render:** 🎸 update meterial schema ([14f4e5c](https://github.com/ByteCrazy/chameleon/commit/14f4e5caf0a4f6eca131e139d0c34ea21969dc0b))\n* **demo-page, docs-website, engine, layout, model, render:** 🎸 node support methods config ([e080b20](https://github.com/ByteCrazy/chameleon/commit/e080b20cea3cea27b95def196e078c1c225d3a83))\n* **demo-page, engine, layout, model:** 🎸 finish customEvent config ([84640e3](https://github.com/ByteCrazy/chameleon/commit/84640e3d06b79858590b9fe92ef4764fbe3f4f7b))\n* **engine, layout, model:** 🎸 support supportDispatchNativeEvent field ([489fd05](https://github.com/ByteCrazy/chameleon/commit/489fd05b588e85ed4cea81cc33ab27739a1bac59))\n* remove some useless code and fix some problems detected by eslint. ([a8e6ec2](https://github.com/ByteCrazy/chameleon/commit/a8e6ec2f107250facefc81cf87f694e40f3ed684))\n* select element after in add node ([0c0d116](https://github.com/ByteCrazy/chameleon/commit/0c0d1164783420da306062292674fbccb0a8ffb0))\n* update model code ([f74162a](https://github.com/ByteCrazy/chameleon/commit/f74162a32dda1fa4244705a0b63066d73a4d6537))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* adjust find node logic ([5f9f073](https://github.com/ByteCrazy/chameleon/commit/5f9f07320888b3a83a4a24b04cfbd2e9e82aa4b1))\n* **engine, layout, model:** 🐛 fixed type error ([d11e81f](https://github.com/ByteCrazy/chameleon/commit/d11e81f607ef6a41a2dfda0b4ac657a9a87e948c))\n* eslint warning ([403342f](https://github.com/ByteCrazy/chameleon/commit/403342f60420326a69e4b46a1560ba0f4845ed60))\n* **model:** page thirdLibs forget process find return ([e1c5d2b](https://github.com/ByteCrazy/chameleon/commit/e1c5d2b3a7e365f892274c80402a72b7003151a8))\n\n## [0.0.12](https://github.com/ByteCrazy/chameleon/compare/v0.0.11...v0.0.12) (2023-04-23)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **build-script, build-script-example, engine, layout, material, model, render:** 🐛 fix can not find vite/client.d.ts problem ([8a53540](https://github.com/ByteCrazy/chameleon/commit/8a53540f75737707f69f93d2f701203dbf88084a))\n\n### ✨ Features | 新功能\n\n* **demo-page, docs-website, engine, model:** 🎸 update schema and change err to warn when schema check ([e753862](https://github.com/ByteCrazy/chameleon/commit/e7538626bad6681e4d488ac835fef61a603c0853))\n* **demo-page, engine, layout, model, render:** 🎸 node support config self isContainer property ([d94f1f9](https://github.com/ByteCrazy/chameleon/commit/d94f1f9203f2c273856fd4aa489a1ed9cbb0f0ec))\n\n## [0.0.11](https://github.com/ByteCrazy/chameleon/compare/v0.0.10...v0.0.11) (2023-04-21)\n\n### ✨ Features | 新功能\n\n* **build-script, build-script-example, demo-page, engine, layout, material, model, render:** 🎸 optimize build-script output file format ([eda44d2](https://github.com/ByteCrazy/chameleon/commit/eda44d255bd3c5af19013bdd61342e1f0817413a))\n* **engine, model:** 🎸 support custom setter ([5236c54](https://github.com/ByteCrazy/chameleon/commit/5236c543559aac517e833741ffa8046bc1c7f1a3))\n\n### 📝 Documentation | 文档\n\n* **docs-website, model:** ✏️ add material setter doc ([c7490fb](https://github.com/ByteCrazy/chameleon/commit/c7490fb9c9e9ef4cba9e33c5270a035592e3c46e))\n\n## [0.0.10](https://github.com/ByteCrazy/chameleon/compare/v0.0.9...v0.0.10) (2023-04-17)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.0.9](https://github.com/ByteCrazy/chameleon/compare/v0.0.8...v0.0.9) (2023-04-17)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.0.8](https://github.com/ByteCrazy/chameleon/compare/v0.0.7...v0.0.8) (2023-04-16)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, layout, model, render:** 🎸 optimize pack package ([78d99c5](https://github.com/ByteCrazy/chameleon/commit/78d99c507ad65ca39101f0239467737d2b445c87))\n\n## [0.0.7](https://github.com/ByteCrazy/chameleon/compare/v0.0.6...v0.0.7) (2023-03-29)\n\n### ✨ Features | 新功能\n\n* **build-script, build-script-example, demo-page, engine, layout, material, model, render:** 🎸 external monaco eitor and remove style panel ([494d898](https://github.com/ByteCrazy/chameleon/commit/494d898fd75dabe84d867ff45e84ae63f9b59c5e))\n\n## [0.0.6](https://github.com/ByteCrazy/chameleon/compare/v0.0.5...v0.0.6) (2023-03-28)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.0.5](https://github.com/ByteCrazy/chameleon/compare/v0.0.4...v0.0.5) (2023-03-28)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, layout, material, model, render:** 🎸 complete package info ([86b095d](https://github.com/ByteCrazy/chameleon/commit/86b095da6c955946300591b1775f59c1a141c065))\n\n## [0.0.4](https://github.com/ByteCrazy/chameleon/compare/v0.0.3...v0.0.4) (2023-03-27)\n\n**Note:** Version bump only for package @chamn/model\n\n## [0.0.2](https://github.com/ByteCrazy/chameleon/compare/v0.0.1...v0.0.2) (2023-03-27)\n\n**Note:** Version bump only for package @chamn/model\n"
  },
  {
    "path": "packages/model/LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "packages/model/__tests__/Material/index.test.ts",
    "content": "import { CMaterials } from '../../src/Material/index';\nimport { AdvanceDataType, BaseDataType, CMaterialType, ShapeDataType, SpecialDataType } from '../../src/types/material';\n\nconst mockMaterialData: CMaterialType[] = [\n  {\n    title: '测试物料组件',\n    componentName: 'Header',\n    category: '默认',\n    groupName: 'A',\n    npm: {\n      package: 'antd',\n      version: '1.0.0',\n      exportName: 'Button',\n      destructuring: true,\n    },\n    props: [\n      {\n        name: 'text',\n        title: '文本',\n        defaultValue: '按钮',\n        setters: ['StringSetter'],\n        valueType: {\n          type: AdvanceDataType.SHAPE,\n          value: [\n            {\n              name: 'key1',\n              title: 'key1',\n              valueType: {\n                type: AdvanceDataType.SHAPE,\n                value: [\n                  {\n                    name: 'key2',\n                    title: 'key2',\n                    valueType: BaseDataType.STRING,\n                  },\n                ],\n              },\n            },\n            {\n              name: 'renderItem',\n              title: '渲染子元素',\n              valueType: SpecialDataType.COMPONENT,\n            },\n            {\n              name: 'expression',\n              title: '表达式',\n              valueType: SpecialDataType.EXPRESSION,\n            },\n          ],\n        } as ShapeDataType,\n      },\n      {\n        name: 'renderItem',\n        title: '渲染子元素',\n        valueType: SpecialDataType.COMPONENT,\n      },\n      {\n        name: 'expression',\n        title: '表达式',\n        valueType: SpecialDataType.EXPRESSION,\n      },\n    ],\n    snippets: [\n      {\n        title: 'demo',\n        schema: {\n          props: {\n            a: 1,\n          },\n        },\n      },\n      {\n        title: 'demo2',\n        schema: {\n          props: {\n            a: 1,\n          },\n        },\n      },\n    ],\n  },\n  {\n    title: '测试物料组件',\n    componentName: 'Header2',\n    category: '高级2',\n    groupName: 'B',\n    npm: {\n      package: 'antd',\n      version: '1.0.0',\n      exportName: 'Button',\n      destructuring: true,\n    },\n    props: [\n      {\n        name: 'text',\n        title: '文本',\n        defaultValue: '按钮',\n        setters: ['StringSetter'],\n        valueType: {\n          type: AdvanceDataType.SHAPE,\n          value: [\n            {\n              name: 'key1',\n              title: 'key1',\n              valueType: {\n                type: AdvanceDataType.SHAPE,\n                value: [\n                  {\n                    name: 'key2',\n                    title: 'key2',\n                    valueType: BaseDataType.STRING,\n                  },\n                ],\n              },\n            },\n            {\n              name: 'renderItem',\n              title: '渲染子元素',\n              valueType: SpecialDataType.COMPONENT,\n            },\n            {\n              name: 'expression',\n              title: '表达式',\n              valueType: SpecialDataType.EXPRESSION,\n            },\n          ],\n        } as ShapeDataType,\n      },\n      {\n        name: 'renderItem',\n        title: '渲染子元素',\n        valueType: SpecialDataType.COMPONENT,\n      },\n      {\n        name: 'expression',\n        title: '表达式',\n        valueType: SpecialDataType.EXPRESSION,\n      },\n    ],\n    snippets: [\n      {\n        title: 'demo',\n        schema: {\n          props: {\n            a: 1,\n          },\n        },\n      },\n      {\n        title: 'demo2',\n        schema: {\n          props: {\n            a: 1,\n          },\n        },\n      },\n    ],\n  },\n];\n\ndescribe('test page model', () => {\n  it('new a page instance', () => {\n    const node = new CMaterials(mockMaterialData);\n    expect(node).not.toBeNull();\n  });\n\n  it('get all snippets', () => {\n    const node = new CMaterials(mockMaterialData);\n    expect(node).not.toBeNull();\n    expect(node.getAllSnippets().length).not.toEqual(0);\n  });\n});\n"
  },
  {
    "path": "packages/model/__tests__/Page/__snapshots__/index.test.ts.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`test page model test export function 1`] = `\nObject {\n  \"componentsMeta\": Array [\n    Object {\n      \"componentName\": \"Button\",\n      \"exportName\": \"Button\",\n      \"package\": \"demo\",\n      \"version\": \"1.0.0\",\n    },\n    Object {\n      \"componentName\": \"Header\",\n      \"exportName\": \"Header\",\n      \"package\": \"demo\",\n      \"version\": \"1.0.0\",\n    },\n  ],\n  \"componentsTree\": Object {\n    \"children\": Array [\n      Object {\n        \"componentName\": \"Header\",\n        \"configure\": Object {\n          \"propsSetter\": Object {},\n        },\n        \"id\": \"1\",\n        \"props\": Object {\n          \"age\": 12,\n          \"itemRender\": Object {\n            \"renderType\": \"COMP\",\n            \"type\": \"SLOT\",\n            \"value\": Array [\n              Object {\n                \"componentName\": \"Button\",\n                \"configure\": Object {\n                  \"propsSetter\": Object {},\n                },\n                \"id\": \"2\",\n              },\n            ],\n          },\n          \"key\": \"1\",\n        },\n      },\n    ],\n    \"componentName\": \"CPage\",\n  },\n  \"pageName\": \"testPage\",\n  \"style\": \"\",\n  \"version\": \"1.1.0\",\n}\n`;\n"
  },
  {
    "path": "packages/model/__tests__/Page/editMethods.test.ts",
    "content": "import { BasePage } from '../../mockPage/basePage';\nimport { CPage } from '../../src/Page/index';\nimport { CNode } from '../../src/Page/Schema/Node';\nimport { CSlot } from '../../src/Page/Schema/Node/slot';\nimport { CPageDataType } from '../../src/types/page';\n\nconst mockPageData = BasePage as CPageDataType;\ndescribe('test page model methods', () => {\n  it('test getNode', () => {\n    const page = new CPage(mockPageData);\n    const node = page.getNode('5');\n    const node2 = page.getNode('10');\n    const node3 = page.getNode('1110');\n    expect(node).not.toBeNull();\n    expect(node2).not.toBeNull();\n    expect(node3).toBeNull();\n  });\n\n  it('test addNode', () => {\n    const page = new CPage(mockPageData);\n    const node = page.getNode('5');\n    const newNode = new CNode({\n      componentName: 'Button',\n      children: ['动态添加的按钮'],\n    });\n    const newNode2 = new CNode({\n      componentName: 'Button',\n      children: ['动态添加的按钮 end'],\n    });\n    const newNode3 = new CNode({\n      componentName: 'Button',\n      children: ['动态添加的按钮 current After'],\n    });\n\n    const newNode4 = new CNode({\n      componentName: 'Button',\n      children: ['动态添加的按钮 current Before'],\n    });\n    page.addNode(newNode, node!, 'CHILD_START');\n    page.addNode(newNode2, node!, 'CHILD_END');\n    page.addNode(newNode3, node!, 'AFTER');\n    page.addNode(newNode4, node!, 'BEFORE');\n    expect(node?.value.children[0]).toEqual(newNode);\n    expect(node?.value.children[node?.value.children.length - 1]).toEqual(\n      newNode2\n    );\n  });\n  it('test addNode on child BEFORE and AFTER', () => {\n    const page = new CPage(mockPageData);\n\n    const targetNode2 = page.getNode('div1');\n    const n1 = new CNode({\n      componentName: 'Button',\n      children: ['动态添加的按钮 current normal After'],\n    });\n    const n2 = new CNode({\n      componentName: 'Button',\n      children: ['动态添加的按钮 current normal After'],\n    });\n    page.addNode(n1, targetNode2!, 'BEFORE');\n    page.addNode(n2, targetNode2!, 'AFTER');\n    const parentNode = targetNode2?.parent;\n    // 当节点不是 CPage 活着 CSlot 时\n    if (!(parentNode instanceof CSlot) && !(parentNode instanceof CPage)) {\n      expect(parentNode?.value.children[0]).toEqual(n1);\n      expect(parentNode?.value.children[2]).toEqual(n2);\n    }\n  });\n\n  it('test delete node', () => {\n    const page = new CPage(mockPageData);\n\n    const targetNode1 = page.getNode('5');\n    const targetNode2 = page.getNode('div1');\n\n    page.deleteNode(targetNode1!);\n    page.deleteNode(targetNode2!);\n    expect(page.getNode('5')).toBeFalsy();\n    expect(page.getNode('div1')).toBeFalsy();\n  });\n\n  it('test copy a node', () => {\n    const page = new CPage(mockPageData);\n    const sourceNode = page.getNode('5');\n    const newNode = page.copyNodeById('5');\n    expect(newNode).not.toBeNull();\n    if (newNode) {\n      expect(newNode.value.componentName).toEqual('Row');\n      expect(newNode.id).not.toEqual(sourceNode?.id);\n    }\n  });\n  it('test move a node', () => {\n    const page = new CPage(mockPageData);\n\n    page.moveNodeById('8', '99898999', 'AFTER');\n    page.export();\n    page.moveNodeById('999', '5', 'AFTER');\n    page.export();\n    page.moveNodeById('5', '2', 'AFTER');\n    page.export();\n\n    const targetNode = page.getNode('5');\n    const anchorNode = page.getNode('2');\n    expect(page.getNode('5')).not.toBeNull();\n    expect(targetNode?.parent).toEqual(anchorNode?.parent);\n    const parent = anchorNode?.parent as CNode;\n    const findNodeRes = parent?.value.children.find((el) => el === targetNode);\n    expect(findNodeRes).not.toBeNull();\n  });\n});\n"
  },
  {
    "path": "packages/model/__tests__/Page/index.test.ts",
    "content": "import { CPage } from '../../src/Page/index';\nimport { CMaterialType } from '../../src/types/material';\nimport { CNodeDataType } from '../../src/types/node';\nimport { CPageDataType } from '../../src/types/page';\nimport { BasePage } from '@chamn/demo-page';\nimport { InnerComponentNameEnum } from '../../src/types/schema';\n\nconst mockMaterial: CMaterialType[] = [\n  {\n    title: '按钮',\n    componentName: 'Button',\n    props: [\n      {\n        title: '文案',\n        name: 'text',\n        valueType: 'string',\n      },\n    ],\n    npm: {\n      package: 'demo',\n      version: '1.0.0',\n      exportName: 'Button',\n    },\n    snippets: [\n      {\n        title: '测试按钮',\n        schema: {\n          componentName: 'Button',\n        },\n      },\n    ],\n  },\n  {\n    title: '页首',\n    componentName: 'Header',\n    props: [\n      {\n        name: 'key',\n        title: '唯一标记',\n        valueType: 'string',\n      },\n      {\n        name: 'age',\n        title: '年龄',\n        valueType: 'number',\n      },\n      {\n        name: 'itemRender',\n        title: 'itemRender',\n        valueType: 'string',\n      },\n    ],\n    npm: {\n      package: 'demo',\n      version: '1.0.0',\n      exportName: 'Header',\n    },\n    snippets: [],\n  },\n];\n\nconst mockNodeData: CNodeDataType = {\n  id: '1',\n  componentName: 'Header',\n  props: {\n    key: '1',\n    age: 12,\n    itemRender: {\n      type: 'SLOT',\n      renderType: 'COMP',\n      value: [\n        {\n          id: '2',\n          componentName: 'Button',\n        },\n      ],\n    },\n  },\n};\n\nconst mockPageData: CPageDataType = {\n  version: '1.1.0',\n  name: 'testPage',\n  componentsMeta: [\n    {\n      componentName: 'Button',\n      package: 'antd',\n      version: '1.0',\n      exportName: 'Button',\n    },\n  ],\n  componentsTree: {\n    componentName: InnerComponentNameEnum.ROOT_CONTAINER,\n    children: [mockNodeData],\n  },\n};\n\ndescribe('test page model', () => {\n  it('new a page instance with jsslot', () => {\n    const node = new CPage(BasePage as any);\n    expect(node).not.toBeNull();\n  });\n\n  it('new a page instance', () => {\n    const node = new CPage(mockPageData);\n    expect(node).not.toBeNull();\n  });\n\n  it('validate a bad page', () => {\n    const newPageData = {\n      ...mockPageData,\n    };\n    newPageData.version = 1 as any;\n    let isError = false;\n    try {\n      new CPage(newPageData);\n    } catch (e) {\n      isError = true;\n    }\n\n    expect(isError).toBeTruthy();\n  });\n\n  it('test export function', () => {\n    const page = new CPage(mockPageData, {\n      materials: mockMaterial,\n    });\n    expect(page).not.toBeNull();\n    expect(page.emitter).not.toBeUndefined();\n    expect(page.value.componentsTree.value.children[0].material).not.toBeUndefined();\n    expect(page.export()).toMatchSnapshot();\n  });\n\n  it('test page init with material', () => {\n    const page = new CPage(mockPageData, {\n      materials: mockMaterial,\n    });\n    expect(page).not.toBeNull();\n    expect(page.emitter).not.toBeUndefined();\n    expect(page.value.componentsTree.value.children[0].material).not.toBeUndefined();\n  });\n});\n"
  },
  {
    "path": "packages/model/__tests__/Schema/Node.test.ts",
    "content": "import { CNode } from '../../src/Page/Schema/Node/index';\n\ndescribe('test node model', () => {\n  it('new a node instance', () => {\n    const mockData = {\n      id: '1',\n      componentName: 'Header',\n      props: {\n        key: '1',\n        age: 12,\n        itemRender: {\n          type: 'SLOT',\n          renderType: 'FUNC',\n          value: {\n            id: '2',\n            componentName: 'Button',\n          },\n        },\n      },\n    };\n    const node = new CNode(mockData, { parent: null, materials: null });\n    expect(node).not.toBeNull();\n    expect(node.value.componentName).toEqual(mockData.componentName);\n  });\n\n  it('test node without pros', () => {\n    const mockData = {\n      id: '1',\n      componentName: 'Header',\n    };\n    const node = new CNode(mockData);\n    expect(node).not.toBeNull();\n  });\n});\n"
  },
  {
    "path": "packages/model/__tests__/demo.test.ts",
    "content": "test('adds 1 + 2 to equal 3', () => {\n  expect(3).toBe(3);\n});\n"
  },
  {
    "path": "packages/model/build.config.js",
    "content": "// 开发模式默认读取 index.html 作为开发模式入口\n// entry 作为打包库入口\nexport default {\n  entry: './src/index.ts',\n  libName: 'CModel',\n  fileName: 'index',\n  global: {\n    react: 'React',\n    'react-dom': 'ReactDOM',\n  },\n};\n"
  },
  {
    "path": "packages/model/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <link rel=\"icon\" type=\"image/svg+xml\" href=\"/vite.svg\" />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n  <title>Vite + React + TS</title>\n</head>\n\n<body>\n  <div id=\"root\"></div>\n  <script type=\"module\" src=\"/src/index.ts\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "packages/model/jest.config.js",
    "content": "/* eslint-disable no-undef */\n/*\n * For a detailed explanation regarding each configuration property, visit:\n * https://jestjs.io/docs/configuration\n */\n\nmodule.exports = {\n  // All imported modules in your tests should be mocked automatically\n  // automock: false,\n\n  // Stop running tests after `n` failures\n  // bail: 0,\n\n  // The directory where Jest should store its cached dependency information\n  // cacheDirectory: \"/private/var/folders/l9/th_r5d_12wxdj16859_mctjw0000gn/T/jest_dx\",\n\n  // Automatically clear mock calls, instances, contexts and results before every test\n  // clearMocks: false,\n\n  // Indicates whether the coverage information should be collected while executing the test\n  // collectCoverage: false,\n\n  // An array of glob patterns indicating a set of files for which coverage information should be collected\n  // collectCoverageFrom: undefined,\n\n  // The directory where Jest should output its coverage files\n  // coverageDirectory: undefined,\n\n  // An array of regexp pattern strings used to skip coverage collection\n  // coveragePathIgnorePatterns: [\n  //   \"/node_modules/\"\n  // ],\n\n  // Indicates which provider should be used to instrument code for coverage\n  // coverageProvider: \"babel\",\n\n  // A list of reporter names that Jest uses when writing coverage reports\n  // coverageReporters: [\n  //   \"json\",\n  //   \"text\",\n  //   \"lcov\",\n  //   \"clover\"\n  // ],\n\n  // An object that configures minimum threshold enforcement for coverage results\n  // coverageThreshold: undefined,\n\n  // A path to a custom dependency extractor\n  // dependencyExtractor: undefined,\n\n  // Make calling deprecated APIs throw helpful error messages\n  // errorOnDeprecated: false,\n\n  // The default configuration for fake timers\n  // fakeTimers: {\n  //   \"enableGlobally\": false\n  // },\n\n  // Force coverage collection from ignored files using an array of glob patterns\n  // forceCoverageMatch: [],\n\n  // A path to a module which exports an async function that is triggered once before all test suites\n  // globalSetup: undefined,\n\n  // A path to a module which exports an async function that is triggered once after all test suites\n  // globalTeardown: undefined,\n\n  // A set of global variables that need to be available in all test environments\n  // globals: {},\n\n  // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.\n  // maxWorkers: \"50%\",\n\n  // An array of directory names to be searched recursively up from the requiring module's location\n  // moduleDirectories: [\n  //   \"node_modules\"\n  // ],\n\n  // An array of file extensions your modules use\n  // moduleFileExtensions: [\n  //   \"js\",\n  //   \"mjs\",\n  //   \"cjs\",\n  //   \"jsx\",\n  //   \"ts\",\n  //   \"tsx\",\n  //   \"json\",\n  //   \"node\"\n  // ],\n\n  // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module\n  moduleNameMapper: {\n    '^lodash-es$': 'lodash',\n  },\n  // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader\n  // modulePathIgnorePatterns: [],\n\n  // Activates notifications for test results\n  // notify: false,\n\n  // An enum that specifies notification mode. Requires { notify: true }\n  // notifyMode: \"failure-change\",\n\n  // A preset that is used as a base for Jest's configuration\n  preset: 'ts-jest',\n\n  // Run tests from one or more projects\n  // projects: undefined,\n\n  // Use this configuration option to add custom reporters to Jest\n  // reporters: undefined,\n\n  // Automatically reset mock state before every test\n  // resetMocks: false,\n\n  // Reset the module registry before running each individual test\n  // resetModules: false,\n\n  // A path to a custom resolver\n  // resolver: undefined,\n\n  // Automatically restore mock state and implementation before every test\n  // restoreMocks: false,\n\n  // The root directory that Jest should scan for tests and modules within\n  // rootDir: undefined,\n\n  // A list of paths to directories that Jest should use to search for files in\n  // roots: [\n  //   \"<rootDir>\"\n  // ],\n\n  // Allows you to use a custom runner instead of Jest's default test runner\n  // runner: \"jest-runner\",\n\n  // The paths to modules that run some code to configure or set up the testing environment before each test\n  setupFiles: ['<rootDir>/jest.setup.js'],\n\n  // A list of paths to modules that run some code to configure or set up the testing framework before each test\n  // setupFilesAfterEnv: [],\n\n  // The number of seconds after which a test is considered as slow and reported as such in the results.\n  // slowTestThreshold: 5,\n\n  // A list of paths to snapshot serializer modules Jest should use for snapshot testing\n  // snapshotSerializers: [],\n\n  // The test environment that will be used for testing\n  testEnvironment: 'jsdom',\n\n  // Options that will be passed to the testEnvironment\n  // testEnvironmentOptions: {},\n\n  // Adds a location field to test results\n  // testLocationInResults: false,\n\n  // The glob patterns Jest uses to detect test files\n  // testMatch: [\n  //   \"**/__tests__/**/*.[jt]s?(x)\",\n  //   \"**/?(*.)+(spec|test).[tj]s?(x)\"\n  // ],\n\n  // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped\n  // testPathIgnorePatterns: [\n  //   \"/node_modules/\"\n  // ],\n\n  // The regexp pattern or array of patterns that Jest uses to detect test files\n  // testRegex: [],\n\n  // This option allows the use of a custom results processor\n  // testResultsProcessor: undefined,\n\n  // This option allows use of a custom test runner\n  // testRunner: \"jest-circus/runner\",\n\n  // A map from regular expressions to paths to transformers\n  // transform: undefined,\n\n  // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation\n  // transformIgnorePatterns: [\n  //   \"/node_modules/\",\n  //   \"\\\\.pnp\\\\.[^\\\\/]+$\"\n  // ],\n\n  // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them\n  // unmockedModulePathPatterns: undefined,\n\n  // Indicates whether each individual test should be reported during the run\n  // verbose: undefined,\n\n  // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode\n  // watchPathIgnorePatterns: [],\n\n  // Whether to use watchman for file crawling\n  // watchman: true,\n};\n"
  },
  {
    "path": "packages/model/jest.setup.js",
    "content": "/* eslint-disable @typescript-eslint/no-empty-function */\n/* eslint-disable no-undef */\njest.mock('mitt', () => {\n  return {\n    default: () => {\n      return {\n        on: () => {},\n        off: () => {},\n        emit: () => {},\n        clear: () => {},\n      };\n    },\n  };\n});\n\njest.mock('html-tag-names', () => {\n  return [];\n});\n"
  },
  {
    "path": "packages/model/mockPage/basePage.ts",
    "content": "const data = [\n  {\n    key: '1',\n    name: 'John Brown',\n    age: 32,\n    address: 'New York No. 1 Lake Park',\n    tags: ['nice', 'developer'],\n  },\n  {\n    key: '2',\n    name: 'Jim Green',\n    age: 42,\n    address: 'London No. 1 Lake Park',\n    tags: ['loser'],\n  },\n  {\n    key: '3',\n    name: 'Joe Black',\n    age: 32,\n    address: 'Sidney No. 1 Lake Park',\n    tags: ['cool', 'teacher'],\n  },\n];\n\nconst columns = [\n  {\n    title: 'Name',\n    dataIndex: 'name',\n    key: 'name',\n    render: {\n      type: 'SLOT',\n      renderType: 'FUNC',\n      params: ['val', 'record', 'index'],\n      value: [\n        {\n          id: '5',\n          componentName: 'Row',\n          children: [\n            {\n              id: '6',\n              componentName: 'Button',\n              props: {\n                mark: 'nameRender',\n                children: {\n                  type: 'EXPRESSION',\n                  value: '$$context.params.val',\n                },\n              },\n            },\n            {\n              id: '7',\n              componentName: 'Col',\n              children: [\n                {\n                  componentName: 'Button',\n                  props: {\n                    mark: 'nameRender',\n                  },\n                  children: [\n                    {\n                      id: '8',\n                      componentName: 'div',\n                      children: ['I am div'],\n                    },\n                  ],\n                },\n              ],\n            },\n          ],\n        },\n        {\n          id: '99898999',\n          componentName: 'Button',\n          children: ['123'],\n        },\n      ],\n    },\n  },\n  {\n    title: 'Age',\n    dataIndex: 'age',\n    key: 'age',\n  },\n  {\n    title: 'Address',\n    dataIndex: 'address',\n    key: 'address',\n  },\n  {\n    title: 'Tags',\n    key: 'tags',\n    dataIndex: 'tags',\n  },\n  {\n    title: 'Action',\n    key: 'action',\n  },\n];\n\nexport const BasePage = {\n  version: '1.0.0',\n  pageName: 'BaseDemoPage',\n  componentsMeta: [],\n  componentsTree: {\n    id: '1',\n    componentName: 'Page',\n    props: {\n      a: 1,\n    },\n    state: {\n      b: 1,\n      buttonVisible: true,\n      modalVisible: false,\n    },\n    children: [\n      {\n        id: 'div1',\n        componentName: 'div',\n        children: [\n          {\n            id: 'div1btn',\n            componentName: 'Button',\n            state: {\n              list: [11, 22, 33, 44, 55],\n            },\n            props: {\n              children: {\n                type: 'EXPRESSION',\n                value: '$$context.loopData.index',\n              },\n            },\n            loop: {\n              open: true,\n              data: {\n                type: 'EXPRESSION',\n                value: '$$context.state.list',\n              },\n            },\n          },\n        ],\n      },\n      {\n        id: 'div12222',\n        componentName: 'div',\n        children: ['566666'],\n      },\n      {\n        id: 'Modal',\n        componentName: 'Modal',\n        refId: 'ModalRef',\n        props: {\n          open: {\n            type: 'EXPRESSION',\n            value: '$$context.globalState.modalVisible',\n          },\n          onCancel: {\n            type: 'FUNCTION',\n            value: `\n            function (a, b) {\n                b.updateGlobalState({\n                 modalVisible: false\n                });\n            }\n            `,\n          },\n        },\n      },\n      {\n        id: '999',\n        componentName: 'Button',\n        state: {\n          a: 1,\n        },\n        props: {\n          type: 'primary',\n          onClick: {\n            type: 'FUNCTION',\n            value: `function onClick(a,b) {\n              console.log(a, b);\n              b.updateState({a: b.state.a + 1})\n              b.updateGlobalState({\n                buttonVisible: !b.globalState.buttonVisible,\n                modalVisible: !b.globalState.modalVisible\n              })\n            }`,\n          },\n          children: ['控制右边按钮的显示隐藏'],\n        },\n      },\n      {\n        id: '2',\n        componentName: 'Button',\n        state: {\n          a: 1,\n        },\n        props: {\n          type: 'primary',\n          onClick: {\n            type: 'FUNCTION',\n            value: `function onClick(a,b) {\n              console.log(a, b);\n              b.updateState({a: b.state.a + 1})\n              b.updateGlobalState({ b: b.globalState.b + 1})\n            }`,\n          },\n          children: {\n            type: 'EXPRESSION',\n            value: '$$context.globalState.b',\n          },\n        },\n        condition: {\n          type: 'EXPRESSION',\n          value: '$$context.globalState.buttonVisible',\n        },\n      },\n\n      {\n        id: '3',\n        componentName: 'Table',\n        state: {\n          a: 3,\n          data: data,\n        },\n        props: {\n          columns,\n          dataSource: {\n            type: 'EXPRESSION',\n            value: '$$context.state.data',\n          },\n        },\n      },\n      {\n        id: '4',\n        componentName: 'Row',\n        state: {\n          rowMark: 1,\n        },\n        nodeName: 'RowState',\n        children: [\n          {\n            componentName: 'div',\n            props: {\n              children: {\n                type: 'EXPRESSION',\n                value: '\"rowState to reshow: \" + $$context.stateManager.RowState.state.rowMark',\n              },\n            },\n          },\n          {\n            id: '10',\n            componentName: 'Col',\n            children: [\n              {\n                id: '11',\n                componentName: 'Button',\n                children: ['123 木头人'],\n              },\n            ],\n          },\n          {\n            id: '12',\n            componentName: 'Input',\n            props: {\n              value: {\n                type: 'EXPRESSION',\n                value: '$$context.globalState.b',\n              },\n              onChange: {\n                type: 'FUNCTION',\n                value: `\n                  function(value, $$context) {\n                    console.log(value, $$context);\n                    $$context.updateGlobalState({\n                      b: value.target.value\n                    })\n                  }\n                `,\n              },\n            },\n          },\n          {\n            componentName: 'div',\n            children: [\n              '2222',\n              {\n                componentName: 'div',\n                children: [\n                  '1111',\n                  {\n                    componentName: 'Button',\n                    props: {\n                      onClick: {\n                        type: 'FUNCTION',\n                        value: `\n                          function (a, ctx) {\n                            console.log(a, ctx);\n                            const stateManager = ctx.stateManager;\n                            const state = stateManager.RowState.state;\n                            stateManager.RowState.updateState({\n                              rowMark: state.rowMark  +1\n                            })\n                          }\n                        `,\n                      },\n                    },\n                    children: ['change row state value'],\n                  },\n                ],\n              },\n            ],\n          },\n        ],\n      },\n    ],\n  },\n};\n"
  },
  {
    "path": "packages/model/mockPage/material.ts",
    "content": "import { AdvanceDataType, CMaterialType } from '../src/types/material';\n\nexport const mockMaterialData: CMaterialType[] = [\n  {\n    title: '测试物料组件',\n    componentName: 'Header',\n    category: '默认',\n    groupName: 'A',\n    npm: {\n      package: 'antd',\n      version: '1.0.0',\n      exportName: 'Button',\n      destructuring: true,\n    },\n    props: [\n      {\n        name: 'text',\n        title: '文本',\n        defaultValue: '按钮',\n        setters: ['StringSetter'],\n        valueType: {\n          type: AdvanceDataType.SHAPE,\n          value: [\n            {\n              name: 'key1',\n              title: 'key1',\n              valueType: {\n                type: AdvanceDataType.SHAPE,\n                value: [\n                  {\n                    name: 'key2',\n                    title: 'key2',\n                    valueType: BaseDataType.STRING,\n                  },\n                ],\n              },\n            },\n            {\n              name: 'renderItem',\n              title: '渲染子元素',\n              valueType: SpecialDataType.COMPONENT,\n            },\n            {\n              name: 'expression',\n              title: '表达式',\n              valueType: SpecialDataType.EXPRESSION,\n            },\n          ],\n        } as ShapeDataType,\n      },\n      {\n        name: 'renderItem',\n        title: '渲染子元素',\n        valueType: SpecialDataType.COMPONENT,\n      },\n      {\n        name: 'expression',\n        title: '表达式',\n        valueType: SpecialDataType.EXPRESSION,\n      },\n    ],\n    snippets: [\n      {\n        title: 'demo',\n        schema: {\n          props: {\n            a: 1,\n          },\n        },\n      },\n      {\n        title: 'demo2',\n        schema: {\n          props: {\n            a: 1,\n          },\n        },\n      },\n    ],\n  },\n  {\n    title: '测试物料组件',\n    componentName: 'Header2',\n    category: '高级2',\n    groupName: 'B',\n    npm: {\n      package: 'antd',\n      version: '1.0.0',\n      exportName: 'Button',\n      destructuring: true,\n    },\n    props: [\n      {\n        name: 'text',\n        title: '文本',\n        defaultValue: '按钮',\n        setters: ['StringSetter'],\n        valueType: {\n          type: AdvanceDataType.SHAPE,\n          value: [\n            {\n              name: 'key1',\n              title: 'key1',\n              valueType: {\n                type: AdvanceDataType.SHAPE,\n                value: [\n                  {\n                    name: 'key2',\n                    title: 'key2',\n                    valueType: BaseDataType.STRING,\n                  },\n                ],\n              },\n            },\n            {\n              name: 'renderItem',\n              title: '渲染子元素',\n              valueType: SpecialDataType.COMPONENT,\n            },\n            {\n              name: 'expression',\n              title: '表达式',\n              valueType: SpecialDataType.EXPRESSION,\n            },\n          ],\n        } as ShapeDataType,\n      },\n      {\n        name: 'renderItem',\n        title: '渲染子元素',\n        valueType: SpecialDataType.COMPONENT,\n      },\n      {\n        name: 'expression',\n        title: '表达式',\n        valueType: SpecialDataType.EXPRESSION,\n      },\n    ],\n    snippets: [\n      {\n        title: 'demo',\n        schema: {\n          props: {\n            a: 1,\n          },\n        },\n      },\n      {\n        title: 'demo2',\n        schema: {\n          props: {\n            a: 1,\n          },\n        },\n      },\n    ],\n  },\n];\n"
  },
  {
    "path": "packages/model/mockPage/simplePage.ts",
    "content": "export const SamplePage = {\n  version: '1.0.0',\n  pageName: 'BaseDemoPage',\n  componentsMeta: [],\n  componentsTree: {\n    id: '1',\n    componentName: 'Page',\n    props: {\n      a: 1,\n    },\n    children: [\n      {\n        id: 'Modal',\n        componentName: 'Modal',\n        props: {\n          open: false,\n        },\n      },\n      {\n        id: '2',\n        componentName: 'Button',\n        props: {\n          type: 'primary',\n        },\n        children: ['123'],\n      },\n    ],\n  },\n};\n"
  },
  {
    "path": "packages/model/package.json",
    "content": "{\n  \"name\": \"@chamn/model\",\n  \"version\": \"0.10.4\",\n  \"type\": \"module\",\n  \"files\": [\n    \"dist\"\n  ],\n  \"main\": \"dist/index.cjs.js\",\n  \"module\": \"dist/index.es.js\",\n  \"types\": \"dist/index.d.ts\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./dist/index.d.ts\",\n      \"module-sync\": \"./dist/index.es.js\",\n      \"import\": \"./dist/index.es.js\",\n      \"require\": \"./dist/index.cjs.js\"\n    },\n    \"./dist/*\": \"./dist/*\"\n  },\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"start\": \"build-script\",\n    \"build\": \"build-script --build\",\n    \"build:analyze\": \"build-script --build --analyze\",\n    \"build:w\": \"build-script --build --watch\",\n    \"lint\": \"eslint ./src\",\n    \"prettier\": \"prettier --write ./src\",\n    \"test\": \"jest\"\n  },\n  \"peerDependencies\": {\n    \"react\": \">=16.9.0\",\n    \"react-dom\": \">=16.9.0\"\n  },\n  \"dependencies\": {\n    \"html-tag-names\": \"^2.0.1\",\n    \"lodash-es\": \"^4.17.21\",\n    \"mitt\": \"^3.0.0\",\n    \"superstruct\": \"^0.16.0\"\n  },\n  \"devDependencies\": {\n    \"@chamn/build-script\": \"workspace:*\",\n    \"@types/lodash-es\": \"^4.17.6\",\n    \"@types/react\": \"^18.2.0\",\n    \"jest\": \"^28.1.3\",\n    \"sass\": \"^1.54.0\"\n  },\n  \"config\": {},\n  \"gitHead\": \"dc3e55fdeb903a8012f6ebd3ebc018ed61ad89db\"\n}\n"
  },
  {
    "path": "packages/model/src/Material/index.test.ts",
    "content": "import { CMaterials } from '.';\n\ndescribe('CMaterials', () => {\n  test('Add Materials methods', () => {\n    const mat = new CMaterials([\n      {\n        componentName: 'A',\n        title: 'A',\n        snippets: [],\n        props: [],\n      },\n      {\n        componentName: 'F',\n        title: 'F',\n        snippets: [],\n        props: [],\n      },\n    ]);\n\n    mat.addMaterials([\n      {\n        componentName: 'A',\n        title: 'AB',\n        snippets: [],\n        props: [],\n      },\n      {\n        componentName: 'D',\n        title: 'D',\n        snippets: [],\n        props: [],\n      },\n    ]);\n    expect(mat.value.length).toBe(3);\n    expect(mat.value[0].componentName).toEqual('A');\n    expect(mat.value[0].value.title).toEqual('AB');\n    expect(mat.value[2].value.title).toEqual('D');\n  });\n});\n"
  },
  {
    "path": "packages/model/src/Material/index.ts",
    "content": "import { CMaterialStanderType, CMaterialType, CMaterialTypeDescribe, SnippetsType } from '../types/material';\nimport { cloneDeep, isArray } from '../util/lodash';\nimport { checkComplexData } from '../util/dataCheck';\nimport { getRandomStr } from '../util';\n\nconst parseMaterial = (data: CMaterialType): CMaterialStanderType => {\n  const newData = cloneDeep(data);\n  const snippets = newData.snippets;\n  delete (newData as any).snippets;\n  newData.snippets = snippets.map((el) => {\n    return {\n      ...newData,\n      ...el,\n      id: el.id || `${data.componentName}-${getRandomStr()}`,\n      title: el.title || data.title,\n      category: el.category || data.category,\n      tags: [...(el.tags || []), ...(data.tags || [])],\n      groupName: el.groupName || data.groupName,\n      snapshot: el.snapshot || data.icon,\n      snapshotText: el.snapshotText,\n      schema: {\n        ...el.schema,\n        componentName: el.schema.componentName || data.componentName,\n      },\n    };\n  });\n  return newData as CMaterialStanderType;\n};\n\nexport class CMaterial {\n  private rawData: CMaterialType;\n  private data: CMaterialStanderType;\n  constructor(data: CMaterialType) {\n    this.rawData = data;\n    this.data = parseMaterial(data);\n  }\n\n  get value() {\n    return this.data;\n  }\n\n  get rawValue() {\n    return this.rawData;\n  }\n\n  get componentName() {\n    return this.data.componentName;\n  }\n\n  get snippets() {\n    return this.data.snippets;\n  }\n\n  getSnippetById(id: string) {\n    return this.data.snippets.find((el) => el.id === id);\n  }\n}\n\nconst parseMaterials = (data: any[]) => {\n  if (!isArray(data)) {\n    throw new Error('Materials must be a array');\n  }\n  return data.map((el) => {\n    return new CMaterial(el);\n  });\n};\n\nexport const checkMaterials = (data: CMaterialType[]) => {\n  // check page children\n  data?.forEach((it: any) => {\n    checkComplexData({\n      data: it,\n      dataStruct: CMaterialTypeDescribe,\n      throwError: false,\n    });\n  });\n};\n\nexport type SnippetsCollection = {\n  name: string;\n  list: { name: string; list: SnippetsType[] }[];\n}[];\nexport class CMaterials {\n  private rawData: CMaterialType[];\n  private data: CMaterial[];\n  // 使用过的物料\n  usedMaterials: CMaterial[] = [];\n\n  constructor(data: CMaterialType[]) {\n    this.rawData = data;\n    checkMaterials(data);\n    this.data = parseMaterials(data);\n  }\n\n  addMaterials(data: CMaterialType[]) {\n    const appendMaterial = parseMaterials(data);\n    const updateItemIndex: number[] = [];\n    const newList = this.data.map((el) => {\n      const targetItemIndex = appendMaterial.findIndex((it) => it.componentName === el.componentName);\n      if (targetItemIndex >= 0) {\n        updateItemIndex.push(targetItemIndex);\n        return appendMaterial[targetItemIndex];\n      }\n      return el;\n    });\n    const needAppend = appendMaterial.filter((_el, index) => {\n      return !updateItemIndex.includes(index);\n    });\n\n    this.data = [...newList, ...needAppend];\n  }\n\n  // 使用新的物料整个替换现有的物料列表\n  replaceMaterials(data: CMaterialType[]) {\n    const appendMaterial = parseMaterials(data);\n    this.data = [...appendMaterial];\n  }\n\n  removeMaterial(componentNames: string[]) {\n    const newData = this.data.filter((el) => {\n      return !componentNames.includes(el.componentName);\n    });\n    this.data = newData;\n  }\n\n  findByComponentName(componentName: string) {\n    const target = this.data.find((el) => el.componentName === componentName);\n    return target;\n  }\n\n  findSnippetById(id: string) {\n    const list = [...this.data];\n    let res = null;\n    while (!res && list.length) {\n      const target = list.pop();\n      res = target?.getSnippetById(id);\n    }\n    return res;\n  }\n\n  getAllSnippets() {\n    const allSnippets = this.data.reduce((res, el) => {\n      res.push(...el.snippets);\n      return res;\n    }, [] as SnippetsType[]);\n    const groups: string[] = [];\n    const groupRes: Record<string, SnippetsType[]> = {\n      default: [],\n    };\n\n    // split group\n    allSnippets.forEach((el) => {\n      const groupName = el.groupName || 'default';\n      if (!groups.includes(groupName)) {\n        groups.push(groupName);\n        groupRes[groupName] = [];\n      }\n      groupRes[groupName].push(el);\n    });\n\n    let res: SnippetsCollection = [];\n    groups.forEach((groupName) => {\n      const categories: string[] = ['default'];\n      const categoryRes: Record<string, SnippetsType[]> = {\n        default: [],\n      };\n      const list = groupRes[groupName];\n      if (list.length !== 0) {\n        list.forEach((el) => {\n          const categoryName = el.category || 'default';\n          if (!categories.includes(categoryName)) {\n            categories.push(categoryName);\n            categoryRes[categoryName] = [];\n          }\n          categoryRes[categoryName].push(el);\n        });\n\n        const categoryList: {\n          name: string;\n          list: SnippetsType[];\n        }[] = [];\n        categories.forEach((category) => {\n          if (categoryRes[category].length) {\n            categoryList.push({\n              name: category,\n              list: categoryRes[category],\n            });\n          }\n        });\n\n        const val = {\n          name: groupName,\n          list: categoryList,\n        };\n\n        res.push(val);\n      }\n    });\n\n    res = res.sort((a, b) => {\n      return (a.name || '') > (b.name || '') ? 1 : -1;\n    });\n\n    return res;\n  }\n\n  get value() {\n    return this.data;\n  }\n\n  get rawValue() {\n    return this.rawData;\n  }\n}\n"
  },
  {
    "path": "packages/model/src/Page/RootNode/Node/index.ts",
    "content": "import { get, isPlainObject, merge } from 'lodash-es';\nimport { CRootNode } from '..';\nimport { ExportType } from '../../../const/schema';\nimport { CMaterials } from '../../../Material';\nimport { CNodeDataStructDescribe, CNodeDataType } from '../../../types/node';\nimport { getRandomStr, clearSchema, getNode } from '../../../util';\nimport { checkComplexData } from '../../../util/dataCheck';\nimport { DataModelEmitter } from '../../../util/modelEmitter';\nimport type { DataModelEmitterType, DataModelEventType } from '../../../util/modelEmitter';\nimport { CProp } from './prop';\nimport { CSlot } from './slot';\n\nexport const checkNode = (data: any) => {\n  if (typeof data === 'string') {\n    return true;\n  }\n  // check data\n  checkComplexData({\n    data: data,\n    dataStruct: CNodeDataStructDescribe,\n    throwError: false,\n  });\n};\n\nexport type CNodeModelDataType = Omit<CNodeDataType, 'children'> & {\n  id: string;\n  children: (CNode | string)[];\n  props: Record<string, CProp>;\n  configure: Required<CNodeDataType>['configure'];\n};\n\nexport const parseNode = (data: CNodeDataType | CNodeModelDataType, self: CNode | CRootNode, materials: CMaterials) => {\n  if (typeof data === 'string') {\n    return data;\n  }\n  const res: CNodeModelDataType = {\n    ...data,\n    id: data.id ?? getRandomStr(),\n    children: [],\n    props: {},\n    methods: data.methods || [],\n    configure: merge(data.configure || {}, {\n      propsSetter: {},\n      advanceSetter: {},\n    }),\n  };\n\n  const propsKeys = Object.keys(data.props || {});\n  if (propsKeys.length) {\n    propsKeys.forEach((propKey) => {\n      const targetProps = data.props?.[propKey];\n\n      if (targetProps instanceof CProp) {\n        res.props[propKey] = targetProps;\n        return;\n      }\n      res.props[propKey] = new CProp(propKey, targetProps, {\n        parent: self,\n        materials,\n      });\n    });\n  }\n\n  if (data.children) {\n    if (Array.isArray(data.children)) {\n      res.children = data.children.map((el) => {\n        if (el instanceof CNode) {\n          return el;\n        }\n        if (isPlainObject(el)) {\n          const tmpObj = el as CNodeDataType;\n          return new CNode(tmpObj, {\n            parent: self,\n            materials,\n          });\n        } else {\n          return el as string;\n        }\n      });\n    } else {\n      if ((data.children as any) instanceof CNode) {\n        res.children = [data.children];\n      }\n      res.children = [\n        new CNode(data.children, {\n          parent: self,\n          materials: materials,\n        }),\n      ];\n    }\n  }\n  return res;\n};\n\ntype OnNodeChangeType = (params: DataModelEventType['onNodeChange']) => void;\ntype ParentType = CNode | CRootNode | CSlot | null;\n\nexport class CNode {\n  nodeType = 'NODE' as const;\n  private rawData: CNodeDataType;\n  private data: CNodeModelDataType;\n  emitter: DataModelEmitterType = DataModelEmitter;\n  parent: ParentType;\n  materialsModel: CMaterials;\n  listenerHandler: (() => void)[];\n  onChangeCbQueue: OnNodeChangeType[];\n\n  constructor(data: CNodeDataType, options?: { parent?: ParentType; materials: CMaterials | null }) {\n    this.rawData = JSON.parse(JSON.stringify(data));\n    checkNode(data);\n    const materials = options?.materials || new CMaterials([]);\n    this.parent = options?.parent || null;\n    this.materialsModel = materials;\n    this.data = parseNode(data, this, materials);\n    this.listenerHandler = [];\n    this.onChangeCbQueue = [];\n    this.registerListener();\n  }\n\n  registerListener() {\n    const onNodeChange = (params: DataModelEventType['onNodeChange']) => {\n      const { node } = params;\n      if (node === this && node.id === this.id) {\n        this.onChangeCbQueue.forEach((it) => it(params));\n      }\n    };\n    this.emitter.on('onNodeChange', onNodeChange);\n    this.listenerHandler.push(() => {\n      this.emitter.off('onNodeChange', onNodeChange);\n    });\n  }\n\n  onChange(cb: OnNodeChangeType) {\n    this.onChangeCbQueue.push(cb);\n    return () => {\n      this.onChangeCbQueue = this.onChangeCbQueue.filter((el) => el !== cb);\n    };\n  }\n\n  destroy() {\n    this.listenerHandler.forEach((it) => it());\n  }\n\n  get id() {\n    return this.data.id;\n  }\n\n  get value(): CNodeModelDataType {\n    return this.data;\n  }\n\n  clone(id?: string) {\n    const newData = {\n      ...this.export('design'),\n      id: id || getRandomStr(),\n    };\n    return new CNode(newData, {\n      materials: this.materialsModel,\n    });\n  }\n\n  updateWithPlainObj(val?: Partial<CNodeModelDataType | CNodeDataType>) {\n    const newVal: any = {\n      ...this.data,\n      ...val,\n    };\n    this.data = parseNode(newVal, this, this.materialsModel);\n    return newVal;\n  }\n\n  updateValue(val?: Partial<CNodeModelDataType | CNodeDataType>) {\n    const oldData = this.data;\n    const newVal = this.updateWithPlainObj(val);\n    this.emitter.emit('onNodeChange', {\n      value: newVal,\n      preValue: oldData,\n      node: this,\n    });\n  }\n\n  contains(nodeId: string) {\n    const res = getNode(this, nodeId);\n    return res;\n  }\n\n  get props() {\n    return this.data.props;\n  }\n\n  get material() {\n    const materialModel = this.materialsModel;\n    return materialModel?.findByComponentName(this.data.componentName);\n  }\n\n  getPlainProps() {\n    const data = this.data;\n    const props: any = {};\n    Object.keys(data.props || {}).forEach((key) => {\n      props[key] = data.props[key].export('design');\n    });\n    return props;\n  }\n\n  // 节点物料中的配置信息\n  getMaterialConfig(key: 'isContainer') {\n    if (this.data.configure[key] !== undefined) {\n      return this.data.configure.isContainer;\n    } else {\n      return this.material?.value.isContainer;\n    }\n  }\n\n  isContainer() {\n    return this.getMaterialConfig('isContainer');\n  }\n\n  export(mode: ExportType): CNodeDataType {\n    const data = this.data;\n    if (typeof data === 'string') {\n      return data;\n    }\n    const props: any = {};\n    Object.keys(data.props || {}).forEach((key) => {\n      props[key] = data.props[key].export(mode);\n    });\n    const children: any[] = data.children?.map((child) => {\n      if (child instanceof CNode) {\n        return child.export(mode);\n      } else {\n        return child;\n      }\n    });\n\n    // handle configure props setter config, clear invalidate setter config\n    const configure = data.configure || {};\n    const propsSetter = configure.propsSetter || {};\n    const newPropsSetter: typeof configure.propsSetter = {};\n    Object.keys(propsSetter).forEach((key) => {\n      const val = get(propsSetter, key, false);\n      if (val) {\n        newPropsSetter[key] = val;\n      }\n    });\n    configure.propsSetter = newPropsSetter;\n    // handle configure props setter config, clear invalidate setter config end\n    // 避免重复的物料\n    const hasExitsMeta = this.materialsModel.usedMaterials.find(\n      (el) => el.componentName === this.material?.componentName\n    );\n    if (this.material && !hasExitsMeta) {\n      this.materialsModel.usedMaterials.push(this.material);\n    }\n    let newRes: CNodeDataType = {\n      ...data,\n      configure,\n      props: props,\n      children,\n      condition: data.condition,\n      extra: this.data.extra,\n    };\n    if (mode === 'design') {\n      delete newRes.id;\n    }\n    newRes = clearSchema(newRes);\n    return newRes;\n  }\n}\n"
  },
  {
    "path": "packages/model/src/Page/RootNode/Node/prop.ts",
    "content": "import { CNode } from '.';\nimport { CRootNode } from '..';\nimport { CNodePropsTypeEnum, ExportType } from '../../../const/schema';\nimport { CMaterials } from '../../../Material';\nimport { CMaterialPropsType, MaterialPropType, PropsUIType, SpecialMaterialPropType } from '../../../types/material';\n\nimport {\n  FunctionPropType,\n  JSExpressionPropType,\n  NormalPropType,\n  CPropDataType,\n  CPropObjDataType,\n} from '../../../types/node';\nimport { isArray, isPlainObject } from '../../../util/lodash';\nimport { DataModelEmitter, DataModelEmitterType } from '../../../util/modelEmitter';\nimport { CJSSlotPropDataType, CSlot } from './slot';\n\nexport type CSpecialPropDataType = CJSSlotPropDataType | FunctionPropType | JSExpressionPropType;\n\nexport type CPropModelDataType = NormalPropType | CSpecialPropDataType | CSpecialPropDataType[];\n\nconst flatProps = (props: CMaterialPropsType): MaterialPropType[] => {\n  let allProps: MaterialPropType[] = [];\n  props.forEach((el) => {\n    const specialProp = el as SpecialMaterialPropType;\n    if (specialProp.type) {\n      if (specialProp.type === PropsUIType.SINGLE) {\n        allProps.push(specialProp.content);\n      } else if (specialProp.type === PropsUIType.GROUP) {\n        allProps = [...allProps, ...flatProps(specialProp.content)];\n      }\n    } else {\n      allProps.push(el as MaterialPropType);\n    }\n  });\n\n  return allProps;\n};\n\n// TODO: 需要递归处理每个节点，将特殊节点转换为 CProp Model\nconst handleObjProp = (data: any, parent: ParentType, materials: CMaterials): any => {\n  if (data.type) {\n    if (data.type === CNodePropsTypeEnum.SLOT) {\n      // 转换为 Slot Node\n      const newNode = new CSlot(data, { parent, materials });\n      return newNode;\n\n      // return newData;\n    }\n    return data;\n  } else if (isPlainObject(data)) {\n    const newData: CPropDataType = {};\n    Object.keys(data).forEach((key) => {\n      newData[key] = parseData(data[key], parent, materials);\n    });\n    return newData;\n  } else if (Array.isArray(data)) {\n    return data.map((el) => handleObjProp(el, parent, materials));\n  } else {\n    return data;\n  }\n};\ntype ParentType = CProp | null;\n\n// 递归处理所有的节点\nconst parseData = (data: any, parent: ParentType, materials: CMaterials) => {\n  if (isPlainObject(data)) {\n    return handleObjProp(data, parent, materials);\n  }\n  if (isArray(data)) {\n    return data.map((el) => handleObjProp(el, parent, materials));\n  }\n  return data;\n};\n\nexport class CProp {\n  nodeType = 'PROP' as const;\n  private rawData: CPropDataType;\n  parent: CNode | CRootNode | null;\n  emitter: DataModelEmitterType = DataModelEmitter;\n  private data: CPropModelDataType;\n  name: string;\n  materialsMode: CMaterials;\n  constructor(\n    name: string,\n    data: CPropDataType,\n    options: { parent: CNode | CRootNode | null; materials?: CMaterials }\n  ) {\n    const materials = options?.materials || new CMaterials([]);\n    this.materialsMode = materials;\n    this.parent = options?.parent;\n    this.rawData = data;\n    this.name = name;\n    this.data = parseData(data, this, materials);\n  }\n  // TODO:\n  isIncludeSlot() {\n    return false;\n  }\n  // TODO:\n  isIncludeExpression() {\n    return false;\n  }\n\n  get value() {\n    return this.data;\n  }\n\n  updateValue(val?: CPropDataType | CPropModelDataType) {\n    const oldData = this.data;\n    this.data = parseData(val ?? oldData, this, this.materialsMode);\n    this.emitter.emit('onPropChange', {\n      value: this.data,\n      preValue: oldData,\n      node: this,\n    });\n    // 排除 CSlot 类型\n    if (this.parent && !(this.parent instanceof CSlot)) {\n      this.emitter.emit('onNodeChange', {\n        value: this.parent.value,\n        preValue: this.parent.value,\n        node: this.parent,\n      });\n    }\n  }\n\n  get material() {\n    const parent = this.parent;\n    if (parent instanceof CNode) {\n      const parentMaterial = parent.material;\n      const allProps = flatProps(parentMaterial?.value.props || []);\n      const target = allProps.find((el) => el.name === this.name);\n      return target;\n    } else {\n      return null;\n    }\n  }\n\n  export(mode: ExportType): any {\n    const data = this.data;\n    const handleSingleProps = (propVal: any) => {\n      if (propVal instanceof CProp) {\n        return propVal.export(mode);\n      }\n      if (propVal instanceof CSlot) {\n        return propVal.export(mode);\n      }\n\n      if (propVal instanceof CNode) {\n        return propVal.export(mode);\n      }\n\n      if (isArray(propVal)) {\n        const newList: any[] = propVal.map((el) => {\n          return handleSingleProps(el);\n        });\n        return newList;\n      }\n\n      if (isPlainObject(propVal)) {\n        const newObj: Record<string, any> = {};\n\n        Object.keys(propVal || {}).forEach((key) => {\n          newObj[key] = handleSingleProps(propVal[key]);\n        });\n        return newObj;\n      }\n\n      return propVal;\n    };\n    return handleSingleProps(data);\n  }\n}\n\nexport const transformObjToPropsModelObj = (props: CPropObjDataType, parent: CNode | null = null) => {\n  const newProps: Record<string, CPropModelDataType> = {};\n  const propsKeys = Object.keys(props || {});\n  if (propsKeys.length) {\n    propsKeys.forEach((propKey) => {\n      newProps[propKey] = new CProp(propKey, props[propKey], {\n        parent: parent,\n      });\n    });\n  }\n  return newProps;\n};\n"
  },
  {
    "path": "packages/model/src/Page/RootNode/Node/slot.ts",
    "content": "import { CNode } from '.';\nimport { ExportType } from '../../../const/schema';\nimport { CMaterials } from '../../../Material';\n\nimport { RenderPropType } from '../../../types/node';\nimport { getRandomStr } from '../../../util';\nimport { isArray, isPlainObject } from '../../../util/lodash';\nimport { DataModelEmitter, DataModelEmitterType } from '../../../util/modelEmitter';\nimport { CProp } from './prop';\n\nexport type CJSSlotPropDataType = Omit<RenderPropType, 'value'> & {\n  value: CNode[];\n};\ntype ParentType = CSlot | null;\n\n// 递归处理所有的节点\nconst parseData = (data: any, parent?: ParentType) => {\n  const newData: CJSSlotPropDataType = {\n    ...data,\n    value: [],\n  };\n  const nodeValue = data.value;\n  let material = new CMaterials([]);\n  if (parent) {\n    material = parent.materialsMode || new CMaterials([]);\n  }\n  if (nodeValue) {\n    if (isArray(nodeValue)) {\n      newData.value = nodeValue.map(\n        (el) =>\n          new CNode(el, {\n            parent,\n            materials: material,\n          })\n      );\n    } else if (isPlainObject(nodeValue)) {\n      newData.value.push(\n        new CNode(nodeValue, {\n          parent: parent,\n          materials: material,\n        })\n      );\n    }\n  }\n  return newData;\n};\n\nexport class CSlot {\n  nodeType = 'SLOT' as const;\n  private rawData: RenderPropType;\n  parent: CProp | null;\n  emitter: DataModelEmitterType = DataModelEmitter;\n  private data: CJSSlotPropDataType;\n  id: string;\n  materialsMode: CMaterials;\n  constructor(data: RenderPropType, options?: { parent: CProp | null; materials: CMaterials }) {\n    this.parent = options?.parent || null;\n    this.rawData = data;\n    const materials = options?.materials || new CMaterials([]);\n    this.materialsMode = materials;\n    this.id = getRandomStr();\n    this.data = parseData(data, this);\n  }\n\n  get value() {\n    return this.data;\n  }\n\n  export(mode: ExportType) {\n    const data = this.data;\n    const handleSingleProps = (propVal: any) => {\n      if (propVal instanceof CNode) {\n        return propVal.export(mode);\n      }\n      if (isPlainObject(propVal)) {\n        const newObj: Record<string, any> = {};\n\n        Object.keys(propVal || {}).forEach((key) => {\n          newObj[key] = handleSingleProps(propVal[key]);\n        });\n        return newObj;\n      }\n\n      if (isArray(propVal)) {\n        const newList: any[] = propVal.map((el) => {\n          return handleSingleProps(el);\n        });\n        return newList;\n      }\n      if (mode === 'design') {\n        delete propVal.id;\n      }\n      return propVal;\n    };\n    return handleSingleProps(data);\n  }\n}\n"
  },
  {
    "path": "packages/model/src/Page/RootNode/index.ts",
    "content": "import { merge, omit } from 'lodash-es';\nimport { CPage } from '..';\nimport { ExportType, ExportTypeEnum } from '../../const/schema';\nimport { CMaterials } from '../../Material';\nimport { CRootNodeDataType, CRootNodeDataTypeDescribe, InnerComponentNameEnum } from '../../types/rootNode';\nimport { clearSchema, getNode, getRandomStr } from '../../util';\nimport { checkComplexData } from '../../util/dataCheck';\nimport { isArray, isPlainObject } from '../../util/lodash';\nimport { DataModelEmitter, DataModelEmitterType, DataModelEventType } from '../../util/modelEmitter';\nimport { CNode } from './Node/index';\nimport { CProp } from './Node/prop';\n\nexport type CRootNodeModelDataType = Omit<CRootNodeDataType, 'children'> & {\n  id: string;\n  children: CNode[];\n  props: Record<string, CProp>;\n  configure: Required<CRootNodeDataType>['configure'];\n};\n\nexport const checkRootNode = (data: any): CRootNodeDataType => {\n  checkComplexData({\n    data: data,\n    dataStruct: CRootNodeDataTypeDescribe,\n    throwError: false,\n  });\n  return data;\n};\n\nexport const parseSchema = (\n  data: CRootNodeDataType | CRootNodeModelDataType,\n  parent: CRootNode,\n  materials: CMaterials\n): CRootNodeModelDataType => {\n  const res: CRootNodeModelDataType = {\n    ...data,\n    id: getRandomStr(),\n    props: {} as any,\n    componentName: InnerComponentNameEnum.ROOT_CONTAINER,\n    children: [],\n    methods: data.methods || [],\n    configure: merge(data.configure || {}, {\n      propsSetter: {},\n      advanceSetter: {},\n    }),\n    /** 需要注入给特定组件的运行时引擎的内置变量 */\n    injectEnvList: data.injectEnvList,\n  };\n  let child: any = [];\n  if (isArray(data.children)) {\n    child = data.children.map((el: any) => {\n      if (el instanceof CNode) {\n        return el;\n      }\n      if (isPlainObject(el)) {\n        return new CNode(el, { parent: parent, materials });\n      } else {\n        return el;\n      }\n    });\n  } else {\n    if ((data.children as unknown) instanceof CNode) {\n      child.push(data.children);\n    } else {\n      if (data.children && isPlainObject(data.children)) {\n        child.push(new CNode(data.children, { parent: parent, materials }));\n      }\n    }\n  }\n\n  const propsKeys = Object.keys(data.props || {});\n\n  if (propsKeys.length) {\n    propsKeys.forEach((propKey) => {\n      const targetProps = data.props?.[propKey];\n      if (targetProps instanceof CProp) {\n        res.props[propKey] = targetProps;\n      } else {\n        res.props[propKey] = new CProp(propKey, targetProps || '', {\n          parent: parent,\n          materials,\n        });\n      }\n    });\n  }\n  res.children = child;\n\n  return res;\n};\n\ntype OnNodeChangeType = (params: DataModelEventType['onNodeChange']) => void;\n\nexport class CRootNode {\n  private rawData: CRootNodeDataType;\n  private data: CRootNodeModelDataType;\n  nodeType = InnerComponentNameEnum.ROOT_CONTAINER;\n  emitter: DataModelEmitterType = DataModelEmitter;\n  materialsModel: CMaterials;\n  listenerHandler: (() => void)[];\n  onChangeCbQueue: OnNodeChangeType[];\n  parent: CPage | null;\n\n  constructor(data: any, { parent, materials }: { parent: CPage | null; materials: CMaterials }) {\n    this.materialsModel = materials;\n    this.rawData = JSON.parse(JSON.stringify(data));\n    this.data = parseSchema(data, this, materials);\n    this.listenerHandler = [];\n    this.onChangeCbQueue = [];\n    this.registerListener();\n    this.parent = parent;\n  }\n\n  registerListener() {\n    const onNodeChange = (params: DataModelEventType['onNodeChange']) => {\n      const { node } = params;\n      if (node === this && node.id === this.id) {\n        this.onChangeCbQueue.forEach((it) => it(params));\n      }\n    };\n    this.emitter.on('onNodeChange', onNodeChange);\n    this.listenerHandler.push(() => {\n      this.emitter.off('onNodeChange', onNodeChange);\n    });\n  }\n\n  onChange(cb: OnNodeChangeType) {\n    this.onChangeCbQueue.push(cb);\n    return () => {\n      this.onChangeCbQueue = this.onChangeCbQueue.filter((el) => el !== cb);\n    };\n  }\n\n  get id() {\n    return this.data.id;\n  }\n\n  get value() {\n    return this.data;\n  }\n\n  get props() {\n    return this.data.props;\n  }\n\n  get material() {\n    const materialModel = this.materialsModel;\n    return materialModel?.findByComponentName(this.data.componentName);\n  }\n\n  updateWithPlainObj(val?: Partial<CRootNodeModelDataType | CRootNodeDataType>) {\n    const newVal: any = {\n      ...this.data,\n      ...val,\n    };\n    this.data = parseSchema(newVal, this, this.materialsModel);\n    return newVal;\n  }\n\n  updateValue(val?: Partial<CRootNodeModelDataType | CRootNodeDataType>) {\n    const oldData = this.data;\n    this.updateWithPlainObj(val);\n    this.emitter.emit('onNodeChange', {\n      value: this.data,\n      preValue: oldData,\n      node: this,\n    });\n  }\n\n  contains(nodeId: string) {\n    const res = getNode(this, nodeId);\n    return res;\n  }\n\n  /**\n   * 从物料 和 node config 中获取合并后的属性，node 中的配置优先级更高\n   * @param key 目前只支持 isContainer 属性\n   * @returns\n   */\n  getMaterialConfig(key: 'isContainer') {\n    if (this.data.configure[key] !== undefined) {\n      return this.data.configure.isContainer;\n    } else {\n      return this.material?.value.isContainer;\n    }\n  }\n\n  isContainer() {\n    return this.getMaterialConfig('isContainer');\n  }\n\n  export(mode: ExportType = ExportTypeEnum.SAVE): CRootNodeDataType {\n    const data = this.data;\n    const props: any = {};\n    Object.keys(data.props || {}).forEach((key) => {\n      props[key] = data.props[key].export(mode);\n    });\n    const children: any[] =\n      data.children?.map((child) => {\n        return child?.export?.(mode);\n      }) || [];\n\n    const tempData: CRootNodeDataType = {\n      ...data,\n      props: props,\n      children: children.filter((el) => el),\n      condition: data.condition,\n      extra: this.data.extra,\n    };\n    let finalRes: any = omit(tempData, ['id']);\n    finalRes = clearSchema(finalRes);\n\n    return finalRes;\n  }\n\n  getPlainProps() {\n    const data = this.data;\n    const props: any = {};\n    Object.keys(data.props || {}).forEach((key) => {\n      props[key] = data.props[key].export('design');\n    });\n    return props;\n  }\n\n  destroy() {\n    this.listenerHandler.forEach((it) => it());\n  }\n\n  clone(id?: string) {\n    const newData = {\n      ...this.export('design'),\n      id: id || getRandomStr(),\n    };\n    return new CRootNode(newData, {\n      materials: this.materialsModel,\n      parent: null,\n    });\n  }\n}\n"
  },
  {
    "path": "packages/model/src/Page/index.ts",
    "content": "import { checkComplexData } from '../util/dataCheck';\nimport { ComponentMetaType, CPageDataType, CPageDataTypeDescribe } from '../types/page';\nimport { DataModelEmitter, DataModelEmitterType } from '../util/modelEmitter';\nimport { CRootNode } from './RootNode';\nimport { ExportType, ExportTypeEnum } from '../const/schema';\nimport { CMaterials } from '../Material';\nimport { CNode } from './RootNode/Node';\nimport { CNodeDataType } from '../types/node';\nimport { cloneDeep, isPlainObject, omit, unionBy } from 'lodash-es';\nimport { CProp } from './RootNode/Node/prop';\nimport { CSlot } from './RootNode/Node/slot';\nimport { clearSchema, getNode, getRandomStr } from '../util';\nimport { InnerComponentNameEnum } from '../types/rootNode';\nimport { AssetPackage } from '../types/base';\nimport { CMaterialType } from '../types/material';\n\nexport const checkPage = (data: any): CPageDataType => {\n  checkComplexData({\n    data: data,\n    dataStruct: CPageDataTypeDescribe,\n    throwError: false,\n  });\n\n  return data;\n};\n\nexport const parsePage = (data: CPageDataType, parent: CPage, materials: CMaterials) => {\n  return {\n    ...data,\n    componentsTree: new CRootNode(data.componentsTree, {\n      parent: parent,\n      materials,\n    }),\n  };\n};\n\nexport type CPpageDataModelType = Omit<CPageDataType, 'componentsTree'> & {\n  componentsTree: CRootNode;\n};\n\nexport type PosObj = {\n  type: 'CHILD';\n  index: number;\n  pos: 'BEFORE' | 'AFTER';\n};\n\nexport type InsertNodePosType = 'BEFORE' | 'AFTER' | 'CHILD_START' | 'CHILD_END' | PosObj;\n\nexport class CPage {\n  nodeType = 'PAGE' as const;\n  rawData: CPageDataType;\n  emitter: DataModelEmitterType = DataModelEmitter;\n  data: CPpageDataModelType;\n  parent: null | undefined;\n  materialsModel: CMaterials;\n  assetPackagesList: AssetPackage[];\n\n  constructor(\n    data: CPageDataType,\n    options?: {\n      materials?: CMaterialType[];\n      assetPackagesList?: AssetPackage[];\n    }\n  ) {\n    checkPage(data);\n    this.rawData = JSON.parse(JSON.stringify(data));\n    this.materialsModel = new CMaterials(options?.materials || []);\n    this.data = parsePage(data, this, this.materialsModel);\n    this.assetPackagesList = [...(options?.assetPackagesList || []), ...(this.data.assets || [])];\n  }\n\n  updatePage(data: CPageDataType) {\n    const oldData = this.data;\n    this.rawData = JSON.parse(JSON.stringify(data));\n    this.data = parsePage(data, this, this.materialsModel);\n    this.emitter.emit('onPageChange', {\n      value: this.data,\n      preValue: oldData,\n      node: this,\n    });\n  }\n\n  reloadPage(data?: CPageDataType) {\n    const oldData = this.data;\n    if (data) {\n      this.rawData = JSON.parse(JSON.stringify(data));\n      this.data = parsePage(data, this, this.materialsModel);\n    }\n    this.emitter.emit('onReloadPage', {\n      value: this.data,\n      preValue: oldData,\n      node: this,\n    });\n  }\n\n  get value() {\n    return this.data;\n  }\n\n  // moveNode(from, to, pos) {}\n  getNode(id?: string) {\n    if (!id) return undefined;\n    const nodeTree = this.data.componentsTree;\n    return getNode(nodeTree, id);\n  }\n\n  addNode(newNode: CNode, targetNode: CNode | CRootNode, pos: InsertNodePosType = 'AFTER') {\n    if (pos === 'AFTER' || pos === 'BEFORE') {\n      const parentNode = targetNode.parent;\n      // 说明是容器节点, 只能插入 child\n      if (parentNode === null && targetNode instanceof CRootNode) {\n        console.warn('Not found parent node');\n        return false;\n      }\n\n      if (parentNode instanceof CProp) {\n        console.warn('CProp can not add node');\n        return false;\n      }\n      // TODO:\n      if (parentNode instanceof CSlot) {\n        const parentList = parentNode.value.value;\n        // find it on children;\n        const targetIndex = parentList.findIndex((el) => el === targetNode) ?? -1;\n        if (targetIndex >= 0) {\n          if (pos === 'BEFORE') {\n            parentList.splice(targetIndex, 0, newNode);\n          } else {\n            parentList.splice(targetIndex + 1, 0, newNode);\n          }\n          newNode.parent = parentNode;\n          parentNode.parent?.updateValue();\n          return true;\n        }\n        return false;\n      }\n      // TODO:\n      if (parentNode instanceof CPage) {\n        return false;\n      }\n      // find it on children;\n      const targetIndex = parentNode?.value.children.findIndex((el) => el === targetNode) ?? -1;\n      if (targetIndex >= 0) {\n        if (pos === 'BEFORE') {\n          parentNode?.value.children.splice(targetIndex, 0, newNode);\n        } else {\n          parentNode?.value.children.splice(targetIndex + 1, 0, newNode);\n        }\n        newNode.parent = parentNode;\n        parentNode?.updateValue();\n        return true;\n      }\n      console.warn('Not found target node');\n      return false;\n    }\n\n    if (pos === 'CHILD_START') {\n      targetNode.value.children.unshift(newNode);\n      newNode.parent = targetNode;\n      targetNode.updateValue();\n      return true;\n    }\n\n    if (pos === 'CHILD_END') {\n      targetNode.value.children.push(newNode);\n      newNode.parent = targetNode;\n      targetNode.updateValue();\n      return true;\n    }\n\n    if (isPlainObject(pos)) {\n      const posObj = pos as PosObj;\n      if (posObj.type === 'CHILD') {\n        const subPos = posObj.pos;\n        const index = posObj.index || 0;\n        if (subPos === 'BEFORE') {\n          targetNode?.value.children.splice(index, 0, newNode);\n        } else {\n          targetNode?.value.children.splice(index + 1, 0, newNode);\n        }\n        newNode.parent = targetNode;\n        targetNode.updateValue();\n        return true;\n      } else {\n        console.warn('Can not parse pos obj');\n      }\n    }\n    return false;\n  }\n\n  createNode(nodeData: CNodeDataType) {\n    delete nodeData.id;\n    const newNode = new CNode(nodeData, {\n      parent: null,\n      materials: this.materialsModel,\n    });\n    return newNode;\n  }\n\n  addNodeById(newNode: CNode, targetNodeId: string, pos: InsertNodePosType = 'AFTER') {\n    const targetNode = this.getNode(targetNodeId);\n    if (targetNode) {\n      return this.addNode(newNode, targetNode, pos);\n    } else {\n      console.warn(`Not find a node by ${targetNodeId}, pls check it`);\n      return false;\n    }\n  }\n\n  copyNode(node: CNode) {\n    const newNodeData = node.export('design');\n    newNodeData.id = getRandomStr();\n    const newNode = new CNode(newNodeData, {\n      parent: node.parent,\n      materials: this.materialsModel,\n    });\n    this.addNode(newNode, node, 'AFTER');\n    return newNode;\n  }\n\n  copyNodeById(nodeId: string) {\n    const node = this.getNode(nodeId);\n    if (node && node instanceof CNode) {\n      return this.copyNode(node);\n    } else {\n      return false;\n    }\n  }\n\n  moveNode(from: CNode, to: CNode, pos: InsertNodePosType) {\n    this.deleteNode(from);\n    let parent: any = to;\n    const tempTypeList: InsertNodePosType[] = ['AFTER', 'BEFORE'];\n    if (tempTypeList.includes(pos)) {\n      parent = to.parent;\n    }\n    from.parent = parent;\n\n    return this.addNode(from, to, pos);\n  }\n\n  moveNodeById(fromId: string, toId: string, pos: InsertNodePosType) {\n    const from = this.getNode(fromId);\n    const to = this.getNode(toId);\n    if (from && to && from instanceof CNode && to instanceof CNode) {\n      return this.moveNode(from, to, pos);\n    }\n\n    return false;\n  }\n\n  // replaceNode(targetNode, node) {}\n\n  deleteNode(node: CNode | CRootNode) {\n    const parent = node.parent;\n    if (!parent) {\n      throw new Error('parent node is null or undefined, pls check it');\n    }\n\n    if (parent instanceof CSlot) {\n      const childList = parent.value.value;\n      const targetIndex = childList.findIndex((el) => el === node);\n      const deleteNode = childList[targetIndex];\n\n      childList.splice(targetIndex, 1);\n      parent.parent?.updateValue();\n\n      return deleteNode;\n    }\n\n    if (parent instanceof CNode || parent instanceof CRootNode) {\n      const childList = parent.value.children;\n      const targetIndex = childList.findIndex((el) => el === node);\n      const deleteNode = childList[targetIndex];\n\n      childList.splice(targetIndex, 1);\n      parent.updateValue();\n      return deleteNode;\n    }\n  }\n\n  deleteNodeById(nodeId: string) {\n    const node = this.getNode(nodeId);\n    if (node) {\n      return this.deleteNode(node);\n    }\n  }\n\n  export(mode: ExportType = ExportTypeEnum.SAVE): CPageDataType {\n    const componentsTree = this.data.componentsTree.export(mode);\n\n    const assetPackagesList = this.assetPackagesList;\n\n    const finalAssets: AssetPackage[] = [];\n    const componentsMetaList: ComponentMetaType[] = this.materialsModel.usedMaterials.map((it) => {\n      const asset = assetPackagesList.find((el) => {\n        return el.package === it.value.npm?.package;\n      });\n      if (asset) {\n        finalAssets.push(asset);\n      }\n      return {\n        componentName: it.componentName,\n        ...cloneDeep(it.value.npm || {}),\n      } as ComponentMetaType;\n    });\n    // 剔除不合法的 meta\n    const finalComponentsMetaList = componentsMetaList.filter((el) => {\n      if (el.componentName && el.package && el.version) {\n        return true;\n      }\n      console.warn(`${JSON.stringify(el, null, 2)} not a valid material info`);\n      return false;\n    });\n    this.materialsModel.usedMaterials = [];\n    let res: CPageDataType = {\n      ...this.data,\n      componentsTree: clearSchema(componentsTree),\n      componentsMeta: finalComponentsMetaList,\n      thirdLibs: this.data.thirdLibs,\n      assets: [],\n      extra: this.data.extra,\n    };\n\n    this.data.thirdLibs?.forEach((thirdEl) => {\n      const asset = assetPackagesList.find((el) => {\n        return thirdEl.package === el.package;\n      });\n      if (asset) {\n        finalAssets.push(asset);\n      }\n    });\n\n    res.assets = unionBy(finalAssets, (el) => el.package);\n    res = omit(res, ['id']) as any;\n    return JSON.parse(JSON.stringify(res));\n  }\n\n  getRootNode() {\n    return this.data.componentsTree;\n  }\n}\n\nexport const EmptyPage: CPageDataType = {\n  version: '1.0.0',\n  name: 'EmptyPage',\n  componentsMeta: [],\n  componentsTree: {\n    componentName: InnerComponentNameEnum.ROOT_CONTAINER,\n    props: {},\n    children: [],\n  },\n};\n"
  },
  {
    "path": "packages/model/src/build-script-env.d.ts",
    "content": "/// <reference types=\"@chamn/build-script/client\" />\n"
  },
  {
    "path": "packages/model/src/const/eventList.ts",
    "content": "import { CMaterialEventType } from '../types/material';\n\nexport const DEFAULT_EVENT_LIST: CMaterialEventType[] = [\n  {\n    event: 'onClick',\n  },\n  {\n    event: 'onDoubleClick',\n  },\n  {\n    event: 'onMouseOver',\n  },\n  {\n    event: 'onMouseDown',\n  },\n  {\n    event: 'onMouseUp',\n  },\n  {\n    event: 'onMouseMove',\n  },\n  {\n    event: 'onMouseIn',\n  },\n  {\n    event: 'onMouseOut',\n  },\n  {\n    event: 'onMouseLeave',\n  },\n];\n"
  },
  {
    "path": "packages/model/src/const/schema.ts",
    "content": "export enum CNodePropsTypeEnum {\n  SLOT = 'SLOT',\n  FUNCTION = 'FUNCTION',\n  EXPRESSION = 'EXPRESSION',\n  ACTION = 'ACTION',\n  EVENT = 'EVENT',\n}\n\nexport enum ExportTypeEnum {\n  DESIGN = 'design',\n  SAVE = 'save',\n}\n\nexport type ExportType = ExportTypeEnum | `${ExportTypeEnum}`;\n\nexport enum SlotRenderType {\n  FUNC = 'FUNC',\n  COMP = 'COMP',\n}\n"
  },
  {
    "path": "packages/model/src/index.ts",
    "content": "export * from './Material';\nexport * from './Page';\nexport * from './Page/RootNode';\nexport * from './Page/RootNode/Node/index';\nexport * from './Page/RootNode/Node/prop';\nexport * from './Page/RootNode/Node/slot';\nexport * from './const/schema';\n\nexport * from './types/base';\nexport * from './types/material';\nexport * from './types/node';\nexport * from './types/rootNode';\nexport * from './types/page';\n\nexport * from './util/index';\nexport * from './const/eventList';\n"
  },
  {
    "path": "packages/model/src/types/base.ts",
    "content": "import { array, boolean, object, optional, string } from 'superstruct';\nimport { htmlTagNames } from 'html-tag-names';\n\nexport const HTMl_TAGS = htmlTagNames;\n\nexport enum SetterTypeEnum {\n  STRING_SETTER = 'StringSetter',\n  BOOLEAN_SETTER = 'BooleanSetter',\n  JSON_SETTER = 'JSONSetter',\n  SELECT_SETTER = 'SelectSetter',\n  NUMBER_SETTER = 'NumberSetter',\n  EXPRESSION_SETTER = 'ExpressionSetter',\n  FUNCTION_SETTER = 'FunctionSetter',\n  COMPONENT_SETTER = 'ComponentSetter',\n  TEXT_AREA_SETTER = 'TextAreaSetter',\n  COLOR_SETTER = 'ColorSetter',\n  ANTD_COLOR_SETTER = 'AntDColorSetter',\n  RADIO_CROUP_SETTER = 'RadioGroupSetter',\n  ACTION_FLOW_SETTER = 'ActionFlowSetter',\n  EMPTY_VALUE_SETTER = 'EmptyValueSetter',\n}\n\nexport type SetterBasicType = string;\n\nexport enum ComplexSetterTypeEnum {\n  SHAPE_SETTER = 'ShapeSetter',\n  ARRAY_SETTER = 'ArraySetter',\n}\n\nexport type AssetItem = {\n  id?: string;\n  type?: 'CSS' | 'JS';\n  src: string;\n};\n\nexport type AssetPackage = {\n  id?: string;\n  package: string;\n  // window.[globalName]\n  globalName: string;\n  resources: AssetItem[];\n};\n\nexport type LibMetaType = {\n  // unique\n  package: string;\n  name: string;\n  version: string;\n  exportName?: string;\n  destructuring?: boolean;\n  // some library need to import css file\n  cssPaths?: string[];\n  subName?: string;\n  // 库的特殊路径\n  main?: string;\n};\n\nexport const LibMetaTypeDescribe = object({\n  package: string(),\n  version: string(),\n  name: string(),\n  exportName: optional(string()),\n  destructuring: optional(boolean()),\n  subName: optional(string()),\n  main: optional(string()),\n  cssPaths: optional(array(string())),\n});\n\nexport const ThirdLibTypeDescribe = array(LibMetaTypeDescribe);\n\nexport type MaterialAssetPackage = {\n  name: string;\n  material: AssetPackage;\n  component: AssetPackage;\n  version?: string;\n};\n\nexport type CSSValue = {\n  /**  'normal' | 'hover' | 'active' | 'focus' etc */\n  state: string;\n  media?: {\n    type: 'max-width';\n    value: string;\n    text?: string;\n  }[];\n  // css 样式字符串\n  text?: string;\n};\n\nexport type CSSType = {\n  class?: string;\n  value: CSSValue[];\n};\n\n/** 基础的组件列表 */\nexport const BaseComponentTagList = [\n  'CBlock',\n  'CContainer',\n  'CImage',\n  'CCanvas',\n  'CVideo',\n  'CAudio',\n  'CText',\n  'CNativeTag',\n];\n\nexport type DropPosType = {\n  direction: 'vertical' | 'horizontal';\n  pos: 'before' | 'after' | 'current';\n};\n\nexport type DesignerInjectProps = {\n  $SET_DOM?: (dom: HTMLElement) => void;\n};\n\n/** 存储开发过程中的中间态配置，不影响渲染 */\nexport const DEV_CONFIG_KEY = '__DEV_CONFIG__';\n"
  },
  {
    "path": "packages/model/src/types/material.ts",
    "content": "import { isPlainObject } from 'lodash-es';\nimport React, { ReactInstance } from 'react';\nimport {\n  object,\n  string,\n  optional,\n  boolean,\n  enums,\n  literal,\n  any,\n  array,\n  func,\n  union,\n  number,\n  omit,\n  record,\n  dynamic,\n  assign,\n} from 'superstruct';\nimport {\n  ComplexSetterTypeEnum,\n  DropPosType,\n  LibMetaType,\n  LibMetaTypeDescribe,\n  SetterBasicType,\n  SetterTypeEnum,\n} from './base';\nimport { CNodeDataStructDescribe, CNodeDataType } from './node';\nimport type { CNode } from '../Page/RootNode/Node';\nimport type { CRootNode } from '../Page/RootNode';\n\nexport enum BaseDataType {\n  STRING = 'string',\n  NUMBER = 'number',\n  BOOLEAN = 'boolean',\n  OBJECT = 'object',\n  ARRAY = 'array',\n}\n\nexport enum AdvanceDataType {\n  SHAPE = 'shape',\n  ENUMS = 'enums',\n  UNION = 'union',\n}\n\nexport enum SpecialDataType {\n  COMPONENT = 'component',\n  EXPRESSION = 'expression',\n  FUNCTION = 'function',\n}\n\nexport type MTitle =\n  | string\n  | {\n      label: string;\n      tip?: string;\n    };\n\nexport const getMTitle = (title: MTitle) => {\n  if (isPlainObject(title)) {\n    return (title as any).label;\n  } else {\n    return title;\n  }\n};\n\nexport const getMTitleTip = (title: MTitle) => {\n  if (isPlainObject(title)) {\n    return (title as any).tip;\n  } else {\n    return '';\n  }\n};\n\nexport const MTitleDescribe = union([\n  string(),\n  object({\n    label: string(),\n    tip: optional(string()),\n  }),\n]);\n\nexport type ShapeDataType = {\n  type: AdvanceDataType.SHAPE | `${AdvanceDataType.SHAPE}`;\n  value: {\n    name: string;\n    title: MTitle;\n    valueType: PropsValueType;\n  }[];\n};\n\nexport const ShapeDataTypeDescribe = object({\n  type: literal(AdvanceDataType.SHAPE),\n  value: array(\n    object({\n      name: string(),\n      title: MTitleDescribe,\n      valueType: dynamic(() => {\n        return PropsValueTypeDescribe;\n      }),\n    })\n  ),\n});\n\nexport type EnumDataType = {\n  type: AdvanceDataType.ENUMS | `${AdvanceDataType.ENUMS}`;\n  value: string[];\n};\n\nexport const EnumDataTypeDescribe = object({\n  type: literal(AdvanceDataType.ENUMS),\n  value: array(string()),\n});\n\nexport type ArrayDataType = {\n  type: BaseDataType.ARRAY | `${BaseDataType.ARRAY}`;\n  value: PropsValueType;\n};\n\nexport const ArrayDataTypeDescribe = object({\n  type: literal(BaseDataType.ARRAY),\n  value: dynamic(() => {\n    return PropsValueTypeDescribe;\n  }),\n});\n\nexport type UnionDataType = {\n  type: AdvanceDataType.UNION | `${AdvanceDataType.UNION}`;\n  value: PropsValueType[];\n};\n\nexport const UnionDataTypeDescribe = object({\n  type: literal(BaseDataType.ARRAY),\n  value: dynamic(() => {\n    return array(PropsValueTypeDescribe);\n  }),\n});\n\nexport type PropsValueType =\n  | BaseDataType\n  | `${BaseDataType}`\n  | SpecialDataType\n  | `${SpecialDataType}`\n  | ShapeDataType\n  | EnumDataType\n  | ArrayDataType\n  | UnionDataType;\n\nexport const PropsValueTypeDescribe: any = union([\n  enums([BaseDataType.ARRAY, BaseDataType.BOOLEAN, BaseDataType.NUMBER, BaseDataType.OBJECT, BaseDataType.STRING]),\n  enums([SpecialDataType.COMPONENT, SpecialDataType.EXPRESSION, SpecialDataType.FUNCTION]),\n  ShapeDataTypeDescribe,\n  EnumDataTypeDescribe,\n  UnionDataTypeDescribe,\n]);\n\nexport type SetterType<T extends SetterBasicType = ''> =\n  | SetterTypeEnum\n  | `${SetterTypeEnum}`\n  | ComplexSetterTypeEnum\n  | `${ComplexSetterTypeEnum}`\n  | SetterObjType<T>\n  | T\n  | `${T}`;\n\nexport type BasicSetterObjType<T extends SetterBasicType = ''> = {\n  componentName: SetterTypeEnum | `${SetterTypeEnum}` | T | `${T}`;\n  props?: Record<any, any>;\n  /** 被设置属性的初始值 */\n  initialValue?: any;\n  /** props reference CSetterProps<T> from engine */\n  component?: (props: any) => React.ReactNode;\n  /** 是否隐藏前面的 label */\n  hiddenLabel?: boolean;\n  labelWidth?: string;\n  labelAlign?: 'start' | 'center' | 'end';\n};\n\nexport type ShapeSetterObjType<T extends SetterBasicType = ''> = {\n  componentName: ComplexSetterTypeEnum.SHAPE_SETTER | `${ComplexSetterTypeEnum.SHAPE_SETTER}` | T | `${T}`;\n  props?: {\n    elements: MaterialPropType<T>[];\n    /** 是否可以收缩，默认： true  */\n    collapse?:\n      | boolean\n      | {\n          open?: boolean;\n        };\n  } & {};\n  initialValue: any;\n  /** props reference CSetterProps<T> from engine */\n  component?: (props: any) => React.ReactNode;\n  hiddenLabel?: boolean;\n  labelWidth?: string;\n  labelAlign?: 'start' | 'center' | 'end';\n};\n\nexport type ArraySetterObjType<T extends SetterBasicType = ''> = {\n  componentName: ComplexSetterTypeEnum.ARRAY_SETTER | `${ComplexSetterTypeEnum.ARRAY_SETTER}` | T | `${T}`;\n  props?: {\n    collapse?:\n      | boolean\n      | {\n          open?: boolean;\n        };\n    item: {\n      setters: SetterType<T>[];\n      initialValue: any;\n    };\n    itemLabelPrefix?: string;\n    sortLabelKey?: any;\n  };\n  initialValue: any;\n  /** props reference CSetterProps<T> from engine */\n  component?: (props: any) => React.ReactNode;\n  hiddenLabel?: boolean;\n  labelWidth?: string;\n  labelAlign?: 'start' | 'center' | 'end';\n};\n\nexport type SetterObjType<T extends SetterBasicType = ''> =\n  | BasicSetterObjType<T>\n  | ShapeSetterObjType<T>\n  | ArraySetterObjType<T>;\n\nexport const SetterTypeDescribe = union([\n  string(),\n  object({\n    componentName: string(),\n    props: optional(any()),\n    /** 用于标记当前数据的初始值，如添加一个数组元素可以使用该值填充 */\n    initialValue: optional(any()),\n    component: optional(any()),\n    hiddenLabel: optional(boolean()),\n  }),\n]);\n\nexport type MaterialPropType<CustomSetter extends SetterBasicType = ''> = {\n  name: string;\n  title: MTitle;\n  valueType: PropsValueType;\n  description?: string;\n  defaultValue?: any;\n  setters?: SetterType<CustomSetter>[];\n  condition?: (state: any) => boolean;\n};\n\nexport const MaterialPropDescribe = object({\n  name: string(),\n  title: MTitleDescribe,\n  // 描述 name 对应值的类型\n  valueType: PropsValueTypeDescribe,\n  description: optional(string()),\n  defaultValue: any(),\n  //用于产生 valueType 类型的值\n  setters: optional(array(SetterTypeDescribe)),\n  condition: optional(func()),\n});\n\nexport type ActionType = string | ((node: CNode | CRootNode, context: any) => React.ReactNode);\n\nexport const ActionTypeDescribe = union([string(), func()]);\n\nexport enum PropsUIType {\n  SINGLE = 'single',\n  GROUP = 'group',\n}\n\nexport type SpecialMaterialPropType<CustomSetter extends SetterBasicType = ''> =\n  | {\n      title: MTitle;\n      type: PropsUIType.SINGLE | `${PropsUIType.SINGLE}`;\n      content: MaterialPropType<CustomSetter>;\n    }\n  | {\n      title: MTitle;\n      type: PropsUIType.GROUP | `${PropsUIType.GROUP}`;\n      content: MaterialPropType<CustomSetter>[];\n    };\n\nexport type CMaterialPropsType<CustomSetter extends SetterBasicType = ''> = (\n  | MaterialPropType<CustomSetter>\n  | SpecialMaterialPropType<CustomSetter>\n)[];\n\nexport const isSpecialMaterialPropType = <CustomSetter extends SetterBasicType = ''>(\n  val: any\n): val is SpecialMaterialPropType<CustomSetter> => {\n  if (val.type && [PropsUIType.GROUP, PropsUIType.SINGLE].includes(val.type)) {\n    return true;\n  } else {\n    return false;\n  }\n};\n\nexport type TFunctionOrEventParameter = {\n  /** 参数名 */\n  name: string;\n  /** 参数的 ts 类型 */\n  tsType?: string;\n  description?: string;\n  example?: string;\n};\n\nexport type CMaterialEventType =\n  | string\n  | {\n      /** 对外展示的描述 */\n      name?: string;\n      /** 事件 key */\n      event: string;\n      descriptions?: string;\n      // 事件参数描述\n      params?: TFunctionOrEventParameter[];\n      // function string\n      template?: string;\n    };\nexport const CMaterialEventTypeDescribe = union([\n  string(),\n  object({\n    event: string(),\n    name: optional(string()),\n    describe: optional(string()),\n    params: optional(\n      array(\n        object({\n          name: string(),\n          description: string(),\n          example: string(),\n        })\n      )\n    ),\n    template: optional(string()),\n  }),\n]);\n\nexport type SnippetsType = {\n  id?: string;\n  title: string;\n  snapshotText?: string;\n  snapshot?: string | React.ReactNode;\n  description?: string | React.ReactNode;\n  // 组件标签用于搜索\n  tags?: string[];\n  // 分 tab 面板\n  groupName?: string;\n  // 分类\n  category?: string;\n  schema: Omit<CNodeDataType, 'id' | 'componentName'> & {\n    componentName?: string;\n  };\n};\n\nexport type SnippetsStanderType = Omit<SnippetsType, 'schema'> & {\n  schema: Omit<CNodeDataType, 'id'>;\n};\n\nexport const SnippetsTypeDescribe = object({\n  id: optional(string()),\n  title: string(),\n  snapshot: union([string(), any()]),\n  snapshotText: optional(string()),\n  description: optional(string()),\n  // 组件分类,用于搜索\n  tags: optional(array(string())),\n  // 分 tab 面板\n  groupName: optional(string()),\n  // 分类\n  category: optional(string()),\n  schema: assign(omit(CNodeDataStructDescribe, ['id']), object({ componentName: optional(string()) })),\n});\n\nexport type ContainerConfig = {\n  placeholder: string;\n  width: string;\n  height: string;\n  style?: React.CSSProperties;\n};\n\nexport type DragAndDropEventExtraData = {\n  dropType?: 'NEW_ADD' | 'NORMAL' | '';\n  dragNode?: CNode | CRootNode;\n  dragNodeUID?: string;\n  dropNode?: CNode | CRootNode;\n  dropNodeUID?: string;\n  dropPosInfo?: DropPosType;\n};\n\nexport type CustomViewRenderProps = {\n  node: CNode | CRootNode;\n  params: AdvanceCustomFuncParam;\n  componentInstance?: ReactInstance;\n  /** 一个组件可能被循环渲染，这里表示是第几个索引 **/\n  componentInstanceIndex?: number;\n};\n\nexport type AdvanceCustomFuncParam = {\n  dropNode?: CNode | CRootNode;\n  viewPortal: {\n    setView: (view: React.ReactNode) => void;\n    clearView: () => void;\n  };\n  //  当前元素在拖动中是否被锁住\n  isLock?: boolean;\n  context: any;\n  cancelDrag?: () => void;\n  /** 如果是 api 触发的，则没有事件对象 */\n  event?: any;\n  extra: DragAndDropEventExtraData;\n};\n\nexport type EventName = keyof HTMLElementEventMap;\n\nexport type AdvanceCustom = {\n  canDragNode?: (\n    node: CNode | CRootNode,\n    params: AdvanceCustomFuncParam\n  ) => Promise<\n    | boolean\n    | {\n        dragNode?: CNode | CRootNode;\n      }\n  >;\n  onDragStart?: (node: CNode | CRootNode, params: AdvanceCustomFuncParam) => Promise<boolean | void | undefined>;\n  /** 拖动中触发 */\n  onDragging?: (node: CNode | CRootNode, params: AdvanceCustomFuncParam) => Promise<boolean | void | undefined>;\n  onDragEnd?: (node: CNode | CRootNode, params: AdvanceCustomFuncParam) => void;\n  /** 当有其他 node 被放置到当前 node 的 child 时触发校验，可以控制落点的 UI 样式？ */\n  canAcceptNode?: (node: CNode | CRootNode, params: AdvanceCustomFuncParam) => Promise<boolean | void | undefined>;\n  /** 当前节点是否能被放置, 可以控制落点的 UI 样 */\n  canDropNode?: (\n    node: CNode | CRootNode,\n    params: AdvanceCustomFuncParam\n  ) => Promise<\n    | boolean\n    | void\n    | undefined\n    | {\n        /** 被拖动的节点 */\n        dragNode?: CNode | CRootNode;\n        /** node 可能被循环渲染多次，这里表示 node 的唯一 id， node.id 只能表示 node 在 schema 中唯一，不代表渲染唯一 */\n        dragNodeUID?: string;\n        /** 放置到的目标节点 */\n        dropNode?: CNode | CRootNode;\n        dropNodeUID?: string;\n        dropPosInfo?: DropPosType;\n      }\n  >;\n  onDrop?: (node: CNode | CRootNode, params: AdvanceCustomFuncParam) => Promise<boolean | void | undefined>;\n  /** 当第一次被拖入到画布时触发 */\n  onNewAdd?: (\n    node: CNode | CRootNode,\n    params: AdvanceCustomFuncParam\n  ) => Promise<\n    | boolean\n    | void\n    | undefined\n    | {\n        addNode?: CNode | CRootNode;\n        dropNode?: CNode | CRootNode;\n        dropPosInfo?: DropPosType;\n      }\n  >;\n  /** 当元素被删除时触发 */\n  onDelete?: (\n    node: CNode | CRootNode,\n    params: AdvanceCustomFuncParam\n  ) => Promise<\n    | boolean\n    | void\n    | undefined\n    | {\n        deleteNode?: CNode | CRootNode;\n      }\n  >;\n  /** 元素被选中时触发 */\n  onSelect?: (\n    node: CNode | CRootNode,\n    params: AdvanceCustomFuncParam\n  ) =>\n    | Promise<boolean | void | undefined>\n    | {\n        selectedNode?: CNode | CRootNode;\n      };\n  onCopy?: (\n    node: CNode | CRootNode,\n    params: AdvanceCustomFuncParam\n  ) => Promise<\n    | boolean\n    | void\n    | undefined\n    | {\n        copyNode?: CNode | CRootNode;\n      }\n  >;\n  toolbarViewRender?: (props: {\n    node: CNode | CRootNode;\n    context: any;\n    toolBarItems: {\n      copyItem: React.ReactElement;\n      deleteItem: React.ReactElement;\n      visibleItem: React.ReactElement;\n      nodeLayout: React.ReactElement;\n    };\n    toolBarItemList: React.ReactElement[];\n  }) => React.ReactElement;\n  selectRectViewRender?: (props: CustomViewRenderProps) => React.ReactElement;\n  hoverRectViewRender?: (props: CustomViewRenderProps) => React.ReactElement;\n  dropViewRender?: (\n    props: CustomViewRenderProps & {\n      // 是否可以放置\n      canDrop: boolean;\n      posInfo: DropPosType;\n    }\n  ) => React.ReactElement;\n  ghostViewRender?: (props: CustomViewRenderProps) => React.ReactElement;\n  // TODO: 编辑模式下会使用该函数包裹目标组件，可用于定制编辑模式下的特殊信息\n  wrapComponent?: (\n    targetComponent: (...args: any[]) => React.ReactElement,\n    options: {\n      ctx: any;\n      node: CNode | CRootNode;\n    }\n  ) => (...args: any[]) => React.ReactElement;\n  dropPlaceholder?: (props: { node: CNode | CRootNode }) => React.ReactElement;\n  /** 配置右侧面板 */\n  rightPanel?: {\n    /** 是否显示相应的面板 */\n    visual?: boolean;\n    state?: boolean;\n    advance?: boolean;\n    advanceOptions?: {\n      /** 是否展示 loop 选项 */\n      loop?: boolean;\n      /** 是否展示 render 选项, 如果为 false 会被包裹 div 并设置 display 为 none */\n      render?: boolean;\n    };\n    property?: boolean;\n    customTabs?: {\n      /** 唯一标识 */\n      key: string;\n      name: string | ((params: { node: CNode | CRootNode | null; pluginCtx: any }) => string);\n      view: (params: { node: CNode | CRootNode | null; pluginCtx: any }) => React.ReactElement;\n      show?: (options: { node: CNode | CRootNode | null; pluginCtx: any }) => boolean;\n    }[];\n  };\n  /** 是否自动获取 元素 dom, 配置 getDom  使用, 默认 为 true */\n  autoGetDom?: boolean;\n};\n\nexport type CMaterialType<PropsSetter extends string = ''> = {\n  componentName: string;\n  title: string;\n  screenshot?: string;\n  icon?: string;\n  /** 组件标签用于搜索 */\n  tags?: string[];\n  /** 分 tab 面板 */\n  groupName?: string;\n  /** 分类 */\n  category?: string;\n  /** 排序 */\n  priority?: number;\n  npm?: LibMetaType;\n  snippets: SnippetsType[];\n  props: CMaterialPropsType<PropsSetter>;\n  /** 固定的props, 不被 setter 的值覆盖, 只在编辑模式下会生效 */\n  fixedProps?: Record<string, any> | ((props: Record<string, any>) => Record<string, any>);\n  /** 可以拖入组件 */\n  isContainer?: boolean | ContainerConfig;\n  /** 选择框的根选择器 */\n  rootSelector?: string;\n  /** 是否禁止编辑器的 drag 事件，被命中的 dom 不会出发 编辑器的 */\n  disableEditorDragDom?:\n    | {\n        class?: string[];\n        id?: string[];\n      }\n    | boolean;\n  /** TODO: 组件支持的可被调用的方法， todo： 没有补充验证 类型 describe */\n  methods?: {\n    title: string;\n    // 方法名\n    name: string;\n    params?: TFunctionOrEventParameter[];\n    // 方法的 ts 类型定义\n    tsType?: string;\n    template?: string;\n  }[];\n  /**  组件可能触发的事件 */\n  events?: CMaterialEventType[];\n  /** 定制组件高级编辑行为 */\n  advanceCustom?: AdvanceCustom;\n  /** 自定义扩展配置 */\n  extra?: Record<any, any>;\n};\n\nexport type CMaterialStanderType = Omit<CMaterialType, 'snippets'> & {\n  snippets: SnippetsStanderType[];\n};\n\nexport const CMaterialTypeDescribe = object({\n  componentName: string(),\n  title: string(),\n  screenshot: optional(string()),\n  icon: optional(string()),\n  // 组件分类,用于搜索\n  tags: optional(array(string())),\n  // 分 tab 面板\n  groupName: optional(string()),\n  // 分类\n  category: optional(string()),\n  // 排序\n  priority: optional(number()),\n  npm: optional(LibMetaTypeDescribe),\n  snippets: array(SnippetsTypeDescribe),\n  props: array(\n    union([\n      MaterialPropDescribe,\n      object({\n        title: optional(MTitleDescribe),\n        type: literal(PropsUIType.SINGLE),\n        content: MaterialPropDescribe,\n      }),\n      object({\n        title: optional(MTitleDescribe),\n        type: literal(PropsUIType.GROUP),\n        content: array(MaterialPropDescribe),\n      }),\n    ])\n  ),\n  events: optional(any()),\n  methods: optional(any()),\n  fixedProps: optional(any()),\n  // 可以拖入组件\n  isContainer: optional(\n    union([\n      boolean(),\n      object({\n        placeholder: string(),\n        width: string(),\n        height: string(),\n      }),\n    ])\n  ),\n  disableEditorDragDom: optional(any()),\n  // 如果是布局组件，可以考虑将拖拽控制权转移 or 实现 resize\n  isLayout: optional(boolean()),\n  rootSelector: optional(string()),\n  // selectionToolBarView: optional(func()),\n  advanceCustom: optional(any()),\n  // 扩展配置\n  extra: optional(record(any(), any())),\n});\n"
  },
  {
    "path": "packages/model/src/types/node.ts",
    "content": "import {\n  string,\n  number,\n  boolean,\n  any,\n  union,\n  object,\n  literal,\n  optional,\n  record,\n  dynamic,\n  array,\n  define,\n  validate,\n  enums,\n} from 'superstruct';\nimport { CNodePropsTypeEnum, SlotRenderType } from '../const/schema';\nimport { isPlainObject } from '../util/lodash';\nimport { CSSType, DEV_CONFIG_KEY } from './base';\nimport type { CPage } from '../Page';\nimport type { CNode } from '../Page/RootNode/Node';\n\nexport type NormalPropType = string | boolean | number | Record<string, any> | undefined | null;\n\n// get enum value list\ntype IValue = `${SlotRenderType}`;\n\nexport type RenderPropType = {\n  type: CNodePropsTypeEnum.SLOT | `${CNodePropsTypeEnum.SLOT}`;\n  params?: string[];\n  renderType: SlotRenderType | IValue;\n  value: CNodeDataType | CNodeDataType[];\n};\n\nexport type JSExpressionPropType = {\n  type: CNodePropsTypeEnum.EXPRESSION | `${CNodePropsTypeEnum.EXPRESSION}`;\n  value: string;\n};\n\nexport type TBaseFunction = {\n  /** TODO: 编辑器使，存储函数的源码，用于编辑器使用  */\n  sourceCode?: string;\n  /** 编辑器使用 */\n  tsType?: string;\n  /** 可直接在浏览器运行的代码  */\n  value: string;\n  /** 函数名称 */\n  name?: string;\n};\n\nexport type FunctionPropType = {\n  type: CNodePropsTypeEnum.FUNCTION | `${CNodePropsTypeEnum.FUNCTION}`;\n} & TBaseFunction;\n\nexport enum LogicType {\n  JUMP_LINK = 'JUMP_LINK',\n  RUN_CODE = 'RUN_CODE',\n  REQUEST_API = 'REQUEST_API',\n  /** 调用节点方法 */\n  CALL_NODE_METHOD = 'CALL_NODE_METHOD',\n  // 赋予值操作\n  ASSIGN_VALUE = 'ASSIGN_VALUE',\n}\n\nexport type TDynamicValue = string | number | JSExpressionPropType | FunctionPropType;\n\nexport type TBaseActionNode = {\n  id: string;\n  next?: string | number;\n};\n/** 存储开发中的一些临时状态 */\nexport type TActionFlowDevConfig = {\n  pageModel: CPage;\n  currentNode: CNode;\n  defaultSetterMap: Record<\n    string,\n    {\n      name: string;\n      setter: string;\n    }\n  >;\n};\nexport type TLogicJumpLinkItem = {\n  type: LogicType.JUMP_LINK | `${LogicType.JUMP_LINK}`;\n  link: TDynamicValue;\n  [DEV_CONFIG_KEY]?: TActionFlowDevConfig;\n} & TBaseActionNode;\n\n/** 🌧️函数类型 */\nexport type TLogicRunCodeItem = {\n  /** 函数最好有返回值 */\n  type: LogicType.RUN_CODE | `${LogicType.RUN_CODE}`;\n  [DEV_CONFIG_KEY]?: TActionFlowDevConfig;\n} & TBaseFunction &\n  TBaseActionNode;\n\nexport type TLogicCallNodeMethodItem = {\n  type: LogicType.CALL_NODE_METHOD | `${LogicType.CALL_NODE_METHOD}`;\n  nodeId: string;\n  methodName: string;\n  args?: TDynamicValue[];\n  /** 返回值的变量名 */\n  returnVarName?: string;\n  [DEV_CONFIG_KEY]?: TActionFlowDevConfig;\n} & TBaseActionNode;\n\nexport type TLogicRequestAPIItem = {\n  type: LogicType.REQUEST_API | `${LogicType.REQUEST_API}`;\n  /** 直接获取具体的 API path, 完整的 host, 特殊场景使用，一般使用 apiId, 可以控制环境切换 */\n  apiPath: TDynamicValue;\n  /** 默认 get */\n  method?: 'POST' | 'GET' | 'PUT' | 'PATCH' | 'DELETE';\n  query?: Record<string, TDynamicValue>;\n  body?: Record<string, TDynamicValue>;\n  header?: Record<string, TDynamicValue>;\n  // 请求响应成功之后的执行代码， 获取到返回值，可以继续执行多个操作\n  afterSuccessResponse?: TLogicItemHandlerFlow;\n  // 请求响应失败之后的执行代码， 获取到返回值，可以继续执行多个操作\n  afterFailedResponse?: TLogicItemHandlerFlow;\n  /** 响应变量名 */\n  responseVarName?: string;\n  /** 额外的数据 */\n  extra?: Record<any, any>;\n  [DEV_CONFIG_KEY]?: TActionFlowDevConfig;\n} & TBaseActionNode;\n\nexport enum AssignValueType {\n  /** 组件内的局部变量，没有响应性， 只在当前的上下文中有效 */\n  MEMORY = 'MEMORY',\n  STATE = 'STATE',\n}\n\nexport type TAssignValueType = `${AssignValueType}`;\n\nexport type TargetValueNameObject = {\n  nodeId: string;\n  keyPath: string;\n};\n/** 赋值操作  */\nexport type TLogicAssignValueItem = {\n  type: LogicType.ASSIGN_VALUE | `${LogicType.ASSIGN_VALUE}`;\n  valueType: AssignValueType | TAssignValueType;\n  currentValue: TDynamicValue;\n  /** 如果是 STATE 类型需要 nodeId, 否则只用填 string */\n  targetValueName?: TargetValueNameObject | string;\n  [DEV_CONFIG_KEY]?: TActionFlowDevConfig;\n} & TBaseActionNode;\n\nexport type TLogicItemHandlerFlow = (\n  | TLogicJumpLinkItem\n  | TLogicRunCodeItem\n  | TLogicRequestAPIItem\n  | TLogicCallNodeMethodItem\n  | TLogicAssignValueItem\n)[];\n\nexport type TActionLogicItem = {\n  type: CNodePropsTypeEnum.ACTION | `${CNodePropsTypeEnum.ACTION}`;\n  handler: TLogicItemHandlerFlow;\n  /** 调用方传入的参数列表 */\n  params?: string[];\n};\n\nexport type SpecialProp = RenderPropType | JSExpressionPropType | FunctionPropType | TActionLogicItem;\n\nexport type CPropDataType = NormalPropType | SpecialProp | CPropObjDataType;\n\nexport type CPropObjDataType = {\n  [key: string]: CPropDataType | CPropDataType[] | Record<string, CPropDataType>;\n};\n\nconst normalObj = () =>\n  define('normalObj', (value: any) => {\n    if (!isPlainObject(value)) {\n      return false;\n    }\n    if ([CNodePropsTypeEnum.SLOT, CNodePropsTypeEnum.EXPRESSION, CNodePropsTypeEnum.FUNCTION].includes(value?.type)) {\n      return false;\n    }\n    validate(value, record(string(), PropsDataStructDescribe));\n    return true;\n  });\n\nexport const PropsDataStructDescribe: any = union([\n  string(),\n  number(),\n  boolean(),\n  object({\n    type: literal(CNodePropsTypeEnum.SLOT),\n    renderType: enums([SlotRenderType.FUNC, SlotRenderType.COMP]),\n    // if renderType is Func， params will be useful\n    params: optional(array(string())),\n    // here can't use PropsDataStructDescribe, it will  caused  \"Maximum call stack size exceeded\" error\n    value: dynamic(() => {\n      return union([CNodeDataStructDescribe, array(CNodeDataStructDescribe)]);\n    }),\n  }),\n  object({\n    type: literal(CNodePropsTypeEnum.EXPRESSION),\n    value: string(),\n  }),\n  object({\n    type: literal(CNodePropsTypeEnum.FUNCTION),\n    value: string(),\n  }),\n  object({\n    type: literal(CNodePropsTypeEnum.ACTION),\n    /** 暂时不做规则强检验 */\n    handler: any(),\n  }),\n  normalObj(),\n  array(\n    dynamic(() => {\n      return PropsDataStructDescribe;\n    })\n  ),\n]);\n\n// 开发模式使用的 key,导出为生产模式时，需要移除\nexport const DevKey = ['configure'];\n\nexport type ClassNameType = {\n  name: string;\n  status?: JSExpressionPropType;\n};\n\n/** 支持注入到运行时组件的引擎内置变量名列表 */\nexport enum ENGEnvEnum {\n  COMPONENTS = 'COMPONENTS',\n}\n\nexport type CNodeDataType<ExtraT = any> = {\n  id?: string;\n  title?: string;\n  componentName: string;\n  /** 节点类型 */\n  type?: 'dynamic' | 'normal';\n  /** 所有的 props 的 value 需要支持表达式 $$context */\n  props?: CPropObjDataType;\n  state?: Record<string, any>;\n  /** 当前节点的事件处理逻辑，会被压缩为 prop 传入 */\n  eventListener?: {\n    name: string;\n    /** 处理程序 */\n    func: TActionLogicItem;\n  }[];\n  nodeName?: string;\n  children?: (string | CNodeDataType)[];\n  /**\n   * only used in dev mode, if you are run in prod, this key will be undefined\n   *\n   * @type {Record<any, any>}\n   */\n  configure?: {\n    /** 由于一个 prop 可能会有多个设置器，这里用来存储当前使用的那个设置器 */\n    propsSetter?: Record<\n      string,\n      {\n        name: string;\n        setter: string;\n      }\n    >;\n    advanceSetter?: Record<\n      string,\n      {\n        name: string;\n        setter: string;\n      }\n    >;\n    /** 开发模式下中的临时状态存储 */\n    devState?: {\n      condition?: boolean | JSExpressionPropType;\n      props?: CPropObjDataType;\n    };\n    /** 当前节点是否时容器节点 */\n    isContainer?: boolean;\n  };\n  classNames?: ClassNameType[];\n  css?: CSSType;\n  /** css 属性有顺序 */\n  style?: {\n    property: string;\n    value: JSExpressionPropType | string;\n  }[];\n  // 组件引用的唯一id\n  refId?: string;\n  // 组件上写的自定义方法, 一般不会使用\n  methods?: FunctionPropType[];\n  loop?: {\n    open: boolean;\n    data: any[] | JSExpressionPropType;\n    forName?: string;\n    forIndex?: string;\n    key?: JSExpressionPropType | string;\n    name?: string;\n  };\n  /** 需要注入运行时组件的环境变量列表，可以在组件内部通过 props 获取 */\n  injectEnvList?: (ENGEnvEnum | `${ENGEnvEnum}`)[];\n  // 是否渲染\n  condition?: boolean | JSExpressionPropType;\n  extra?: ExtraT;\n};\n\nconst JSExpressionDescribe = object({\n  type: literal(CNodePropsTypeEnum.EXPRESSION),\n  value: string(),\n});\n\nexport const CNodeDataStructDescribe: any = object({\n  id: optional(string()),\n  title: optional(string()),\n  componentName: string(),\n  props: optional(record(string(), PropsDataStructDescribe)),\n  nodeName: optional(string()),\n  state: optional(record(string(), any())),\n  children: dynamic(() => {\n    return optional(array(union([string(), CNodeDataStructDescribe])));\n  }),\n  eventListener: optional(array(any())),\n  configure: optional(any()),\n  css: optional(any()),\n  style: optional(any()),\n  classNames: optional(array(any())),\n  refId: optional(string()),\n  extra: optional(record(any(), any())),\n  condition: optional(union([boolean(), JSExpressionDescribe])),\n  loop: optional(\n    object({\n      open: boolean(),\n      data: union([array(any()), JSExpressionDescribe]),\n      args: optional(array(string())),\n      forName: optional(string()),\n      forIndex: optional(string()),\n      key: optional(any()),\n      name: optional(string()),\n    })\n  ),\n  injectEnvList: optional(array(any())),\n  methods: optional(array(any())),\n});\n"
  },
  {
    "path": "packages/model/src/types/page.ts",
    "content": "import { any, array, assign, object, optional, string } from 'superstruct';\nimport { LibMetaType, ThirdLibTypeDescribe, LibMetaTypeDescribe, AssetPackage, CSSType } from './base';\nimport { FunctionPropType } from './node';\nimport { CRootNodeDataType, CRootNodeDataTypeDescribe, FunctionPropertyTypeDescribe } from './rootNode';\nimport type { CNode } from '../Page/RootNode/Node';\nimport type { CPage } from '../Page';\nimport type { CRootNode } from '../Page/RootNode';\n\nexport type ComponentMetaType = {\n  componentName: string;\n} & LibMetaType;\n\nexport enum RenderType {\n  PAGE = 'PAGE',\n  COMPONENT = 'COMPONENT',\n}\n\nexport type LifecycleItem = {\n  nodeId: string;\n  run: (params: { ctx: any }) => void;\n};\n\nexport type CPageDataType<T = any> = {\n  version: string;\n  name: string;\n  // TODO\n  css?: CSSType[];\n  // TODO\n  lifecycle?: {\n    beforeMount?: LifecycleItem[];\n    didMount?: LifecycleItem[];\n    beforeUnmount?: LifecycleItem[];\n  };\n  // TODO\n  /** 页面级 别 或者组件级别的外部传入的 props, 用于数据交互，比如通过平台导出源码，直接集成到 pro code 项目中使用 */\n  props?: Record<string, any>;\n  methods?: FunctionPropType[];\n  componentsMeta: ComponentMetaType[];\n  thirdLibs?: LibMetaType[];\n  componentsTree: CRootNodeDataType;\n  // runtime render need\n  assets?: AssetPackage[];\n  extra?: T;\n};\n\nexport const CPageDataTypeDescribe = object({\n  version: optional(string()),\n  name: optional(string()),\n  css: optional(string()),\n  lifecycle: optional(any()),\n  props: optional(any()),\n  methods: optional(array(FunctionPropertyTypeDescribe)),\n  componentsMeta: array(\n    assign(\n      object({\n        componentName: string(),\n      }),\n      LibMetaTypeDescribe\n    )\n  ),\n  thirdLibs: optional(ThirdLibTypeDescribe),\n  componentsTree: CRootNodeDataTypeDescribe,\n  assets: optional(array(any())),\n  extra: optional(any()),\n});\n\nexport type CPageNode = CNode | CPage | CRootNode;\n"
  },
  {
    "path": "packages/model/src/types/rootNode.ts",
    "content": "import { assign, literal, object, omit, string } from 'superstruct';\nimport { CNodePropsTypeEnum } from '../const/schema';\nimport { CNodeDataStructDescribe, CNodeDataType } from './node';\n\nexport enum InnerComponentNameEnum {\n  ROOT_CONTAINER = 'RootContainer',\n}\n\nexport type CRootNodeDataType<ExtraT = any> = CNodeDataType<ExtraT> & {\n  componentName: InnerComponentNameEnum | `${InnerComponentNameEnum}`;\n};\n\nexport const FunctionPropertyTypeDescribe = object({\n  type: literal(CNodePropsTypeEnum.FUNCTION),\n  value: string(),\n});\n\nexport const CRootNodeDataTypeDescribe = assign(\n  omit(CNodeDataStructDescribe, ['componentName']),\n  object({\n    componentName: literal(InnerComponentNameEnum.ROOT_CONTAINER),\n  })\n);\n"
  },
  {
    "path": "packages/model/src/util/dataCheck.ts",
    "content": "import { assert, Struct, StructError } from 'superstruct';\nimport { isPlainObject } from './lodash';\nexport type BaseDataCheckParameters = {\n  data: any;\n  message?: string;\n  throwError?: boolean;\n};\nexport type DataCheckFunc = (parameters: BaseDataCheckParameters) => {\n  isValidate: boolean;\n  message?: string;\n  throwError?: boolean;\n  error?: any;\n};\n\nexport const checkFuncWrap = (func: DataCheckFunc): DataCheckFunc => {\n  return ({ data, message, throwError }) => {\n    const validateRes = func({ data, message, throwError });\n\n    if (validateRes.isValidate) {\n      return validateRes;\n    } else {\n      if (throwError) {\n        if (validateRes.message || message) {\n          throw new Error(`${validateRes.message || message}\\n originData: ${JSON.stringify(data)}`);\n        } else {\n          throw new Error(`${JSON.stringify(data)} \\n data struct format is invalidate`);\n        }\n      } else {\n        if (validateRes.message || message) {\n          console.warn(`${validateRes.message || message}\\n originData: ${JSON.stringify(data)}`);\n        } else {\n          console.warn(`${JSON.stringify(data)} \\n data struct format is invalidate`);\n        }\n        return validateRes;\n      }\n    }\n  };\n};\n\nexport const checkString = checkFuncWrap((data) => {\n  if (data && typeof data === 'string') {\n    return {\n      isValidate: true,\n    };\n  } else {\n    return {\n      isValidate: false,\n    };\n  }\n});\n\nexport const checkNumber = checkFuncWrap((data) => {\n  if (data && typeof data === 'number') {\n    return {\n      isValidate: true,\n    };\n  } else {\n    return {\n      isValidate: false,\n    };\n  }\n});\n\nexport const checkIsObject = checkFuncWrap((data) => {\n  if (isPlainObject(data)) {\n    return {\n      isValidate: true,\n    };\n  } else {\n    return {\n      isValidate: false,\n    };\n  }\n});\n\nexport const checkComplexData = (parameters: {\n  data: any;\n  dataStruct: Struct<any, any>;\n  message?: string;\n  throwError?: boolean;\n}) => {\n  const { data, message, throwError, dataStruct } = parameters;\n  const newFunc = checkFuncWrap(({ data: innerData }) => {\n    try {\n      assert(innerData, dataStruct);\n      return {\n        isValidate: true,\n      };\n    } catch (e: any) {\n      let message = e;\n\n      if (e instanceof StructError) {\n        const failures = e.failures();\n\n        message = failures.map((failure) => {\n          return `【${failure.path.join('.')}】: ${failure.message}\\n`;\n        });\n      }\n      return {\n        isValidate: false,\n        message: message,\n        error: e,\n      };\n    }\n  });\n  return newFunc({ data, message, throwError });\n};\n"
  },
  {
    "path": "packages/model/src/util/index.ts",
    "content": "import { isArray, isPlainObject, omitBy } from 'lodash-es';\nimport { CNodePropsTypeEnum } from '../const/schema';\nimport { CPage } from '../Page';\nimport { CRootNode } from '../Page/RootNode';\nimport { CNode } from '../Page/RootNode/Node';\nimport { CProp } from '../Page/RootNode/Node/prop';\nimport { CSlot } from '../Page/RootNode/Node/slot';\nimport { InnerComponentNameEnum } from '../types/rootNode';\nimport { FunctionPropType, JSExpressionPropType, RenderPropType, TActionLogicItem } from '../types/node';\n\nexport const isExpression = (val: any): val is JSExpressionPropType => {\n  if (val?.type === CNodePropsTypeEnum.EXPRESSION) {\n    return true;\n  } else {\n    return false;\n  }\n};\n\nexport const isJSSlotPropNode = (val: any): val is RenderPropType => {\n  if (val?.type === CNodePropsTypeEnum.SLOT) {\n    return true;\n  } else {\n    return false;\n  }\n};\n\nexport const isFunction = (val: any): val is FunctionPropType => {\n  if (val?.type === CNodePropsTypeEnum.FUNCTION) {\n    return true;\n  } else {\n    return false;\n  }\n};\n\nexport const isAction = (val: any): val is TActionLogicItem => {\n  if (val?.type === CNodePropsTypeEnum.ACTION) {\n    return true;\n  } else {\n    return false;\n  }\n};\n\nexport const getRandomStr = () => {\n  return Math.random().toString(32).slice(3, 9);\n};\n\nexport const isSchemaModel = (val: any): val is CRootNode => {\n  if (val?.nodeType === 'SCHEMA' && val instanceof CRootNode) {\n    return true;\n  }\n\n  return false;\n};\n\nexport const isPageModel = (val: any): val is CPage => {\n  if (val?.nodeType === InnerComponentNameEnum.ROOT_CONTAINER) {\n    return true;\n  }\n\n  return false;\n};\n\nexport const isNodeModel = (val: any): val is CNode => {\n  if (val?.nodeType === 'NODE') {\n    return true;\n  }\n\n  return false;\n};\n\nexport const isPropModel = (val: any): val is CProp => {\n  if (val?.nodeType === 'PROP') {\n    return true;\n  }\n  return false;\n};\n\nexport const isSlotModel = (val: any): val is CSlot => {\n  if (val?.nodeType === 'SLOT') {\n    return true;\n  }\n  return false;\n};\n\n/**\n *\n * @param res clear empty array and object\n */\nexport const clearSchema = (res: any) => {\n  const newRes: any = omitBy(res, (val) => {\n    if (isPlainObject(val)) {\n      return !Object.keys(val).length;\n    }\n    if (isArray(val)) {\n      return !val.length;\n    }\n\n    if (val === null || val === undefined) {\n      return true;\n    }\n    return false;\n  });\n  return newRes;\n};\n\nexport function findNode(nodeTree: CRootNode | CNode, iterator: (item: CNode | CRootNode) => boolean) {\n  const nodeList: (CRootNode | CNode)[] = [nodeTree];\n  while (nodeList.length) {\n    const target = nodeList.shift();\n    if (target && iterator(target)) {\n      return target;\n    }\n\n    const props = target?.props || {};\n\n    const dpProps = (prop: unknown) => {\n      if (prop instanceof CNode) {\n        nodeList.push(prop);\n        return;\n      }\n\n      if (prop instanceof CSlot) {\n        dpProps(prop.value.value);\n      }\n\n      if (prop instanceof CProp) {\n        dpProps(prop.value);\n        return;\n      }\n      if (isPlainObject(prop)) {\n        const obj = prop as Record<string, any>;\n        Object.keys(obj).forEach((key) => {\n          dpProps(obj[key]);\n        });\n        return;\n      }\n\n      if (isArray(prop)) {\n        prop.forEach((it) => {\n          dpProps(it);\n        });\n        return;\n      }\n    };\n\n    // 检索所有的 props 中的节点\n    dpProps(props);\n    // 合并入待索引的列表\n    const tempNodeList: CNode[] = (target?.value.children.filter((el) => el instanceof CNode) as CNode[]) || [];\n    nodeList.push(...tempNodeList);\n  }\n  return undefined;\n}\n\nexport function getNode(nodeTree: CRootNode | CNode, id: string) {\n  return findNode(nodeTree, (node) => node.id === id);\n}\n\nexport function findContainerNode(\n  node: CNode | CRootNode | CSlot | CPage | null | undefined\n): CNode | CRootNode | CSlot | CPage | undefined {\n  if (!node) return undefined;\n  if (isSlotModel(node) || isPageModel(node) || node.isContainer()) {\n    return node;\n  }\n  return findContainerNode(node.parent);\n}\n\ntype CBFunction = (node: CNode | CRootNode) => any;\n\n/** 遍历 page 上所有的节点 */\nexport const traversePageNode = (pageModel: CPage, cb: CBFunction) => {\n  // 结合物料以及 state 的定义计算出当前页面的 类型提示\n  const pageNodeTree = pageModel.value.componentsTree;\n  const rootNode = pageNodeTree;\n  traversePageNodeCore(rootNode, cb);\n};\n\nexport const traversePageNodeCore = (node: CNode | CRootNode | (CNode | CRootNode)[], cb: CBFunction) => {\n  if (isArray(node)) {\n    node.map((e) => {\n      traversePageNodeCore(e, cb);\n    });\n    return;\n  }\n\n  cb(node);\n\n  // 处理 props 中的 节点\n  const processProps = (val: Record<any, CProp>, key: string, keys: string[], cb: CBFunction) => {\n    const flag = isSlotModel(val);\n    if (flag) {\n      if (isArray(val.value.value)) {\n        const nodeList = val.value.value;\n        return traversePageNodeCore(nodeList, cb);\n      } else {\n        return traversePageNodeCore(val.value.value, cb);\n      }\n    }\n    if (isPlainObject(val)) {\n      const tempVal = val as any;\n      Object.keys(tempVal).forEach((newKey) => {\n        processProps(tempVal[newKey], newKey, [...keys, key], cb);\n      });\n    }\n\n    if (Array.isArray(val)) {\n      const tempVal = val as any[];\n      tempVal.forEach((item, index) => {\n        processProps(item, String(index), [...keys, key], cb);\n      });\n    }\n    return;\n  };\n\n  const props = node.props;\n\n  processProps(props, '', [], cb);\n\n  // 处理 children\n  if (node.value.children.length) {\n    node.value.children.forEach((el) => {\n      if (isNodeModel(el)) {\n        traversePageNodeCore(el, cb);\n      }\n    });\n  }\n};\n"
  },
  {
    "path": "packages/model/src/util/lodash.ts",
    "content": "export { isPlainObject, values, isArray, isObject, cloneDeep, omitBy, uniqBy } from 'lodash-es';\n"
  },
  {
    "path": "packages/model/src/util/modelEmitter.ts",
    "content": "import mitt, { Emitter } from 'mitt';\nimport { CRootNode, CRootNodeModelDataType } from '../Page/RootNode';\nimport { CNode, CNodeModelDataType } from '../Page/RootNode/Node';\nimport { CProp } from '../Page/RootNode//Node/prop';\nimport { CPropDataType } from '../types/node';\nimport { CPageDataType } from '../types/page';\nimport { CPage, CPpageDataModelType } from '../Page';\n\nexport type DataModelEventType = {\n  onPageChange: {\n    value: CPageDataType | CPpageDataModelType;\n    preValue: CPageDataType | CPpageDataModelType;\n    node: CPage;\n  };\n  onReloadPage: {\n    value: CPageDataType | CPpageDataModelType;\n    preValue: CPageDataType | CPpageDataModelType;\n    node: CPage;\n  };\n  onSchemaChange: any;\n  onNodeChange: {\n    value: CNodeModelDataType | CRootNodeModelDataType;\n    preValue: CNodeModelDataType | CRootNodeModelDataType;\n    node: CNode | CRootNode;\n  };\n  onPropChange: {\n    value: CPropDataType;\n    preValue: CPropDataType;\n    node: CProp;\n  };\n};\n\nexport const DataModelEmitter: Emitter<DataModelEventType> = mitt<DataModelEventType>();\nexport type DataModelEmitterType = Emitter<DataModelEventType>;\n"
  },
  {
    "path": "packages/model/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2021\",\n    \"useDefineForClassFields\": true,\n    \"lib\": [\n      \"DOM\",\n      \"DOM.Iterable\",\n      \"ES2021\"\n    ],\n    \"allowJs\": false,\n    \"skipLibCheck\": true,\n    \"esModuleInterop\": false,\n    \"allowSyntheticDefaultImports\": true,\n    \"strict\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"moduleResolution\": \"Node\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": false,\n    \"noEmit\": true,\n    \"jsx\": \"react\",\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"@/*\": [\n        \"src/*\"\n      ],\n      \"mitt\": [\n        \"./node_modules/mitt\"\n      ]\n    },\n  },\n  \"include\": [\n    \"src\"\n  ],\n}"
  },
  {
    "path": "packages/render/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "packages/render/CHANGELOG.md",
    "content": "# Change Log\n\nAll notable changes to this project will be documented in this file.\nSee [Conventional Commits](https://conventionalcommits.org) for commit guidelines.\n\n## [0.10.4](https://github.com/ByteCrazy/chameleon/compare/v0.10.3...v0.10.4) (2026-01-17)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.10.3](https://github.com/ByteCrazy/chameleon/compare/v0.10.2...v0.10.3) (2026-01-17)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.10.2](https://github.com/ByteCrazy/chameleon/compare/v0.10.1...v0.10.2) (2026-01-17)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.10.1](https://github.com/ByteCrazy/chameleon/compare/v0.10.0...v0.10.1) (2026-01-11)\n\n### ✨ Features | 新功能\n\n* **build-script, demo-page, engine, render:** 🎸 update build script dts & optimize modal root selector ([024d5ab](https://github.com/ByteCrazy/chameleon/commit/024d5ab26d19fc5f50172c328638a551fb99c129))\n* **docs-app, render:** 🎸 adapte ssr render ([7f19d8f](https://github.com/ByteCrazy/chameleon/commit/7f19d8f9084e16de27460bc1e5dea28eba31d6dd))\n\n## [0.10.0](https://github.com/hlerenow/chameleon/compare/v0.9.3...v0.10.0) (2025-12-25)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* 优化构建配置，修复 ES 模块外部化问题 ([866f0ae](https://github.com/hlerenow/chameleon/commit/866f0ae39472625137d73d143625d88d873f6c00))\n\n## [0.9.3](https://github.com/ByteCrazy/chameleon/compare/v0.9.2...v0.9.3) (2025-07-20)\n\n### ✨ Features | 新功能\n\n* **engine, render:** 🎸 add $EVENT_PARAMS and remove $Event $PARAMS_RUNTIME ([b8e58c1](https://github.com/ByteCrazy/chameleon/commit/b8e58c1e5e98d2e4b3d31e2ba52a26fda256eb47))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **render:** 🐛 fixed style props not apply to dom  from props ([2e0c80a](https://github.com/ByteCrazy/chameleon/commit/2e0c80ad02881319089b1b08225c325e37a41c6c))\n\n## [0.9.2](https://github.com/ByteCrazy/chameleon/compare/v0.9.1...v0.9.2) (2025-07-13)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **render:** 🐛 fixed ref get not correct on designer mode ([14544f4](https://github.com/ByteCrazy/chameleon/commit/14544f45a5dadeacfab5a711504b9a736ee7835e))\n\n## [0.9.1](https://github.com/ByteCrazy/chameleon/compare/v0.9.0...v0.9.1) (2025-07-13)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, render:** 🐛 fixed CustomSchemaForm title ([178cddd](https://github.com/ByteCrazy/chameleon/commit/178cddd3e8d9147822e91fcd1ce7d8e08c70d924))\n\n## [0.9.0](https://github.com/ByteCrazy/chameleon/compare/v0.8.6...v0.9.0) (2025-07-13)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.8.6](https://github.com/ByteCrazy/chameleon/compare/v0.8.5...v0.8.6) (2025-06-21)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **demo-page, engine, render:** 🐛 fixed action flow failed node not connect correct ([e235a2b](https://github.com/ByteCrazy/chameleon/commit/e235a2be2d1e1935dfc40b56936de763efc4fb67))\n* **render:** 🐛 fixed $RESPONSE value is error ([dee9906](https://github.com/ByteCrazy/chameleon/commit/dee9906c967eb079d5a7e53431dc9d29458195c8))\n\n## [0.8.5](https://github.com/ByteCrazy/chameleon/compare/v0.8.4...v0.8.5) (2025-04-13)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.8.4](https://github.com/ByteCrazy/chameleon/compare/v0.8.3...v0.8.4) (2025-04-13)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, render:** 🐛 CSS editor value update error ([209106a](https://github.com/ByteCrazy/chameleon/commit/209106a6a71819862626ee39c887d1835a8b5611))\n* **engine, render:** 🐛 fixed event not trigger when loop ([e3a10a8](https://github.com/ByteCrazy/chameleon/commit/e3a10a8ab77a3c5321f47440c7bdc2ad52e82ee2))\n\n## [0.8.3](https://github.com/ByteCrazy/chameleon/compare/v0.8.2...v0.8.3) (2025-04-12)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.8.2](https://github.com/ByteCrazy/chameleon/compare/v0.8.1...v0.8.2) (2025-04-11)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, render:** 🐛 fixed functionSeter and expressionSetter dts ([1e326e2](https://github.com/ByteCrazy/chameleon/commit/1e326e288f8d072497c01c1ccb0c06bed0521949))\n\n## [0.8.1](https://github.com/ByteCrazy/chameleon/compare/v0.8.0...v0.8.1) (2025-04-10)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.8.0](https://github.com/ByteCrazy/chameleon/compare/v0.7.0...v0.8.0) (2025-04-10)\n\n### ✨ Features | 新功能\n\n* **demo-page, engine, render:** 🎸 update run time var for function ([d8fb90a](https://github.com/ByteCrazy/chameleon/commit/d8fb90ac72a233d7fdb5fcb36e0b7963f83c5acf))\n* **engine, model, render:** 🎸 fixed cycle update when update value in mount ([9b30922](https://github.com/ByteCrazy/chameleon/commit/9b30922ffac4a5b4fb1fe123c4604cc0fff63911))\n* **engine, render:** 🎸 optimize expression setter ([07c11e9](https://github.com/ByteCrazy/chameleon/commit/07c11e9bdf011f70763623d6a2f185a3c3830e5e))\n* **engine, render:** 🎸 optimize globalState update ([4e7bb6a](https://github.com/ByteCrazy/chameleon/commit/4e7bb6ab68cd69fe6d429abe417587c3ac26eb18))\n\n## [0.7.0](https://github.com/ByteCrazy/chameleon/compare/v0.6.0...v0.7.0) (2025-04-06)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.6.0](https://github.com/ByteCrazy/chameleon/compare/v0.5.2...v0.6.0) (2025-03-30)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.5.2](https://github.com/ByteCrazy/chameleon/compare/v0.5.1...v0.5.2) (2025-03-30)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.5.1](https://github.com/ByteCrazy/chameleon/compare/v0.5.0...v0.5.1) (2025-03-30)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.5.0](https://github.com/ByteCrazy/chameleon/compare/v0.4.0...v0.5.0) (2025-03-30)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.4.0](https://github.com/ByteCrazy/chameleon/compare/v0.3.21...v0.4.0) (2025-03-29)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.3.21](https://github.com/ByteCrazy/chameleon/compare/v0.3.20...v0.3.21) (2025-03-26)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.3.20](https://github.com/ByteCrazy/chameleon/compare/v0.3.19...v0.3.20) (2025-03-26)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.3.19](https://github.com/ByteCrazy/chameleon/compare/v0.3.18...v0.3.19) (2025-03-26)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.3.18](https://github.com/ByteCrazy/chameleon/compare/v0.3.17...v0.3.18) (2025-03-26)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.3.17](https://github.com/ByteCrazy/chameleon/compare/v0.3.16...v0.3.17) (2025-03-25)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, material, model, render:** 🐛 fixed node update value material is undefined ([969174d](https://github.com/ByteCrazy/chameleon/commit/969174da968aec4a1ee1fec7ca44a5e459be56bc))\n\n## [0.3.16](https://github.com/ByteCrazy/chameleon/compare/v0.3.15...v0.3.16) (2025-03-24)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.3.15](https://github.com/ByteCrazy/chameleon/compare/v0.3.14...v0.3.15) (2025-03-23)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.3.14](https://github.com/ByteCrazy/chameleon/compare/v0.3.13...v0.3.14) (2025-03-23)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.3.13](https://github.com/ByteCrazy/chameleon/compare/v0.3.12...v0.3.13) (2025-03-23)\n\n### ✨ Features | 新功能\n\n* **render:** 🎸 redner support config document context ([8d3c041](https://github.com/ByteCrazy/chameleon/commit/8d3c041263ed567a1e91087abcde382519876b2e))\n\n## [0.3.12](https://github.com/ByteCrazy/chameleon/compare/v0.3.11...v0.3.12) (2025-03-23)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.3.11](https://github.com/ByteCrazy/chameleon/compare/v0.3.10...v0.3.11) (2025-03-22)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **render:** 🐛 fixed PlaceHoder UI ([349a951](https://github.com/ByteCrazy/chameleon/commit/349a951ac9e2351acb0c556bc0ecb4295367e94e))\n\n## [0.3.10](https://github.com/ByteCrazy/chameleon/compare/v0.3.9...v0.3.10) (2025-03-22)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.3.9](https://github.com/ByteCrazy/chameleon/compare/v0.3.8...v0.3.9) (2025-03-22)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.3.8](https://github.com/ByteCrazy/chameleon/compare/v0.3.7...v0.3.8) (2025-03-16)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.3.7](https://github.com/ByteCrazy/chameleon/compare/v0.3.6...v0.3.7) (2025-03-16)\n\n### ✨ Features | 新功能\n\n* **demo-page, engine, model, render:** 🎸 support inject eng inner env to runtime ([baa5c11](https://github.com/ByteCrazy/chameleon/commit/baa5c11d389019a7e4e4b8e000433a99038b4ae3))\n\n## [0.3.6](https://github.com/ByteCrazy/chameleon/compare/v0.3.5...v0.3.6) (2025-03-09)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.3.5](https://github.com/ByteCrazy/chameleon/compare/v0.3.4...v0.3.5) (2025-03-09)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **demo-page, engine, render:** 🐛 fixed action flow only run first node ([c4bdb85](https://github.com/ByteCrazy/chameleon/commit/c4bdb85d0ca6ed09c6c66d15847de5bd14da556e))\n\n## [0.3.4](https://github.com/ByteCrazy/chameleon/compare/v0.3.3...v0.3.4) (2025-02-16)\n\n### ✨ Features | 新功能\n\n* **engine, layout, render:** 🎸 action node express support $response ([9bf1570](https://github.com/ByteCrazy/chameleon/commit/9bf1570c3b80404be78a5d3ca2c401464e7645d3))\n\n## [0.3.3](https://github.com/ByteCrazy/chameleon/compare/v0.3.2...v0.3.3) (2025-02-16)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.3.2](https://github.com/ByteCrazy/chameleon/compare/v0.3.1...v0.3.2) (2025-02-16)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.3.1](https://github.com/ByteCrazy/chameleon/compare/v0.3.0...v0.3.1) (2025-02-16)\n\n### ✨ Features | 新功能\n\n* **engine, render:** 🎸 optimize CustomAPISelectInput from and event list label ([8dbf5af](https://github.com/ByteCrazy/chameleon/commit/8dbf5af08e50c60c5ff7adc5c7e644c4ca1a9c08))\n\n## [0.3.0](https://github.com/ByteCrazy/chameleon/compare/v0.2.4...v0.3.0) (2025-02-15)\n\n### ♻️ Code Refactoring | 代码重构\n\n* **render:** 💡 optimize convertModelToComponent code logic ([3334dd5](https://github.com/ByteCrazy/chameleon/commit/3334dd5f421b0d1b78ccde403c9673cadcea91da))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, engine-website-app, material, model, render:** 🐛 fixed GRL hidden offsetY loop add size ([3df132b](https://github.com/ByteCrazy/chameleon/commit/3df132b6493026b42435d4868d77915b9f7316b2))\n\n### ✨ Features | 新功能\n\n* **demo-page, engine, model, render:** 🎸 action node use link struct ([599ece4](https://github.com/ByteCrazy/chameleon/commit/599ece4927523f7c0e330cac722c9c9e6976ea3c))\n* **demo-page, engine, model, render:** 🎸 add event panel ([e8c5648](https://github.com/ByteCrazy/chameleon/commit/e8c5648017b40cbae42c576267d1e3b9d9660918))\n* **demo-page, engine, model, render:** 🎸 support TActionLogicItem prop ([e1b9d1e](https://github.com/ByteCrazy/chameleon/commit/e1b9d1e150ae810750249322ddf906b62eee9969))\n* **demo-page, model, render:** 🎸 optimize afterResponse ([b4060ba](https://github.com/ByteCrazy/chameleon/commit/b4060ba0a0a01960ae5f8dffea159e2d5ea680b6))\n* **demo-page, model, render:** 🎸 support ASSIGN_VALUE node ([74c395b](https://github.com/ByteCrazy/chameleon/commit/74c395baec55911de734e47d4a270f9cf016ee21))\n* **demo-page, render:** 🎸 getMethods support get methods from ref ([0a26967](https://github.com/ByteCrazy/chameleon/commit/0a2696739eee29c3e30b805d537605a06496ebc5))\n* **demo-page, render:** 🎸 support eventListener attr ([fa0977f](https://github.com/ByteCrazy/chameleon/commit/fa0977fab37b961d1b7a0b04c8e528e43ab9503f))\n* **demo-page, render:** 🎸 support ON_DID_RENDER and ON_WILL_DESTROY inner event ([b4743e8](https://github.com/ByteCrazy/chameleon/commit/b4743e855766b69a8c5bc58ea3849694948bd501))\n* **docs-app, engine, engine-website-app, layout, material, model, render:** 🎸 do ActionFlowSetter 50% ([6399466](https://github.com/ByteCrazy/chameleon/commit/6399466c7253436591e071df25828a357bb7089e))\n* **engine, engine-website-app, render:** 🎸 RequestAPINode support custom select ([5b72385](https://github.com/ByteCrazy/chameleon/commit/5b72385673bb646aff3ad2c5a9d8fffa142733dd))\n* **model, render:** 🎸 add acion logic type defined ([a7c32a3](https://github.com/ByteCrazy/chameleon/commit/a7c32a33f80a00458fe16fbc4d0c34631c103b61))\n* **model, render:** 🎸 optimize render react adapter code struct ([d9fa3d0](https://github.com/ByteCrazy/chameleon/commit/d9fa3d0a72e4d7e04e4e86e62d2a4f6f31bd808e))\n* **render:** 🎸 add findDOMNode API ([2898452](https://github.com/ByteCrazy/chameleon/commit/2898452491554afddf36a795876182ff0d1bfc59))\n\n## [0.2.4](https://github.com/ByteCrazy/chameleon/compare/v0.2.3...v0.2.4) (2024-12-08)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.2.3](https://github.com/ByteCrazy/chameleon/compare/v0.2.2...v0.2.3) (2024-12-08)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.2.2](https://github.com/ByteCrazy/chameleon/compare/v0.2.1...v0.2.2) (2024-12-07)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.2.1](https://github.com/ByteCrazy/chameleon/compare/v0.2.0...v0.2.1) (2024-12-07)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, engine-website-app, material, model, render:** 🎸 fixed RGL component and optimize project struct ([99f0679](https://github.com/ByteCrazy/chameleon/commit/99f0679d93a2fd696034ebed8f1abcd9d9e601d4))\n\n## [0.2.0](https://github.com/ByteCrazy/chameleon/compare/v0.1.1...v0.2.0) (2024-12-07)\n\n### ✨ Features | 新功能\n\n* **build-script, demo-page, engine, engine-website-app, layout, material, model, render:** 🎸 upgrade vite to 6.0 ([bcac2b1](https://github.com/ByteCrazy/chameleon/commit/bcac2b15b83b41a7042ca37368c1b45302ad81d5))\n* **engine, layout, render:** 🎸 replace findDOMNode API ([af2531a](https://github.com/ByteCrazy/chameleon/commit/af2531a095124ac55d4f6dc6896d430f52f5da82))\n\n## [0.1.1](https://github.com/ByteCrazy/chameleon/compare/v0.1.0...v0.1.1) (2024-11-11)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **render:** 🐛 fixeds useRender not memory ([d579bfa](https://github.com/ByteCrazy/chameleon/commit/d579bfa401c39810a5dadc55e3fa1f3a0f407322))\n\n## [0.1.0](https://github.com/ByteCrazy/chameleon/compare/v0.0.46...v0.1.0) (2024-09-07)\n\n### 👷 Continuous Integration | CI 配置\n\n* **build-script, docs-website, material, render:** 🎡 not build docs ([37aa10a](https://github.com/ByteCrazy/chameleon/commit/37aa10abf0324f3465a6921a9a014875e9500f8f))\n\n### ✨ Features | 新功能\n\n* **build-script, demo-page, engine, layout, render:** 🎸 add lang switch ([29da65e](https://github.com/ByteCrazy/chameleon/commit/29da65ee1aa09550d910ddfbbcb9d8b4db983373))\n* **demo-page, engine, render:** 🎸 add getGlobalState and optimize node udpate ([4d3934f](https://github.com/ByteCrazy/chameleon/commit/4d3934fd8febe616a44e5d39da0e10964f3c800d))\n* **docs-app, engine, layout, material, model, render:** 🎸 optimize GL layout ([02274b4](https://github.com/ByteCrazy/chameleon/commit/02274b432903dc247c5613873f14715dd806decd))\n\n## [0.0.46](https://github.com/ByteCrazy/chameleon/compare/v0.0.45...v0.0.46) (2024-06-30)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.0.45](https://github.com/ByteCrazy/chameleon/compare/v0.0.44...v0.0.45) (2024-06-30)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.0.44](https://github.com/ByteCrazy/chameleon/compare/v0.0.43...v0.0.44) (2024-06-30)\n\n### ✨ Features | 新功能\n\n* **engine, model, render:** 🎸 optimize addMaterials and fixed inner mat schema ([aa77803](https://github.com/ByteCrazy/chameleon/commit/aa77803c75b203ee2a098fbee143f4a9581e15fa))\n\n## [0.0.43](https://github.com/ByteCrazy/chameleon/compare/v0.0.42...v0.0.43) (2024-06-29)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.0.42](https://github.com/ByteCrazy/chameleon/compare/v0.0.41...v0.0.42) (2024-06-01)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.0.41](https://github.com/ByteCrazy/chameleon/compare/v0.0.40...v0.0.41) (2024-05-26)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, layout, material, model, render:** 🎸 optimize build script ([0a60cc4](https://github.com/ByteCrazy/chameleon/commit/0a60cc4f5e5635d9e544b5141eaed5b8433901a5))\n\n## [0.0.40](https://github.com/ByteCrazy/chameleon/compare/v0.0.39...v0.0.40) (2024-04-28)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* fixed collectVariable logic ([e51bce9](https://github.com/ByteCrazy/chameleon/commit/e51bce9db030f8657575900406200972eee2e928))\n\n## [0.0.39](https://github.com/ByteCrazy/chameleon/compare/v0.0.38...v0.0.39) (2024-04-27)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **build-script, demo-page, engine, layout, material, model, render:** 🐛 fix flatObject collectVariable refeer pos ([1c4163a](https://github.com/ByteCrazy/chameleon/commit/1c4163aeeb89176a1ff5b50f76930889df7751fe))\n\n## [0.0.38](https://github.com/ByteCrazy/chameleon/compare/v0.0.37...v0.0.38) (2024-04-27)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.0.37](https://github.com/ByteCrazy/chameleon/compare/v0.0.36...v0.0.37) (2024-04-27)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.0.36](https://github.com/ByteCrazy/chameleon/compare/v0.0.35...v0.0.36) (2024-04-27)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **render:** 🐛 fixed collectVariable method bug ([d5b7c5f](https://github.com/ByteCrazy/chameleon/commit/d5b7c5f274da52de09875cb9a9acf263ff051e59))\n\n## [0.0.35](https://github.com/ByteCrazy/chameleon/compare/v0.0.34...v0.0.35) (2024-04-27)\n\n### ✨ Features | 新功能\n\n* **render:** 🎸 support comp name  with '.' and adapte __esModule prop ([0020e9e](https://github.com/ByteCrazy/chameleon/commit/0020e9e0db01cc08895c042c54253d133676a672))\n\n## [0.0.34](https://github.com/ByteCrazy/chameleon/compare/v0.0.33...v0.0.34) (2024-04-27)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.0.33](https://github.com/ByteCrazy/chameleon/compare/v0.0.32...v0.0.33) (2024-04-27)\n\n### ✨ Features | 新功能\n\n* **engine, layout, render:** 🎸 optimize history step save ([dca964a](https://github.com/ByteCrazy/chameleon/commit/dca964a990a3cc0c5fd853d853a55a4957999644))\n* **engine, render:** 🎸 designer plugin add  updateComponent method ([f161177](https://github.com/ByteCrazy/chameleon/commit/f16117793402681a1eda8f8cba9c06e2ee5247ad))\n\n## [0.0.32](https://github.com/ByteCrazy/chameleon/compare/v0.0.31...v0.0.32) (2024-01-30)\n\n### ✨ Features | 新功能\n\n* **docs-website, engine, model, render:** 🎸 add updateMaterials、updatePage methods ([39ed2b6](https://github.com/ByteCrazy/chameleon/commit/39ed2b692a8a7379a79b96cb3fd8cdb76a4f01f2))\n\n## [0.0.31](https://github.com/ByteCrazy/chameleon/compare/v0.0.30...v0.0.31) (2023-10-17)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.0.30](https://github.com/ByteCrazy/chameleon/compare/v0.0.29...v0.0.30) (2023-10-17)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **render:** 🐛 remove child node cache ([0bca7b6](https://github.com/ByteCrazy/chameleon/commit/0bca7b6b1577c79e219f57ff9699b49000bb17ab))\n\n## [0.0.29](https://github.com/ByteCrazy/chameleon/compare/v0.0.28...v0.0.29) (2023-10-17)\n\n### ✨ Features | 新功能\n\n* **demo-page, render:** 🎸 compatible new style schema ([8165453](https://github.com/ByteCrazy/chameleon/commit/816545302e4d88a0976e8fdf3b17ed8a9a2978bd))\n* **engine, model, render:** 🎸 cssEditor support cssText ([3dc74b4](https://github.com/ByteCrazy/chameleon/commit/3dc74b4895d414a718c00911a39d8491fafcfaee))\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, render:** 🐛 fixed build error and add child cache for render ([9026474](https://github.com/ByteCrazy/chameleon/commit/90264746fe99b8cdd4d055f2d778e67aae38af87))\n\n## [0.0.28](https://github.com/ByteCrazy/chameleon/compare/v0.0.27...v0.0.28) (2023-08-29)\n\n### ✨ Features | 新功能\n\n* **model, render:** 🎸 node support custom dropPlaceholder ([dabdbc0](https://github.com/ByteCrazy/chameleon/commit/dabdbc00b375d5b8491ff3498075caac45473a98))\n\n## [0.0.27](https://github.com/ByteCrazy/chameleon/compare/v0.0.26...v0.0.27) (2023-08-28)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.0.26](https://github.com/ByteCrazy/chameleon/compare/v0.0.25...v0.0.26) (2023-08-27)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **render:** 🐛 fixed process is not defined in render ([03e258f](https://github.com/ByteCrazy/chameleon/commit/03e258f5674bdb54da6dee8dd7a68c31f7432a69))\n\n## [0.0.25](https://github.com/ByteCrazy/chameleon/compare/v0.0.24...v0.0.25) (2023-08-27)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.0.24](https://github.com/ByteCrazy/chameleon/compare/v0.0.23...v0.0.24) (2023-08-27)\n\n### ✨ Features | 新功能\n\n* **engine, model, render:** 🎸 fixed node maybe is null on rightPanel ([aee551e](https://github.com/ByteCrazy/chameleon/commit/aee551e77454f61900318003f9e3a3ffb1ef9427))\n\n## [0.0.23](https://github.com/ByteCrazy/chameleon/compare/v0.0.22...v0.0.23) (2023-08-26)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, render:** 🐛 fixed CSSPropertiesVariableBindEditor value not update ([9084c32](https://github.com/ByteCrazy/chameleon/commit/9084c32bb919c2e6191c29b871f9d1537d139050))\n\n## [0.0.22](https://github.com/ByteCrazy/chameleon/compare/v0.0.21...v0.0.22) (2023-08-24)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.0.21](https://github.com/ByteCrazy/chameleon/compare/v0.0.20...v0.0.21) (2023-08-19)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **render:** 🐛 fixed assetsLoader resource is empty problem ([1e7ec0a](https://github.com/ByteCrazy/chameleon/commit/1e7ec0ab966c4b618638ac519401f4df5cfc8f82))\n\n### ✨ Features | 新功能\n\n* **engine, layout, model, render:** 🎸 optimize wrapComponent config ([d5916a7](https://github.com/ByteCrazy/chameleon/commit/d5916a7e6ee3cf79a32d4d23663b6873d86fe671))\n\n## [0.0.20](https://github.com/ByteCrazy/chameleon/compare/v0.0.19...v0.0.20) (2023-08-19)\n\n### ✨ Features | 新功能\n\n* **model, render:** 🎸 support wrapComponent meterial config ([73fa19c](https://github.com/ByteCrazy/chameleon/commit/73fa19cd698bd61ef8079ae71dfd284600796ad2))\n\n## [0.0.19](https://github.com/ByteCrazy/chameleon/compare/v0.0.18...v0.0.19) (2023-08-17)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, render:** 🐛 fixed if assets is empty, loader will not success ([169d9ad](https://github.com/ByteCrazy/chameleon/commit/169d9ad9e5791353ad3a68dd0212c6ecfda64a29))\n\n## [0.0.18](https://github.com/ByteCrazy/chameleon/compare/v0.0.17...v0.0.18) (2023-08-14)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **render:** 🐛 fixed page reload assets load failed ([b9eb815](https://github.com/ByteCrazy/chameleon/commit/b9eb815e75e1cd1738112ee18147f6faf0035155))\n\n## [0.0.17](https://github.com/ByteCrazy/chameleon/compare/v0.0.16...v0.0.17) (2023-08-06)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.0.16](https://github.com/ByteCrazy/chameleon/compare/v0.0.15...v0.0.16) (2023-08-06)\n\n### ✨ Features | 新功能\n\n* **build-script-example, demo-page, engine, layout, material, model, render:** 🎸 support direct import render from package on dev mode ([6bb38e2](https://github.com/ByteCrazy/chameleon/commit/6bb38e29678a8a4d08a7fcf9548fa548399259b0))\n\n## [0.0.15](https://github.com/ByteCrazy/chameleon/compare/v0.0.14...v0.0.15) (2023-07-01)\n\n### ✨ Features | 新功能\n\n* **demo-page, docs-website, engine, model, render:** 🎸 optimize assets load ([3ee6d58](https://github.com/ByteCrazy/chameleon/commit/3ee6d58a88e5af3fc631723240783d5c97a273b0))\n* **docs-website, engine, layout, material, render:** 🎸 support inject thridLib on $context ([ca2f074](https://github.com/ByteCrazy/chameleon/commit/ca2f07492b713c32e5a41d1f250f7888763cb665))\n* **engine, render:** 🎸 change internal api ([0c0c7c3](https://github.com/ByteCrazy/chameleon/commit/0c0c7c3fa8f86c08ab0cefb0396107ed8c52dcd5))\n* **render:** 🎸 render support store reactive ([61c7485](https://github.com/ByteCrazy/chameleon/commit/61c74857ba9541bb20d3722d351019c0ad97ac81))\n\n## [0.0.14](https://github.com/ByteCrazy/chameleon/compare/v0.0.13...v0.0.14) (2023-05-10)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **engine, render:** 🐛 fixed removeMediaCSS method run failed ([9127ec3](https://github.com/ByteCrazy/chameleon/commit/9127ec3d13f43a1c8763b2350bc0224d683da85c))\n\n## [0.0.13](https://github.com/ByteCrazy/chameleon/compare/v0.0.12...v0.0.13) (2023-05-03)\n\n### ✨ Features | 新功能\n\n* **build-script, build-script-example, demo-page, engine, layout, material, model, render:** 🎸 config code style config ([2325504](https://github.com/ByteCrazy/chameleon/commit/23255048fa4a3d4fc8f5cfa1312db5abd6cac70d))\n* **build-script, demo-page, engine, layout, model, render:** 🎸 update meterial schema ([14f4e5c](https://github.com/ByteCrazy/chameleon/commit/14f4e5caf0a4f6eca131e139d0c34ea21969dc0b))\n* **demo-page, docs-website, engine, layout, model, render:** 🎸 node support methods config ([e080b20](https://github.com/ByteCrazy/chameleon/commit/e080b20cea3cea27b95def196e078c1c225d3a83))\n* **engine, render:** 🎸 html native tag component support container filed ([475fdbd](https://github.com/ByteCrazy/chameleon/commit/475fdbd5a34581ab7cafe67b5c9d3b94102b3a16))\n* remove some useless code and fix some problems detected by eslint. ([a8e6ec2](https://github.com/ByteCrazy/chameleon/commit/a8e6ec2f107250facefc81cf87f694e40f3ed684))\n\n## [0.0.12](https://github.com/ByteCrazy/chameleon/compare/v0.0.11...v0.0.12) (2023-04-23)\n\n### 🐛 Bug Fixes | Bug 修复\n\n* **build-script, build-script-example, engine, layout, material, model, render:** 🐛 fix can not find vite/client.d.ts problem ([8a53540](https://github.com/ByteCrazy/chameleon/commit/8a53540f75737707f69f93d2f701203dbf88084a))\n\n### ✨ Features | 新功能\n\n* **demo-page, engine, layout, model, render:** 🎸 node support config self isContainer property ([d94f1f9](https://github.com/ByteCrazy/chameleon/commit/d94f1f9203f2c273856fd4aa489a1ed9cbb0f0ec))\n\n## [0.0.11](https://github.com/ByteCrazy/chameleon/compare/v0.0.10...v0.0.11) (2023-04-21)\n\n### ✨ Features | 新功能\n\n* **build-script, build-script-example, demo-page, engine, layout, material, model, render:** 🎸 optimize build-script output file format ([eda44d2](https://github.com/ByteCrazy/chameleon/commit/eda44d255bd3c5af19013bdd61342e1f0817413a))\n\n## [0.0.10](https://github.com/ByteCrazy/chameleon/compare/v0.0.9...v0.0.10) (2023-04-17)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.0.9](https://github.com/ByteCrazy/chameleon/compare/v0.0.8...v0.0.9) (2023-04-17)\n\n### ✨ Features | 新功能\n\n* **docs-website, engine, render:** 🎸 render add collectVariable flatObject util method ([ae25160](https://github.com/ByteCrazy/chameleon/commit/ae25160b568c267041b3827e836c95f60ecaee59))\n\n## [0.0.8](https://github.com/ByteCrazy/chameleon/compare/v0.0.7...v0.0.8) (2023-04-16)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, layout, model, render:** 🎸 optimize pack package ([78d99c5](https://github.com/ByteCrazy/chameleon/commit/78d99c507ad65ca39101f0239467737d2b445c87))\n\n## [0.0.7](https://github.com/ByteCrazy/chameleon/compare/v0.0.6...v0.0.7) (2023-03-29)\n\n### ✨ Features | 新功能\n\n* **build-script, build-script-example, demo-page, engine, layout, material, model, render:** 🎸 external monaco eitor and remove style panel ([494d898](https://github.com/ByteCrazy/chameleon/commit/494d898fd75dabe84d867ff45e84ae63f9b59c5e))\n\n## [0.0.6](https://github.com/ByteCrazy/chameleon/compare/v0.0.5...v0.0.6) (2023-03-28)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.0.5](https://github.com/ByteCrazy/chameleon/compare/v0.0.4...v0.0.5) (2023-03-28)\n\n### ✨ Features | 新功能\n\n* **build-script, engine, layout, material, model, render:** 🎸 complete package info ([86b095d](https://github.com/ByteCrazy/chameleon/commit/86b095da6c955946300591b1775f59c1a141c065))\n\n## [0.0.4](https://github.com/ByteCrazy/chameleon/compare/v0.0.3...v0.0.4) (2023-03-27)\n\n**Note:** Version bump only for package @chamn/render\n\n## [0.0.2](https://github.com/ByteCrazy/chameleon/compare/v0.0.1...v0.0.2) (2023-03-27)\n\n**Note:** Version bump only for package @chamn/render\n"
  },
  {
    "path": "packages/render/LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "packages/render/__tests__/demo.test.ts",
    "content": "test('adds 1 + 2 to equal 3', () => {\n  expect(3).toBe(3);\n});\n"
  },
  {
    "path": "packages/render/build.config.ts",
    "content": "import dts from 'vite-plugin-dts';\nimport path from 'path';\n// 开发模式默认读取 index.html 作为开发模式入口\n// entry 作为打包库入口\n\nconst env = process.env.BUILD_TYPE === 'PKG' ? 'production' : '';\nexport default {\n  entry: './src/index.ts',\n  // formats 会根据构建模式自动设置：生产模式包含 umd，开发模式只有 cjs 和 es\n  libName: 'CRender',\n  fileName: 'index',\n  global: {\n    react: 'React',\n    'react-dom': 'ReactDOM',\n  },\n  // 额外的 vite 配置\n  vite: {\n    define: {\n      'process.env.NODE_ENV': JSON.stringify(env),\n    },\n    plugins: [\n      dts({\n        entryRoot: path.resolve('./src'),\n        compilerOptions: {\n          skipDefaultLibCheck: false,\n        },\n      }),\n    ],\n  },\n};\n"
  },
  {
    "path": "packages/render/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <link rel=\"icon\" type=\"image/svg+xml\" href=\"/vite.svg\" />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n  <title>Vite + React + TS</title>\n</head>\n\n<body>\n  <div id=\"root\"></div>\n  <script type=\"module\" src=\"/src/_dev_/dev.tsx\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "packages/render/jest.config.js",
    "content": "/* eslint-disable no-undef */\n/*\n * For a detailed explanation regarding each configuration property, visit:\n * https://jestjs.io/docs/configuration\n */\n\nmodule.exports = {\n  // All imported modules in your tests should be mocked automatically\n  // automock: false,\n\n  // Stop running tests after `n` failures\n  // bail: 0,\n\n  // The directory where Jest should store its cached dependency information\n  // cacheDirectory: \"/private/var/folders/l9/th_r5d_12wxdj16859_mctjw0000gn/T/jest_dx\",\n\n  // Automatically clear mock calls, instances, contexts and results before every test\n  // clearMocks: false,\n\n  // Indicates whether the coverage information should be collected while executing the test\n  // collectCoverage: false,\n\n  // An array of glob patterns indicating a set of files for which coverage information should be collected\n  // collectCoverageFrom: undefined,\n\n  // The directory where Jest should output its coverage files\n  // coverageDirectory: undefined,\n\n  // An array of regexp pattern strings used to skip coverage collection\n  // coveragePathIgnorePatterns: [\n  //   \"/node_modules/\"\n  // ],\n\n  // Indicates which provider should be used to instrument code for coverage\n  // coverageProvider: \"babel\",\n\n  // A list of reporter names that Jest uses when writing coverage reports\n  // coverageReporters: [\n  //   \"json\",\n  //   \"text\",\n  //   \"lcov\",\n  //   \"clover\"\n  // ],\n\n  // An object that configures minimum threshold enforcement for coverage results\n  // coverageThreshold: undefined,\n\n  // A path to a custom dependency extractor\n  // dependencyExtractor: undefined,\n\n  // Make calling deprecated APIs throw helpful error messages\n  // errorOnDeprecated: false,\n\n  // The default configuration for fake timers\n  // fakeTimers: {\n  //   \"enableGlobally\": false\n  // },\n\n  // Force coverage collection from ignored files using an array of glob patterns\n  // forceCoverageMatch: [],\n\n  // A path to a module which exports an async function that is triggered once before all test suites\n  // globalSetup: undefined,\n\n  // A path to a module which exports an async function that is triggered once after all test suites\n  // globalTeardown: undefined,\n\n  // A set of global variables that need to be available in all test environments\n  // globals: {},\n\n  // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.\n  // maxWorkers: \"50%\",\n\n  // An array of directory names to be searched recursively up from the requiring module's location\n  // moduleDirectories: [\n  //   \"node_modules\"\n  // ],\n\n  // An array of file extensions your modules use\n  // moduleFileExtensions: [\n  //   \"js\",\n  //   \"mjs\",\n  //   \"cjs\",\n  //   \"jsx\",\n  //   \"ts\",\n  //   \"tsx\",\n  //   \"json\",\n  //   \"node\"\n  // ],\n\n  // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module\n  // moduleNameMapper: {},\n\n  // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader\n  // modulePathIgnorePatterns: [],\n\n  // Activates notifications for test results\n  // notify: false,\n\n  // An enum that specifies notification mode. Requires { notify: true }\n  // notifyMode: \"failure-change\",\n\n  // A preset that is used as a base for Jest's configuration\n  preset: 'ts-jest',\n\n  // Run tests from one or more projects\n  // projects: undefined,\n\n  // Use this configuration option to add custom reporters to Jest\n  // reporters: undefined,\n\n  // Automatically reset mock state before every test\n  // resetMocks: false,\n\n  // Reset the module registry before running each individual test\n  // resetModules: false,\n\n  // A path to a custom resolver\n  // resolver: undefined,\n\n  // Automatically restore mock state and implementation before every test\n  // restoreMocks: false,\n\n  // The root directory that Jest should scan for tests and modules within\n  // rootDir: undefined,\n\n  // A list of paths to directories that Jest should use to search for files in\n  // roots: [\n  //   \"<rootDir>\"\n  // ],\n\n  // Allows you to use a custom runner instead of Jest's default test runner\n  // runner: \"jest-runner\",\n\n  // The paths to modules that run some code to configure or set up the testing environment before each test\n  // setupFiles: [],\n\n  // A list of paths to modules that run some code to configure or set up the testing framework before each test\n  // setupFilesAfterEnv: [],\n\n  // The number of seconds after which a test is considered as slow and reported as such in the results.\n  // slowTestThreshold: 5,\n\n  // A list of paths to snapshot serializer modules Jest should use for snapshot testing\n  // snapshotSerializers: [],\n\n  // The test environment that will be used for testing\n  testEnvironment: 'jsdom',\n\n  // Options that will be passed to the testEnvironment\n  // testEnvironmentOptions: {},\n\n  // Adds a location field to test results\n  // testLocationInResults: false,\n\n  // The glob patterns Jest uses to detect test files\n  // testMatch: [\n  //   \"**/__tests__/**/*.[jt]s?(x)\",\n  //   \"**/?(*.)+(spec|test).[tj]s?(x)\"\n  // ],\n\n  // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped\n  // testPathIgnorePatterns: [\n  //   \"/node_modules/\"\n  // ],\n\n  // The regexp pattern or array of patterns that Jest uses to detect test files\n  // testRegex: [],\n\n  // This option allows the use of a custom results processor\n  // testResultsProcessor: undefined,\n\n  // This option allows use of a custom test runner\n  // testRunner: \"jest-circus/runner\",\n\n  // A map from regular expressions to paths to transformers\n  // transform: undefined,\n\n  // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation\n  // transformIgnorePatterns: [\n  //   \"/node_modules/\",\n  //   \"\\\\.pnp\\\\.[^\\\\/]+$\"\n  // ],\n\n  // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them\n  // unmockedModulePathPatterns: undefined,\n\n  // Indicates whether each individual test should be reported during the run\n  // verbose: undefined,\n\n  // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode\n  // watchPathIgnorePatterns: [],\n\n  // Whether to use watchman for file crawling\n  // watchman: true,\n};\n"
  },
  {
    "path": "packages/render/package.json",
    "content": "{\n  \"name\": \"@chamn/render\",\n  \"private\": false,\n  \"files\": [\n    \"dist\"\n  ],\n  \"type\": \"module\",\n  \"version\": \"0.10.4\",\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"main\": \"dist/index.cjs.js\",\n  \"module\": \"dist/index.es.js\",\n  \"types\": \"dist/index.d.ts\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./dist/index.d.ts\",\n      \"module-sync\": \"./dist/index.es.js\",\n      \"import\": \"./dist/index.es.js\",\n      \"require\": \"./dist/index.cjs.js\"\n    },\n    \"./dist/*\": \"./dist/*\"\n  },\n  \"scripts\": {\n    \"start\": \"build-script\",\n    \"build\": \"cross-env BUILD_TYPE=PKG VITE_CJS_TRACE=true build-script --build \",\n    \"build:analyze\": \"build-script --build --analyze\",\n    \"build:w\": \"build-script --build --watch\",\n    \"lint\": \"eslint --ext .tsx,.ts src/\",\n    \"prettier\": \"prettier --write ./src\",\n    \"test\": \"jest\"\n  },\n  \"peerDependencies\": {\n    \"react\": \">=16.9.0\",\n    \"react-dom\": \">=16.9.0\"\n  },\n  \"dependencies\": {\n    \"@chamn/model\": \"workspace:*\",\n    \"html-tag-names\": \"^2.0.1\",\n    \"loadjs\": \"4.3.0-rc1\",\n    \"lodash-es\": \"^4.17.21\",\n    \"react-reconciler\": \"^0.31.0\",\n    \"zustand\": \"^4.3.8\"\n  },\n  \"devDependencies\": {\n    \"@chamn/build-script\": \"workspace:*\",\n    \"@chamn/demo-page\": \"workspace:*\",\n    \"@types/loadjs\": \"^4.0.1\",\n    \"@types/lodash-es\": \"^4.17.6\",\n    \"@types/node\": \"~18.15.9\",\n    \"@types/react\": \"^18.2.0\",\n    \"@types/react-dom\": \"^18.0.6\",\n    \"antd\": \"^5.23.2\",\n    \"cross-env\": \"^7.0.3\",\n    \"react\": \"^18.2.0\",\n    \"react-dom\": \"^18.2.0\",\n    \"react-router-dom\": \"^6.6.1\",\n    \"sass\": \"^1.54.0\",\n    \"vite-plugin-dts\": \"^2.1.0\"\n  },\n  \"config\": {},\n  \"gitHead\": \"dc3e55fdeb903a8012f6ebd3ebc018ed61ad89db\"\n}\n"
  },
  {
    "path": "packages/render/src/_dev_/components.tsx",
    "content": "import React, { forwardRef, useImperativeHandle, useState } from 'react';\nimport * as antD from 'antd';\n\nexport const components: any = {\n  ...antD,\n  Page: ({ children }: any) => {\n    return <div style={{ padding: '10px' }}>{children}</div>;\n  },\n  div: ({ children, COMPONENTS, ...props }: any) => {\n    return <div {...props}>{children}</div>;\n  },\n  Button: forwardRef((props: any, ref) => {\n    const [state, setState] = useState(1);\n    useImperativeHandle(\n      ref,\n      () => {\n        return {\n          setState,\n          sayHello: (newVal: any) => {\n            setState(newVal + Date.now());\n          },\n        };\n      },\n      []\n    );\n    return <div {...props}>Button{state}</div>;\n  }),\n};\n"
  },
  {
    "path": "packages/render/src/_dev_/dev.tsx",
    "content": "import React from 'react';\nimport ReactDOM from 'react-dom/client';\nimport * as ReactDOMAll from 'react-dom';\nimport { RouterProvider } from 'react-router-dom';\nimport { router } from './router';\n\nwindow.React = React;\n(window as any).ReactDOM = ReactDOMAll;\n\nconst root = ReactDOM.createRoot(document.getElementById('root')!);\n\nroot.render(<RouterProvider router={router} />);\n"
  },
  {
    "path": "packages/render/src/_dev_/index.css",
    "content": "* {\n  margin: 0;\n  padding: 0;\n  box-sizing: border-box;\n}\n\nhtml,\nbody,\n#app {\n  width: 100%;\n  height: 100%;\n  overflow: auto;\n}\n"
  },
  {
    "path": "packages/render/src/_dev_/page/DesignerRenderDemo.tsx",
    "content": "import React, { useEffect, useState } from 'react';\nimport { BasePage, Material } from '@chamn/demo-page';\nimport { ReactAdapter } from '../../index';\nimport '../index.css';\nimport { DesignRender, useDesignRender } from '../../core/designReactRender';\nimport { CPage } from '@chamn/model';\nimport { components } from '../components';\n\nexport type AppProp = {\n  a: string;\n};\n\nexport function DesignerRenderDemo() {\n  // SamplePage;\n  // BasePage;\n  // EmptyPage;\n\n  const [page] = useState(\n    new CPage(BasePage, {\n      materials: Material,\n    })\n  );\n  const renderHandle = useDesignRender();\n  (window as any).renderHandle = renderHandle;\n  useEffect(() => {\n    console.log('🚀 ~ file: dev.tsx ~ line 31 ~ App ~ page', page);\n    page.getNode('5');\n\n    document.documentElement.addEventListener(\n      'click',\n      (e) => {\n        const eventTargetDom = e.target;\n        const instance = renderHandle.getInstanceByDom(eventTargetDom as any);\n        console.log('🚀 ~ file: dev.tsx ~ line 50 ~ useEffect ~ instance', instance);\n        const targetDom = renderHandle.getDomsById(instance?._NODE_ID || '');\n        const targetDomRectList = renderHandle.getDomRectById(instance?._NODE_ID || '');\n\n        console.log('🚀 ~ file: dev.tsx ~ line 51 ~ useEffect ~ targetDom', targetDom, targetDomRectList);\n      },\n      true\n    );\n\n    // setTimeout(() => {\n    //   const newNode = page.createNode({\n    //     componentName: 'Button',\n    //     children: ['动态添加的按钮'],\n    //   });\n    //   const boxNode = page.value.componentsTree.value.children[1];\n    //   const [node] = page.value.componentsTree.value.children.splice(3, 1);\n    //   page.value.componentsTree.updateValue();\n    //   boxNode.value.children.push(node, newNode);\n    //   boxNode.updateValue();\n    //   const tableNode = page.getNode('3');\n    //   console.log(tableNode);\n    //   tableNode?.props.columns.updateValue();\n    // }, 500);\n    page.export();\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n\n  return (\n    <div className=\"App\">\n      <DesignRender\n        renderMode=\"design\"\n        pageModel={page}\n        components={components}\n        render={renderHandle}\n        adapter={ReactAdapter}\n      />\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/render/src/_dev_/page/RenderDemo.tsx",
    "content": "import React, { useEffect, useState } from 'react';\nimport { SamplePage, Material } from '@chamn/demo-page';\nimport { ReactAdapter, Render, useRender } from '../../index';\nimport '../index.css';\nimport { CPage } from '@chamn/model';\nimport { components } from '../components';\n\nexport type AppProp = {\n  a: string;\n};\n\nexport function RenderDemo() {\n  // SamplePage;\n  // BasePage;\n  const [page] = useState(\n    new CPage(SamplePage, {\n      materials: Material,\n    })\n  );\n  (window as any).__CPAGE_MODEL = page;\n  const renderHandle = useRender();\n  (window as any).RENDER_HANDLE = renderHandle;\n  // useEffect(() => {\n  //   const node = page.getNode('9g9ohd');\n  //   console.log('🚀 ~ file: RenderDemo.tsx:25 ~ useEffect ~ node:', node);\n  //   if (!node) {\n  //     return;\n  //   }\n  //   node.value.methods = [\n  //     {\n  //       name: 'testMethod',\n  //       define: {\n  //         type: 'FUNCTION',\n  //         value: 'function () { console.log(\"$$context\", $$context);}',\n  //       },\n  //     },\n  //   ];\n  //   node.updateValue();\n\n  //   // setTimeout(() => {\n  //   //   const newNode = page.createNode({\n  //   //     componentName: 'Button',\n  //   //     children: ['动态添加的按钮'],\n  //   //   });\n  //   //   const boxNode = page.value.componentsTree.value.children[1];\n  //   //   const [node] = page.value.componentsTree.value.children.splice(3, 1);\n  //   //   page.value.componentsTree.updateValue();\n  //   //   console.log(\n  //   //     '🚀 ~ file: dev.tsx ~ line 70 ~ setTimeout ~ boxNode',\n  //   //     boxNode\n  //   //   );\n\n  //   //   boxNode.value.children.push(node, newNode);\n\n  //   //   boxNode.updateValue();\n  //   //   const tableNode = page.getNode('3');\n  //   //   console.log(tableNode);\n  //   //   tableNode?.props.columns.updateValue();\n  //   // }, 500);\n\n  //   console.log(page.export());\n  //   console.log(page);\n  //   page?.moveNodeById('999', '5', 'BEFORE');\n  //   console.log(page);\n\n  //   page.export();\n  // }, []);\n  useEffect(() => {\n    console.log('11111', Date.now());\n  }, []);\n\n  return (\n    <div className=\"App\">\n      <Render\n        pageModel={page}\n        components={components}\n        render={renderHandle as any}\n        adapter={ReactAdapter}\n        renderMode=\"normal\"\n        requestAPI={async (params) => {\n          const random = Math.random();\n          if (random > 0.5) {\n            throw new Error('request error: ');\n          } else {\n            return params;\n          }\n        }}\n      />\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/render/src/_dev_/router.tsx",
    "content": "import React from 'react';\nimport { createBrowserRouter } from 'react-router-dom';\nimport { DesignerRenderDemo } from './page/DesignerRenderDemo';\nimport { RenderDemo } from './page/RenderDemo';\n\nexport const router: ReturnType<typeof createBrowserRouter> = createBrowserRouter([\n  {\n    path: '/',\n    element: <RenderDemo />,\n  },\n  {\n    path: '/designer',\n    element: <DesignerRenderDemo />,\n  },\n]);\n"
  },
  {
    "path": "packages/render/src/_dev_/testPageData.ts",
    "content": "export const testPageData = {\n  version: '1.0.0',\n  name: 'EmptyPage',\n  componentsMeta: [\n    { componentName: 'CContainer', name: 'CContainer', package: 'CHAMELEON_INNER_PKG', version: '1.0.0' },\n    { componentName: 'CBlock', name: 'CBlock', package: 'CHAMELEON_INNER_PKG', version: '1.0.0' },\n  ],\n  componentsTree: {\n    componentName: 'RootContainer',\n    children: [\n      {\n        componentName: 'GridLayout',\n        id: 'fp1rl7',\n        children: [\n          {\n            props: { x: 2, y: 1, w: 6, h: 5 },\n            componentName: 'GridItem',\n            id: 'v1n3ef',\n            configure: { propsSetter: {}, advanceSetter: {} },\n          },\n        ],\n        configure: { propsSetter: {}, advanceSetter: {} },\n      },\n      {\n        css: { value: [{ state: 'normal', media: [], text: 'background: white;width: 100%;' }] },\n        componentName: 'CContainer',\n        id: 'ilthel',\n        configure: { propsSetter: {}, advanceSetter: {} },\n      },\n      {\n        css: { value: [{ state: 'normal', media: [], text: 'background: white;width: 100%;' }] },\n        componentName: 'CContainer',\n        id: 'a5dcgv',\n        children: [\n          {\n            css: { value: [{ state: 'normal', media: [], text: 'background: white ; width: 100%; height: 100px' }] },\n            componentName: 'CBlock',\n            id: 'ucrsk9',\n            configure: { propsSetter: {}, advanceSetter: {} },\n            eventListener: [\n              {\n                name: 'ON_DID_RENDER',\n                func: {\n                  type: 'ACTION',\n                  handler: [\n                    {\n                      id: '7mmci0',\n                      type: 'RUN_CODE',\n                      name: 'run_code_7mmci0',\n                      value: \"\\nconsole.log('hello world',$$context);\\n\\nconsole.log($$context.globalState);\\n\",\n                      __DEV_CONFIG__: {},\n                    },\n                  ],\n                  params: [],\n                },\n              },\n            ],\n          },\n        ],\n        configure: { propsSetter: {}, advanceSetter: {} },\n      },\n    ],\n    configure: { propsSetter: {}, advanceSetter: {} },\n    state: { a: 1 },\n  },\n  assets: [],\n};\n"
  },
  {
    "path": "packages/render/src/build-script-env.d.ts",
    "content": "/// <reference types=\"@chamn/build-script/client\" />\n\ndeclare module 'react-reconciler/reflection';\n"
  },
  {
    "path": "packages/render/src/commonComponent/index.tsx",
    "content": "/* eslint-disable react-refresh/only-export-components */\nimport React, { useEffect } from 'react';\nimport { htmlTagNames } from 'html-tag-names';\n\nconst transformListToObj = (list: { key: string; value: any }[]) => {\n  const res: Record<string, any> = {};\n  list.forEach((el) => {\n    res[el.key] = el.value;\n  });\n  return res;\n};\n\nconst htmlNativeComponents = htmlTagNames.reduce((res, tag) => {\n  res[tag] = ({ children, $$attributes = [], ...props }: any) => {\n    let child = children;\n    if (!Array.isArray(children)) {\n      child = [children];\n    }\n\n    const finalStyle = {\n      boxSizing: 'border-box',\n      ...(props.style || {}),\n    };\n    return React.createElement(\n      tag,\n      {\n        ...props,\n        ...transformListToObj($$attributes),\n        style: finalStyle,\n      },\n      ...child\n    );\n  };\n  return res;\n}, {} as Record<string, (props: any) => React.ReactNode>);\n\nconst CBlock = ({ children, width, height, $$attributes = [], ...props }: any) => {\n  let child = children;\n  if (!Array.isArray(children)) {\n    child = [children];\n  }\n  child = child.filter((el: any) => el !== undefined);\n  const { style = {}, ...attributes } = transformListToObj($$attributes);\n  const finalStyle = {\n    height,\n    width,\n    boxSizing: 'border-box',\n    ...style,\n    ...(props.style || {}),\n  };\n  return React.createElement(\n    'div',\n    {\n      ...props,\n      ...attributes,\n      style: finalStyle,\n    },\n    ...child\n  );\n};\n\nconst CCanvas = ({ afterMount, beforeDestroy, $$attributes = [], ...props }: any) => {\n  useEffect(() => {\n    afterMount?.(props);\n    return () => {\n      beforeDestroy?.(props);\n    };\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n  const finalStyle = {\n    boxSizing: 'border-box',\n    ...(props.style || {}),\n  };\n  return React.createElement('canvas', {\n    ...props,\n    ...transformListToObj($$attributes),\n    style: finalStyle,\n  });\n};\n\nconst CImage = ({ children, $$attributes = [], ...props }: any) => {\n  let child = children;\n  if (!Array.isArray(children)) {\n    child = [children];\n  }\n\n  const finalStyle = {\n    boxSizing: 'border-box',\n    ...(props.style || {}),\n  };\n  return React.createElement(\n    'img',\n    {\n      ...props,\n      ...transformListToObj($$attributes),\n      style: finalStyle,\n    },\n    ...child\n  );\n};\n\nconst CVideo = ({ children, $$attributes = [], ...props }: any) => {\n  let child = children;\n  if (!Array.isArray(children)) {\n    child = [children];\n  }\n  const finalStyle = {\n    boxSizing: 'border-box',\n    ...(props.style || {}),\n  };\n  return React.createElement(\n    'video',\n    {\n      ...props,\n      ...transformListToObj($$attributes),\n      style: finalStyle,\n    },\n    ...child\n  );\n};\n\nconst CAudio = ({ children, $$attributes = [], ...props }: any) => {\n  let child = children;\n  if (!Array.isArray(children)) {\n    child = [children];\n  }\n  const finalStyle = {\n    boxSizing: 'border-box',\n    ...(props.style || {}),\n  };\n  return React.createElement(\n    'audio',\n    {\n      ...props,\n      ...transformListToObj($$attributes),\n      style: finalStyle,\n    },\n    ...child\n  );\n};\n\nconst CText = ({ $$attributes = [], content, ...props }: any) => {\n  const finalStyle = {\n    boxSizing: 'border-box',\n    ...(props.style || {}),\n  };\n  return React.createElement(\n    'span',\n    {\n      ...props,\n      ...transformListToObj($$attributes),\n      style: finalStyle,\n    },\n    content\n  );\n};\n\nconst CContainer = ({ children, $$attributes = [], afterMount, beforeDestroy, style, ...props }: any) => {\n  let child = children;\n  if (!Array.isArray(children)) {\n    child = [children];\n  }\n  useEffect(() => {\n    afterMount?.(props);\n    return () => {\n      beforeDestroy?.(props);\n    };\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n\n  const finalStyle = {\n    boxSizing: 'border-box',\n    ...(style || {}),\n  };\n\n  return React.createElement(\n    'div',\n    {\n      ...props,\n      ...transformListToObj($$attributes),\n      style: finalStyle,\n    },\n    ...child\n  );\n};\n\nconst CNativeTag = ({ children, $$attributes = [], htmlTag = 'div', ...props }: any) => {\n  let child = children;\n  if (!Array.isArray(children)) {\n    child = [children];\n  }\n  const { style = {}, ...attributes } = transformListToObj($$attributes);\n\n  const finalStyle = {\n    boxSizing: 'border-box',\n    ...style,\n    ...(props.style || {}),\n  };\n  return React.createElement(\n    htmlTag,\n    {\n      ...props,\n      ...attributes,\n      style: finalStyle,\n    },\n    ...child\n  );\n};\n\n// 内置物料组件\nexport const InnerComponent = {\n  RootContainer: ({ children }: any) => {\n    return children;\n  },\n  ...htmlNativeComponents,\n  CContainer,\n  CVideo,\n  CAudio,\n  CBlock,\n  CImage,\n  CText,\n  CCanvas,\n  CNativeTag,\n};\n"
  },
  {
    "path": "packages/render/src/const/index.ts",
    "content": "// 动态运行时组件标记\nexport const DYNAMIC_COMPONENT_TYPE = 'DYNAMIC';\n\n// 运行时辅助变量，不能传递给原始渲染组件，需在在最终渲染阶段剔除\nexport const InnerPropList = ['$$context', '$$nodeModel'];\n\n/** 内部事件，组件渲染之后 */\nexport const ON_DID_RENDER = 'ON_DID_RENDER';\n\n/** 组件销毁之前 */\nexport const ON_WILL_DESTROY = 'ON_WILL_DESTROY';\n\nexport const INNER_EVENT_LIST = [ON_DID_RENDER, ON_WILL_DESTROY];\n"
  },
  {
    "path": "packages/render/src/core/ReactAdapter/buildComponent.ts",
    "content": "import { CNode, CRootNode, isNodeModel } from '@chamn/model';\nimport { DYNAMIC_COMPONENT_TYPE } from '../../const';\nimport { ContextType } from '../adapter';\nimport { renderComponent } from './help';\nimport { convertModelToComponent } from './convertModelToComponent';\n\nimport { TRenderBaseOption } from './type';\n\n// 递归建页面组件结构\nexport const buildComponent = (\n  node: CNode | CRootNode | string,\n  option: {\n    $$context: ContextType;\n    idx?: number;\n  } & TRenderBaseOption\n) => {\n  const { runtimeComponentCache, $$context = {}, getComponent, renderMode } = option;\n  const { $$context: _, idx: _2, ...commonOptions } = option;\n  if (typeof node === 'string') {\n    return renderComponent(node);\n  }\n\n  if (!isNodeModel(node)) {\n    return;\n  }\n  const handleNode = ({ currentNode }: { currentNode: CRootNode | CNode }) => {\n    const nodeId = currentNode.value.id;\n    let component = null;\n    if (runtimeComponentCache.get(nodeId)) {\n      const cacheInfo = runtimeComponentCache.get(nodeId);\n      component = cacheInfo?.component;\n    } else {\n      const originalComponent = getComponent(currentNode);\n      component = convertModelToComponent(originalComponent, currentNode, {\n        ...commonOptions,\n      });\n\n      // cache runtime component\n      if (!runtimeComponentCache.get(nodeId) && renderMode !== 'design') {\n        runtimeComponentCache.set(nodeId, {\n          component: component,\n        });\n      }\n    }\n\n    const key = `${nodeId}-${DYNAMIC_COMPONENT_TYPE}`;\n    const props: Record<string, any> = {\n      $$context,\n      $$nodeModel: node,\n      key: key,\n    };\n\n    return renderComponent(component, props);\n  };\n\n  return handleNode({\n    currentNode: node,\n  });\n};\n"
  },
  {
    "path": "packages/render/src/core/ReactAdapter/convertModelToComponent.ts",
    "content": "import {\n  CNode,\n  CRootNode,\n  ENGEnvEnum,\n  getRandomStr,\n  InnerComponentNameEnum,\n  isExpression,\n  JSExpressionPropType,\n} from '@chamn/model';\nimport { ContextType } from '../adapter';\nimport React from 'react';\nimport { DYNAMIC_COMPONENT_TYPE, INNER_EVENT_LIST, ON_DID_RENDER, ON_WILL_DESTROY } from '../../const';\nimport { StoreApi } from 'zustand';\n\nimport {\n  formatSourceStylePropertyName,\n  getInheritObj,\n  getMatchVal,\n  getNodeCssClassName,\n  getObjFromArrayMap,\n  runExpression,\n} from '../../util';\nimport { collectSpecialProps, getContext, renderComponent } from './help';\nimport { transformProps } from './transformProps';\nimport { buildComponent } from './buildComponent';\nimport { IDynamicComponent, TRenderBaseOption } from './type';\nimport { transformActionNode } from './transformProps/actionNode';\nimport { isEqual } from 'lodash-es';\n\ntype PropsType = {\n  $$context: ContextType;\n  $$nodeModel: CNode | CRootNode;\n};\n\nexport const convertModelToComponent = (\n  originalComponent: any,\n  nodeModel: CNode | CRootNode,\n  options: TRenderBaseOption\n) => {\n  const {\n    storeManager,\n    variableManager,\n    onGetRef,\n    onComponentMount,\n    onComponentDestroy,\n    refManager,\n    processNodeConfigHook,\n    requestAPI,\n    doc,\n  } = options;\n  const { ...commonRenderOptions } = options;\n\n  class DynamicComponent extends React.Component<PropsType> implements IDynamicComponent {\n    static __CP_TYPE__ = DYNAMIC_COMPONENT_TYPE;\n    _CONDITION = true;\n    _DESIGN_BOX = false;\n    _NODE_MODEL = nodeModel;\n    _NODE_ID = nodeModel.id;\n\n    UNIQUE_ID = `${nodeModel.id}_${getRandomStr()}`;\n    targetComponentRef: React.MutableRefObject<any>;\n    listenerHandler: (() => void)[] = [];\n    storeState: StoreApi<any>;\n    /** 存储清理 store 的监听函数 */\n    storeListenDisposeList: (() => void)[] = [];\n    /** save dom and media css */\n    domHeader: HTMLHeadElement | undefined;\n    mediaStyleDomMap: Record<string, HTMLStyleElement> = {};\n    /** 存储当前节点的一些变量和方法，不具有响应性 */\n    variableSpace!: {\n      staticVar: Record<any, any>;\n      methods: Record<any, (...args: any) => any>;\n    };\n    nodeName: any;\n    // 是否已挂载\n    isMounted = false;\n\n    constructor(props: PropsType) {\n      super(props);\n      this.targetComponentRef = React.createRef();\n      this.listenerHandler = [];\n      this.state = nodeModel.value.state || {};\n      let nodeName = nodeModel.value.nodeName || nodeModel.id;\n      // 根节点的 node name 强制为 globalState\n      if (nodeModel.value.componentName === InnerComponentNameEnum.ROOT_CONTAINER) {\n        nodeName = 'globalState';\n      }\n      this.nodeName = nodeName;\n      const nodeStore = storeManager.getStore(nodeName);\n      if (!nodeStore) {\n        // add to global store manager\n        this.storeState = storeManager.addStore(nodeName, () => {\n          return {\n            ...(nodeModel.value.state || {}),\n          };\n        });\n      } else {\n        this.storeState = nodeStore;\n        nodeStore.setState({\n          ...(nodeModel.value.state || {}),\n        });\n      }\n\n      // sync storeState to component state;\n      const subscriber = this.storeState.subscribe((newState) => {\n        this.setStateIfChanged(newState);\n      });\n      this.storeListenDisposeList.push(subscriber);\n      this.connectStore();\n\n      // create node variable space, 存储节点的变量空间\n      const variableSpace = variableManager.get(nodeName);\n      if (variableSpace) {\n        this.variableSpace = variableSpace;\n      } else {\n        this.variableSpace = {\n          staticVar: {},\n          methods: {},\n        };\n        variableManager.add(nodeName, this.variableSpace);\n      }\n    }\n\n    updateState = (newState: any) => {\n      if (!this.isMounted) {\n        // console.warn('component not mounted, updateState failed');\n        return;\n      }\n      // 根节点的 node name 强制为 globalState\n      if (nodeModel.value.componentName === InnerComponentNameEnum.ROOT_CONTAINER) {\n        this.setState(newState);\n      } else {\n        this.storeState.setState(newState);\n      }\n    };\n\n    /** 如果值和当前 state 一样了就不触发更新 */\n    setStateIfChanged(updater: any) {\n      if (!this.isMounted) {\n        // console.warn('component not mounted, setStateIfChanged failed');\n        return;\n      }\n      // 如果是根节点就不更新 根节点的state, 避免触发全局更新\n      if (nodeModel.value.componentName === InnerComponentNameEnum.ROOT_CONTAINER) {\n        return;\n      }\n\n      this.setState((prevState) => {\n        const nextState = typeof updater === 'function' ? updater(prevState) : updater;\n        if (!isEqual(prevState, { ...prevState, ...nextState })) {\n          return nextState;\n        }\n        return null;\n      });\n    }\n\n    connectStore() {\n      // props\n      const expressionList = collectSpecialProps(nodeModel.props, (val) => {\n        if (isExpression(val)) {\n          return true;\n        } else {\n          return false;\n        }\n      });\n\n      const cssAndClassExpressionList = collectSpecialProps(\n        {\n          css: nodeModel.value.css,\n          class: nodeModel.value.classNames,\n        },\n        (val) => {\n          if (isExpression(val)) {\n            return true;\n          } else {\n            return false;\n          }\n        }\n      );\n\n      let storeNameList: string[] = [];\n      [...expressionList, ...cssAndClassExpressionList]\n        .map((el) => {\n          const targetVal: JSExpressionPropType = el.val;\n          const regArr = [\n            // 匹配 $$context.stateManger.xxx.state\n            /(?<=context\\.stateManager(?:\\.|\\[[\"'])?)\\w+(?=(?:[\"']?\\])?(?:\\.|\\[)?[\"']?state)/gim,\n            // 匹配 $ALL_STATE.xxx;\n            /\\$ALL_STATE(?:\\.|(?:\\[\\s*[\"']))(\\w+)(?=(?:[\"']\\s*\\])?)/gim,\n            // 匹配 getStateObj(\"xxx\")\n            /getStateObj\\([\"']([^\"']+)[\"']\\)/gim,\n          ];\n          const tempList = getMatchVal(targetVal.value, regArr);\n          storeNameList = [...storeNameList, ...tempList];\n          const regex = /\\$CTX\\.globalState|\\$G_STATE/;\n          if (regex.test(targetVal.value)) {\n            storeNameList.push('globalState');\n          }\n        })\n        .filter(Boolean);\n      const uniqueList = Array.from(new Set(storeNameList));\n\n      const disposeList: (() => void)[] = [];\n      if (uniqueList.length) {\n        uniqueList.forEach((storeName) => {\n          if (!storeName) {\n            return;\n          }\n          const store = storeManager.getStore(storeName);\n          if (!store) {\n            storeManager.addStore(storeName, () => {\n              return {};\n            });\n            console.warn(storeManager, storeName, 'not exits, auto create');\n          }\n          const handle = storeManager.connect(storeName, (newState) => {\n            this.setStateIfChanged(newState);\n          });\n          disposeList.push(handle);\n        });\n      }\n      this.storeListenDisposeList = disposeList;\n    }\n\n    getStyleDomById = (id: string) => {\n      const mediaStyleDomMap = this.mediaStyleDomMap;\n      let styleEl = mediaStyleDomMap[id];\n      if (!styleEl) {\n        styleEl = doc.createElement('style');\n        mediaStyleDomMap[id] = styleEl;\n      }\n      styleEl.id = id;\n      return styleEl;\n    };\n\n    addMediaCSS = () => {\n      let header = this.domHeader;\n      if (!header) {\n        header = doc.getElementsByTagName('head')?.[0];\n        this.domHeader = header;\n      }\n      if (!this.domHeader) {\n        console.warn('not found header dom');\n        return;\n      }\n      const css = this._NODE_MODEL.value.css;\n      if (!css) {\n        return;\n      }\n      css.value.forEach((el) => {\n        const normalId = `${this.UNIQUE_ID}_${el.state}`;\n\n        let className = getNodeCssClassName(this._NODE_MODEL);\n        if (el.state !== 'normal') {\n          className = `${className}:${el.state}`;\n        }\n        if (el.text) {\n          const styleEl = this.getStyleDomById(normalId);\n          styleEl.innerText = `.${className} { ${el.text} }`;\n          header?.appendChild(styleEl);\n        }\n\n        if (el.media?.length) {\n          el.media.forEach((it) => {\n            const mediaId = `${normalId}_${it.type}_${it.value}`;\n            const styleDom = this.getStyleDomById(mediaId);\n            styleDom.media = `screen and (${it.type}:${it.value}px)`;\n            styleDom.innerHTML = `.${className} { ${it.text} }`;\n            header?.appendChild(styleDom);\n          });\n        }\n      });\n    };\n\n    removeMediaCSS = () => {\n      const mediaStyleDomMap = this.mediaStyleDomMap;\n      Object.keys(mediaStyleDomMap).forEach((key) => {\n        this.domHeader?.removeChild(mediaStyleDomMap[key]);\n      });\n      this.mediaStyleDomMap = {};\n    };\n\n    rebuildNode = () => {\n      this.storeListenDisposeList.forEach((el) => el());\n      this.removeMediaCSS();\n      this.connectStore();\n      this.addMediaCSS();\n      this.forceUpdate();\n    };\n\n    componentDidMount(): void {\n      this.isMounted = true;\n      this.addMediaCSS();\n      onGetRef?.(this.targetComponentRef, nodeModel, this as any);\n      onComponentMount?.(this, nodeModel);\n\n      // 注册初始化事件\n      const onDidRenderFlow = nodeModel.value.eventListener?.find((el) => el.name === ON_DID_RENDER);\n      if (onDidRenderFlow) {\n        const func = transformActionNode(onDidRenderFlow.func, {\n          context: this.createCurrentNodeCtx(),\n          storeManager: storeManager,\n          actionVariableSpace: {},\n          nodeModel: nodeModel as any,\n        });\n        func();\n      }\n\n      // 需要 debounce\n      const customForceUpdate = () => {\n        if (!this.isMounted) {\n          // console.warn('component not mounted, forceUpdate failed');\n          return;\n        }\n        // nodeName maybe changed\n        storeManager.setStore(nodeModel.value.nodeName || nodeModel.id, this.storeState);\n        this.storeState.setState({\n          ...this.state,\n          ...(nodeModel.value.state || {}),\n        });\n        this.rebuildNode();\n      };\n      // 设计模式使用, 监听节点属性变化，重新构建该节点下的所有组件\n      nodeModel.onChange(customForceUpdate);\n    }\n\n    componentWillUnmount(): void {\n      this.storeListenDisposeList.forEach((el) => el());\n      this.removeMediaCSS();\n\n      onComponentDestroy?.(this, nodeModel);\n      const EVENT_NAME = ON_WILL_DESTROY;\n      const actionFlow = nodeModel.value.eventListener?.find((el) => el.name === EVENT_NAME);\n      if (actionFlow) {\n        const func = transformActionNode(actionFlow.func, {\n          context: this.createCurrentNodeCtx(),\n          storeManager: storeManager,\n          actionVariableSpace: {},\n          nodeModel: nodeModel as any,\n        });\n        func();\n      }\n    }\n\n    /** 转换节点的 methods */\n    transformMethods(option: { context: ContextType }) {\n      const { context: newContext } = option;\n      // 需要优先处理处理 methods， methods 内部不能调用 methods 上的方法, 转换为可执行的方法\n      const methodsObj = transformProps(\n        {\n          methods: nodeModel.value.methods,\n        },\n        {\n          $$context: newContext,\n          ...commonRenderOptions,\n          nodeModel: nodeModel as any,\n        }\n      );\n      const originalMethods = nodeModel.value.methods || [];\n      const methodList = methodsObj.methods as ((...args: any) => void)[];\n      const methodMap = originalMethods.reduce((res, item, index) => {\n        res[item.name!] = methodList[index];\n        return res;\n      }, {} as any);\n      newContext.methods = methodMap;\n      this.variableSpace.methods = Object.assign(this.variableSpace.methods, methodMap);\n    }\n\n    /** 处理根节点的 context */\n    processRootContext(context: ContextType) {\n      if (nodeModel.value.componentName === InnerComponentNameEnum.ROOT_CONTAINER) {\n        context.globalState = this.state;\n        context.updateGlobalState = this.updateState;\n        context.requestAPI = requestAPI;\n        context.getGlobalState = () => {\n          return this.state;\n        };\n      }\n    }\n\n    processNodeClassName(className: string, context: ContextType) {\n      // 处理 className\n      const classNames =\n        nodeModel.value.classNames?.map((it) => {\n          const name = it.name;\n          const status = isExpression(it.status)\n            ? runExpression(it.status?.value || '', {\n                nodeContext: context,\n                nodeModel: nodeModel as any,\n                storeManager: storeManager,\n              })\n            : false;\n          if (status) {\n            return name;\n          }\n          return '';\n        }) || [];\n\n      let finalClsx = `${className ?? ''} ${classNames.join(' ')}`.trim();\n      if (nodeModel.value.css) {\n        // 每个节点添加一个 表示节点唯一的 className, 使用 node.id\n        const nodeClassName = getNodeCssClassName(nodeModel);\n        const className = `${nodeClassName} ${finalClsx}`.trim();\n        finalClsx = className;\n      }\n      return finalClsx;\n    }\n\n    processNodeStyle(newContext: ContextType) {\n      if (!nodeModel.value.style) {\n        return {};\n      }\n      const newStyle: Record<string, any> = transformProps(\n        { style: nodeModel.value.style },\n        {\n          $$context: newContext,\n          ...commonRenderOptions,\n          nodeModel: nodeModel as any,\n        }\n      );\n      // font-size to fontSize\n      return formatSourceStylePropertyName(newStyle.style || []);\n    }\n\n    processNodeChild(children: any, newContext: ContextType) {\n      let newChildren: React.ReactNode[] = [];\n      if (children !== undefined) {\n        // 优先使用 props 中的 children\n        newChildren = Array.isArray(children) ? children : [children];\n      } else {\n        const children: React.ReactNode[] = [];\n        const childModel = nodeModel.value.children;\n        childModel.forEach((node, index) => {\n          const child = buildComponent(node, {\n            $$context: newContext,\n            idx: index,\n            ...commonRenderOptions,\n          });\n          children.push(child);\n        });\n        newChildren = children;\n      }\n\n      return newChildren;\n    }\n\n    processNodeConditionAndConfigHook(newProps: any, newChildren: any, newContext: ContextType) {\n      let condition = nodeModel.value.condition ?? true;\n      if (typeof condition !== 'boolean') {\n        const conditionObj = condition as JSExpressionPropType;\n        condition = runExpression(conditionObj.value, {\n          nodeContext: newContext || {},\n          nodeModel: nodeModel as any,\n          storeManager: storeManager,\n        }) as boolean;\n      }\n      let finalNodeConfig = {\n        condition,\n        props: newProps,\n      };\n      if (processNodeConfigHook) {\n        finalNodeConfig = processNodeConfigHook(finalNodeConfig, nodeModel as CNode);\n      }\n      const renderView = renderComponent(originalComponent, finalNodeConfig.props, ...newChildren);\n\n      this._CONDITION = finalNodeConfig.condition as boolean;\n      if (!finalNodeConfig.condition) {\n        return React.createElement(\n          'div',\n          {\n            key: newProps.key,\n            style: {\n              display: 'none',\n            },\n          },\n          renderView\n        );\n      }\n      return renderView;\n    }\n\n    processNodeEventListener(newContext: ContextType) {\n      const eventListener = nodeModel.value.eventListener;\n      const res: any = {};\n      eventListener?.forEach((event) => {\n        /** 内部事件略过 */\n        if (INNER_EVENT_LIST.includes(event.name)) {\n          return;\n        }\n        const func = transformActionNode(event.func, {\n          context: newContext,\n          storeManager: storeManager,\n          actionVariableSpace: {},\n          nodeModel: nodeModel as any,\n        });\n        res[event.name] = func;\n      });\n      return res;\n    }\n\n    createCurrentNodeCtx() {\n      const { $$context } = this.props;\n      const nodeName = nodeModel.value.nodeName || nodeModel.id;\n      const nodeId = nodeModel.id;\n\n      const tempContext: ContextType = {\n        state: this.state || {},\n        staticVar: this.variableSpace.staticVar,\n        updateState: this.updateState,\n        storeManager: storeManager,\n        getState: () => storeManager.getStateObj(nodeName),\n        getStateObj: () => storeManager.getStateObj(nodeName),\n        getStateObjById: (nodeId: string) => storeManager.getStateObj(nodeId),\n        stateManager: storeManager.getStateSnapshot(),\n        getMethods: () => {\n          const methods = variableManager.get(nodeId).methods;\n          const nodeRef = refManager.get(nodeId).current;\n          const obj = getInheritObj(methods, nodeRef);\n\n          return obj;\n        },\n        getMethodsById: (nodeId: string) => {\n          const methods = variableManager.get(nodeId).methods;\n          const nodeRef = refManager.get(nodeId).current;\n          const obj = getInheritObj(methods, nodeRef);\n          return obj;\n        },\n        getStaticVar: () => {\n          return variableManager.get(nodeName).staticVar;\n        },\n        getStaticVarById: (nodeId: string) => {\n          return variableManager.get(nodeId).staticVar;\n        },\n        /** 获取当前节点的事件函数 */\n        callEventMethod: (eventName: string, e: any) => {\n          const nodeRef = refManager.get(nodeId).current;\n          nodeRef?.props[eventName]?.(e);\n        },\n        /** 获取当前 node 的 props  */\n        getProps: () => {\n          const nodeRef = refManager.get(nodeId).current;\n          return nodeRef?.props;\n        },\n        nodeRefs: $$context.nodeRefs,\n      };\n\n      // 根节点 context 需要注入额外的变量\n      this.processRootContext(tempContext);\n\n      const newContext = getContext(tempContext, $$context);\n      return newContext;\n    }\n\n    injectEngEnv() {\n      const res: any = {};\n      const injectEnvList = nodeModel.value.injectEnvList || [];\n      if (Array.isArray(nodeModel.value.injectEnvList)) {\n        const map = {\n          [ENGEnvEnum.COMPONENTS]: options.components,\n        };\n        injectEnvList.reduce((result, el) => {\n          result[el] = map[el];\n          return result;\n        }, res);\n      }\n\n      return res;\n    }\n\n    renderCore(): React.ReactNode {\n      const { $$context: _, ...props } = this.props;\n      const newOriginalProps = {\n        key: nodeModel.id,\n        ...nodeModel.props,\n        ...props,\n      };\n\n      const newContext = this.createCurrentNodeCtx();\n\n      this.transformMethods({ context: newContext });\n\n      // handle props\n      let newProps: Record<string, any> = transformProps(newOriginalProps, {\n        $$context: newContext,\n        ...commonRenderOptions,\n        nodeModel: nodeModel as any,\n      });\n\n      // 处理特殊的组件，需要注入 eng 的上下文变量\n      const engEnvProps = this.injectEngEnv();\n      Object.assign(newProps, engEnvProps);\n\n      // 处理 className\n      const finalClsx = this.processNodeClassName(newProps.className, newContext);\n      newProps.className = finalClsx;\n\n      // 处理 style\n      const newStyle: Record<string, any> = this.processNodeStyle(newContext);\n      newProps.style = {\n        ...(newProps?.style || {}),\n        ...(newStyle || {}),\n      };\n\n      // handle children\n      const { children } = newProps;\n      delete newProps.children;\n      const newChildren = this.processNodeChild(children, newContext);\n      // handle children end\n\n      // 处理 ref\n      newProps.ref = this.targetComponentRef;\n      // 处理 eventListener 事件监听\n      if (nodeModel.value.eventListener) {\n        const eventListenerObj = this.processNodeEventListener(newContext);\n        newProps = {\n          ...newProps,\n          ...eventListenerObj,\n        };\n      }\n      const renderView = this.processNodeConditionAndConfigHook(newProps, newChildren, newContext);\n      return renderView;\n      // 可能能复用 end\n    }\n\n    renderLoop(): React.ReactNode {\n      const { $$context: _, ...props } = this.props;\n      const newOriginalProps = {\n        key: nodeModel.id,\n        ...nodeModel.props,\n        ...props,\n      };\n\n      const newContext = this.createCurrentNodeCtx();\n\n      // 需要优先处理处理 methods， methods 内部不能调用 methods 上的方法, 转换为可执行的方法\n      this.transformMethods({ context: newContext });\n\n      // 处理循环\n      const loopObj = nodeModel.value.loop;\n      let loopRes: any[] = [];\n      if (loopObj && loopObj.open) {\n        this.targetComponentRef.current = [];\n        let loopList: any[] = (loopObj.data as any[]) || [];\n        if (isExpression(loopObj.data)) {\n          const expProp = loopObj.data as JSExpressionPropType;\n          loopList =\n            runExpression(expProp.value, {\n              nodeContext: newContext || {},\n              storeManager: storeManager,\n              nodeModel: nodeModel as any,\n            }) || [];\n        }\n        loopRes = loopList.map((...args) => {\n          const innerIndex = args[1];\n          const argsName = [loopObj.forName || 'item', loopObj.forIndex || 'index'];\n          const loopData = getObjFromArrayMap(args, argsName);\n          let loopDataName = 'loopData';\n          // loopDataName: loopData or loopData${xxx}, xxx is capitalize\n          if (loopObj.name) {\n            loopDataName = `${loopDataName}${loopObj.name}`;\n          }\n          const loopContext = getContext(\n            {\n              [loopDataName]: loopData,\n              nodeRefs: newContext.nodeRefs,\n              // 使用空函数，避免获取到父节点的方法或者函数\n              getProps: function (): void {},\n              callEventMethod: function (): void {},\n            },\n            newContext\n          );\n          // handle props\n          let newProps: Record<string, any> = transformProps(newOriginalProps, {\n            $$context: loopContext,\n            ...commonRenderOptions,\n            nodeModel: nodeModel as any,\n          });\n\n          // 处理特殊的组件，需要注入 eng 的上下文变量\n          const engEnvProps = this.injectEngEnv();\n          Object.assign(newProps, engEnvProps);\n\n          // 处理 className\n          const finalClsx = this.processNodeClassName(newProps.className, loopContext);\n          newProps.className = finalClsx;\n\n          // 处理 style\n          const newStyle: Record<string, any> = this.processNodeStyle(loopContext);\n          newProps.style = {\n            ...(newProps?.style || {}),\n            ...(newStyle || {}),\n          };\n\n          // handle children\n          const { children } = newProps;\n          delete newProps.children;\n          const newChildren = this.processNodeChild(children, loopContext);\n          // handle children end\n\n          // loop 渲染特有的 key\n          newProps.key = `${newProps.key}-${innerIndex}`;\n          if (isExpression(loopObj.key)) {\n            const keyObj = loopObj.key as JSExpressionPropType;\n            const specialKey = runExpression(keyObj.value, {\n              nodeContext: loopContext || {},\n              storeManager: storeManager,\n              nodeModel: nodeModel as any,\n            });\n            newProps.key += `-${specialKey}`;\n          }\n          // loop 渲染特有的 key End\n\n          // 处理 ref\n          newProps.ref = (ref: any) => {\n            this.targetComponentRef.current = this.targetComponentRef.current || [];\n            this.targetComponentRef.current[innerIndex] = ref;\n          };\n\n          // 处理 eventListener 事件监听\n          if (nodeModel.value.eventListener) {\n            const eventListenerObj = this.processNodeEventListener(loopContext);\n            newProps = {\n              ...newProps,\n              ...eventListenerObj,\n            };\n          }\n\n          const renderView = this.processNodeConditionAndConfigHook(newProps, newChildren, loopContext);\n\n          return renderView;\n        });\n\n        // 结束循环渲染\n        return loopRes;\n      }\n      // 处理循环结束\n    }\n\n    render(): React.ReactNode {\n      const loopObj = nodeModel.value.loop;\n      if (loopObj && loopObj.open) {\n        return this.renderLoop();\n      } else {\n        return this.renderCore();\n      }\n    }\n  }\n\n  (DynamicComponent as any).displayName = `${nodeModel.value.componentName}Dynamic`;\n\n  return DynamicComponent;\n};\n"
  },
  {
    "path": "packages/render/src/core/ReactAdapter/help.ts",
    "content": "import React from 'react';\nimport { DYNAMIC_COMPONENT_TYPE, InnerPropList } from '../../const';\nimport { ContextType } from '../adapter';\nimport { isPropModel } from '@chamn/model';\nimport { isArray, isPlainObject } from 'lodash-es';\n\nexport const getContext = (data: ContextType, ctx?: ContextType | null): ContextType => {\n  let newCtx: ContextType = data;\n  if (ctx) {\n    newCtx = {\n      ...data,\n    };\n    (newCtx as any).__proto__ = ctx || null;\n  }\n  return newCtx;\n};\n\nexport const renderComponent = (\n  originalComponent: React.ComponentClass<any> | React.FunctionComponent | string,\n  props: Record<any, any> = {},\n  ...children: React.ReactNode[]\n) => {\n  if (typeof originalComponent === 'string' || typeof originalComponent === 'number') {\n    return String(originalComponent);\n  }\n  InnerPropList.forEach((key) => {\n    if (key in props && (originalComponent as any).__CP_TYPE__ !== DYNAMIC_COMPONENT_TYPE) {\n      delete props[key];\n    }\n  });\n  const res = React.createElement(originalComponent, props, ...children);\n  return res;\n};\n\nexport const collectSpecialProps = (\n  originalProps: Record<string, unknown> = {},\n  isValidate: (val: unknown) => boolean\n) => {\n  const res: { keyPath: string[]; val: any }[] = [];\n  const cb = (keyPath: string[], val: Record<string, any>) => {\n    let tempVal: any = val;\n    if (isPropModel(val)) {\n      tempVal = val.value;\n    }\n    if (isValidate(tempVal)) {\n      res.push({\n        keyPath,\n        val: tempVal,\n      });\n    } else if (isArray(tempVal)) {\n      tempVal.forEach((it, index) => {\n        cb([...keyPath, String(index)], it);\n      });\n    } else if (isPlainObject(tempVal)) {\n      Object.keys(tempVal).forEach((key) => {\n        cb([...keyPath, key], tempVal[key]);\n      });\n    }\n  };\n\n  cb(['$root'], originalProps);\n  return res;\n};\n"
  },
  {
    "path": "packages/render/src/core/ReactAdapter/index.ts",
    "content": "import { CNode, CPage, CRootNode } from '@chamn/model';\nimport { AdapterOptionType, getAdapter } from '../adapter';\nimport { canAcceptsRef, compWrapper, findComponentByChainRefer } from '../../util';\nimport { StoreManager } from '../storeManager';\nimport { VariableManager } from '../variableManager';\nimport { RefManager } from '../refManager';\nimport { convertModelToComponent } from './convertModelToComponent';\nimport { renderComponent } from './help';\n\nexport class DefineReactAdapter {\n  renderMode: AdapterOptionType['renderMode'] = 'normal';\n  components: AdapterOptionType['components'] = {};\n  storeManager = new StoreManager();\n  // 存储节点的变量或者方法\n  variableManager = new VariableManager();\n  runtimeComponentCache = new Map<string, { component: any }>();\n  onGetRef?: AdapterOptionType['onGetRef'];\n  onGetComponent: AdapterOptionType['onGetComponent'];\n  onComponentMount: AdapterOptionType['onComponentMount'];\n\n  refManager: RefManager = new RefManager();\n\n  onComponentDestroy: AdapterOptionType['onComponentDestroy'];\n  /**\n   * 处理 props 钩子, 可以统一拦截 node 的处理，并修改其值\n   */\n  processNodeConfigHook?: AdapterOptionType['processNodeConfigHook'];\n\n  /**\n   * 根据 node 获取对应渲染的组件\n   * @param currentNode\n   * @returns\n   */\n  getComponent(currentNode: CNode | CRootNode) {\n    const componentName = currentNode.value.componentName;\n    // support chain find\n    let res: any = findComponentByChainRefer(componentName, this.components);\n    // check component can accept ref\n    if (!canAcceptsRef(res)) {\n      res = compWrapper(res);\n      this.components[componentName] = res;\n    }\n    // 定制钩子\n    if (this.onGetComponent) {\n      res = this.onGetComponent?.(res, currentNode);\n    }\n\n    return res;\n  }\n\n  pageRender(\n    pageModel: CPage,\n    {\n      components,\n      onGetRef,\n      refManager,\n      $$context = {\n        nodeRefs: refManager,\n        // 使用空函数，避免获取到父节点的方法或者函数\n        getProps: function (): void {},\n        callEventMethod: function (): void {},\n      },\n      onGetComponent,\n      onComponentMount,\n      onComponentDestroy,\n      renderMode,\n      processNodeConfigHook,\n      requestAPI,\n      doc,\n    }: AdapterOptionType\n  ) {\n    this.renderMode = renderMode;\n    this.components = components;\n    this.onGetRef = onGetRef;\n    this.onGetComponent = onGetComponent;\n    this.onComponentMount = onComponentMount;\n    this.onComponentDestroy = onComponentDestroy;\n    this.processNodeConfigHook = processNodeConfigHook;\n    this.refManager = refManager;\n    this.requestAPI = requestAPI;\n    //做一些全局 store 操作\n    const rootNode = pageModel.value.componentsTree;\n    const component = this.getComponent(rootNode);\n    const newComp = convertModelToComponent(component, pageModel.value.componentsTree, {\n      storeManager: this.storeManager,\n      variableManager: this.variableManager,\n      runtimeComponentCache: this.runtimeComponentCache,\n      getComponent: this.getComponent.bind(this),\n      refManager: this.refManager,\n      onGetRef: this.onGetRef!,\n      processNodeConfigHook: processNodeConfigHook!,\n      onComponentMount: onComponentMount!,\n      onComponentDestroy: onComponentDestroy!,\n      renderMode: renderMode!,\n      requestAPI: requestAPI,\n      components: this.components,\n      doc: doc,\n    });\n\n    const props: Record<string, any> = {};\n    const propsModel = rootNode.props;\n    Object.keys(propsModel).forEach((key) => {\n      props[key] = propsModel[key].value;\n    });\n    props.$$context = $$context;\n    return renderComponent(newComp, props);\n  }\n\n  /** 请求 API */\n  requestAPI: AdapterOptionType['requestAPI'] = async (params) => {\n    console.log(params);\n    throw new Error('Need to implement requestAPI for Render');\n  };\n\n  clear() {\n    this.runtimeComponentCache.clear();\n    this.storeManager.destroy();\n  }\n}\n\nexport const ReactAdapter = getAdapter(new DefineReactAdapter());\n"
  },
  {
    "path": "packages/render/src/core/ReactAdapter/transformProps/actionNode.ts",
    "content": "import {\n  CNode,\n  isExpression,\n  isFunction,\n  TActionLogicItem,\n  TargetValueNameObject,\n  TDynamicValue,\n  TLogicAssignValueItem,\n  TLogicCallNodeMethodItem,\n  TLogicItemHandlerFlow,\n  TLogicJumpLinkItem,\n  TLogicRequestAPIItem,\n} from '@chamn/model';\nimport { ContextType } from '../../adapter';\nimport { StoreManager } from '../../storeManager';\nimport { convertCodeStringToFunction, runExpression } from '../../../util';\n\ntype CommonOption = {\n  context: ContextType;\n  storeManager: StoreManager;\n  $$response?: any;\n  /** 当前 action 上下文的变量空间 */\n  actionVariableSpace: Record<string, string>;\n  nodeModel: CNode;\n};\n\nexport const transformActionNode = (propVal: TActionLogicItem, options: CommonOption) => {\n  const handler = propVal.handler;\n  const { context, storeManager } = options;\n  /** 初始化 */\n  if (!options.actionVariableSpace) {\n    options.actionVariableSpace = {};\n  }\n\n  return async function (...args: any[]) {\n    const resultMap: any = {};\n\n    const buildNode = async (nodeItem: TLogicItemHandlerFlow[number]) => {\n      const item = nodeItem;\n      if (item.type === 'RUN_CODE') {\n        const codeFunc = convertCodeStringToFunction({\n          funcName: '',\n          funcBody: `function() {\\n${item.value}\\n}`,\n          nodeContext: context,\n          storeManager: storeManager,\n          $$response: options.$$response,\n          actionVariableSpace: options.actionVariableSpace,\n          nodeModel: options.nodeModel,\n        });\n\n        let res;\n        if (options.$$response !== undefined) {\n          res = codeFunc(options.$$response, ...args);\n        } else {\n          res = codeFunc(...args);\n        }\n\n        if (res?.then) {\n          resultMap[item.id] = await res;\n        } else {\n          resultMap[item.id] = res;\n        }\n      }\n\n      if (item.type === 'JUMP_LINK') {\n        const func = buildJumpLink(item, options);\n        const res = func(...args);\n        if (res?.then) {\n          resultMap[item.id] = await res;\n        } else {\n          resultMap[item.id] = res;\n        }\n      }\n\n      if (item.type === 'REQUEST_API') {\n        const { run, afterFailedResponse, afterSuccessResponse } = buildRequestAPI(item, options);\n\n        try {\n          const res = run(...args);\n          if (res?.then) {\n            resultMap[item.id] = await res;\n          } else {\n            resultMap[item.id] = res;\n          }\n\n          /** 写入变量 */\n          if (item.responseVarName) {\n            options.actionVariableSpace[item.responseVarName] = resultMap[item.id];\n          }\n          // 处理后置操作\n          const res2: any = afterSuccessResponse(resultMap[item.id], ...args);\n          if (res2?.then) {\n            return await res2;\n          }\n          return res2;\n        } catch (err) {\n          const resFailed: any = await afterFailedResponse(err);\n          if (resFailed?.then) {\n            return await resFailed;\n          } else {\n            return resFailed;\n          }\n        }\n      }\n\n      if (item.type === 'CALL_NODE_METHOD') {\n        const func = buildCallNodeMethod(item, options);\n        const res = func(...args);\n        if (res?.then) {\n          resultMap[item.id] = await res;\n        } else {\n          resultMap[item.id] = res;\n        }\n        if (item.returnVarName) {\n          options.actionVariableSpace[item.returnVarName] = resultMap[item.id];\n        }\n      }\n      if (item.type === 'ASSIGN_VALUE') {\n        const func = buildAssignValue(item, options);\n        let tempArgs = [...args];\n        if (options.$$response !== undefined) {\n          tempArgs = [options.$$response, tempArgs];\n        }\n        const res = func(...tempArgs);\n        resultMap[item.id] = await res;\n      }\n    };\n    // 从第一节点开始执行逻辑直到，next 为空\n    let nextNode: TLogicItemHandlerFlow[number] | undefined = handler[0];\n    const hasProcessNodeList = new Set();\n    while (nextNode) {\n      if (hasProcessNodeList.has(nextNode.id)) {\n        // 避免死循环\n        return;\n      }\n      await buildNode(nextNode);\n      hasProcessNodeList.add(nextNode.id);\n      const tempNode = handler.find((el) => nextNode?.next && el.id === nextNode?.next);\n      nextNode = tempNode;\n    }\n  };\n};\n\nfunction buildJumpLink(item: TLogicJumpLinkItem, option: CommonOption) {\n  const linkFunc = buildDynamicValue(item.link, option);\n  return linkFunc;\n}\n\nconst buildDynamicValue = (dynamicValue: TDynamicValue, option: CommonOption) => {\n  return function (...args: any[]): any {\n    if (isExpression(dynamicValue)) {\n      const res = runExpression(dynamicValue.value, {\n        ...option.context,\n        nodeContext: option.context,\n        storeManager: option.storeManager,\n        $$response: option.$$response,\n        actionVariableSpace: option.actionVariableSpace,\n        nodeModel: option.nodeModel,\n      });\n      return res;\n    }\n    if (isFunction(dynamicValue)) {\n      const func = convertCodeStringToFunction({\n        funcName: dynamicValue.name || '',\n        funcBody: dynamicValue.value,\n        nodeContext: option.context,\n        storeManager: option.storeManager,\n        $$response: option.$$response,\n        actionVariableSpace: option.actionVariableSpace,\n        nodeModel: option.nodeModel,\n      });\n\n      return func(...args);\n    }\n\n    return dynamicValue;\n  };\n};\n\nconst buildRequestAPI = (item: TLogicRequestAPIItem, option: CommonOption) => {\n  const run = function (...args: any[]): any {\n    let apiPath = '';\n    if (item.apiPath) {\n      apiPath = buildDynamicValue(item.apiPath, option)(...args);\n    }\n\n    const body: any = item.body || {};\n\n    Object.keys(body).forEach((key) => {\n      body[key] = buildDynamicValue(body[key], option)(...args);\n    });\n\n    const query = item.query || {};\n\n    Object.keys(query).forEach((key) => {\n      query[key] = buildDynamicValue(query[key], option)(...args);\n    });\n    const header = item.header || {};\n\n    Object.keys(header).forEach((key) => {\n      header[key] = buildDynamicValue(header[key], option)(...args);\n    });\n\n    const method = item.method || 'GET';\n\n    const res = option.context.requestAPI?.({\n      url: apiPath,\n      method: method as any,\n      header: header,\n      body: body,\n      query: query,\n    });\n    return res;\n  };\n\n  const afterSuccessResponse = async (response: any, ...args: any[]) => {\n    if (!item.afterSuccessResponse) {\n      return response;\n    }\n\n    const allNodeList = [...(item.afterSuccessResponse || []), ...(item.afterFailedResponse || [])];\n\n    const func = transformActionNode(\n      {\n        type: 'ACTION',\n        handler: allNodeList,\n      },\n      {\n        ...option,\n        $$response: response,\n      }\n    );\n\n    return func(...args);\n  };\n\n  const afterFailedResponse = async (response: any, ...args: any[]) => {\n    if (!item.afterFailedResponse) {\n      return response;\n    }\n    // 请求或者成功的的同级逻辑可以复用，不允许死循环\n    const allNodeList = [...(item.afterFailedResponse || []), ...(item.afterSuccessResponse || [])];\n\n    const func = transformActionNode(\n      {\n        type: 'ACTION',\n        handler: allNodeList,\n      },\n      {\n        ...option,\n        $$response: response,\n      }\n    );\n\n    return func(...args);\n  };\n\n  return {\n    run: run,\n    afterSuccessResponse,\n    afterFailedResponse,\n  };\n};\n\nconst buildCallNodeMethod = (item: TLogicCallNodeMethodItem, option: CommonOption) => {\n  return (...args: any[]): any => {\n    const codeFunc = convertCodeStringToFunction({\n      funcName: '',\n      funcBody: `\n      function () {\n        var args  = arguments;\n        var nodeRef = $$context.nodeRefs.get(${JSON.stringify(item.nodeId)});\n        if(nodeRef && nodeRef.current) {\n          var func = nodeRef.current[${JSON.stringify(item.methodName)}]?.bind(nodeRef.current);\n          var isDev = Boolean(nodeRef.current?.getTargetComponentRef);\n          if (isDev) {\n            const ins = nodeRef.current.getTargetComponentRef()?.current;\n            func = ins?.[${JSON.stringify(item.methodName)}]?.bind(ins);\n          }\n          if (func) {\n            func.apply(null, args);\n          }\n        }\n      }`,\n      nodeContext: option.context,\n      storeManager: option.storeManager,\n      $$response: option.$$response,\n      nodeModel: option.nodeModel,\n    });\n\n    let customArgs = args;\n    if (Array.isArray(item.args)) {\n      customArgs = item.args.map((el) => {\n        const tempArgs = [option.$$response, ...args].filter(Boolean);\n        return buildDynamicValue(el, option)(...tempArgs);\n      });\n    }\n\n    return codeFunc(...customArgs);\n  };\n};\n\nconst buildAssignValue = (item: TLogicAssignValueItem, option: CommonOption) => {\n  return async (...args: any[]): Promise<any> => {\n    const valueFunc = buildDynamicValue(item.currentValue, option);\n    const res = valueFunc(...args);\n    let result;\n    if (res?.then) {\n      result = await res;\n    } else {\n      result = res;\n    }\n    if (item.valueType === 'STATE') {\n      const targetValueName = item.targetValueName as TargetValueNameObject;\n      const store = option.storeManager.getStore(targetValueName.nodeId);\n      if (store) {\n        store.setState({\n          [targetValueName.keyPath]: result,\n        });\n      }\n    } else if (item.valueType === 'MEMORY') {\n      option.actionVariableSpace[item.targetValueName as string] = result;\n    }\n\n    return result;\n  };\n};\n"
  },
  {
    "path": "packages/render/src/core/ReactAdapter/transformProps/index.ts",
    "content": "import {\n  CPropDataType,\n  isPropModel,\n  isSlotModel,\n  CNode,\n  isExpression,\n  FunctionPropType,\n  CProp,\n  isFunction,\n  isAction,\n} from '@chamn/model';\nimport { isPlainObject } from 'lodash-es';\nimport React from 'react';\nimport { DYNAMIC_COMPONENT_TYPE } from '../../../const';\nimport { getObjFromArrayMap, shouldConstruct, runExpression, convertCodeStringToFunction } from '../../../util';\nimport { ContextType } from '../../adapter';\nimport { getContext, renderComponent } from '../help';\nimport { convertModelToComponent } from '../convertModelToComponent';\nimport { TRenderBaseOption } from '../type';\nimport { transformActionNode } from './actionNode';\n\nexport const transformProps = (\n  originalProps: Record<string, any> = {},\n  option: {\n    $$context: ContextType;\n    nodeModel: CNode;\n  } & TRenderBaseOption\n) => {\n  const { $$context: parentContext, getComponent, storeManager } = option;\n  const propsModel = originalProps;\n  const handlePropVal: (propVal: CPropDataType) => Record<string, any> = (propVal: CPropDataType) => {\n    if (Array.isArray(propVal)) {\n      return propVal.map((it) => handlePropVal(it));\n    } else if (isPropModel(propVal)) {\n      return handlePropVal(propVal.value);\n    } else if (isSlotModel(propVal)) {\n      const slotProp = propVal.value;\n      const tempVal = slotProp.value;\n      if (!tempVal) {\n        console.warn('slot value is null, this maybe cause some error, pls check it', originalProps);\n        return () => {};\n      }\n      const handleSingleSlot = (it: CNode) => {\n        const key = `${it.id}-${DYNAMIC_COMPONENT_TYPE}`;\n\n        const component = getComponent(it);\n        const PropNodeRender = convertModelToComponent(component, it, {\n          ...option,\n        });\n        const parmaList = slotProp.params || [];\n        // 运行时组件函数\n        const PropNodeFuncWrap = (...args: any) => {\n          const params: Record<any, any> = getObjFromArrayMap(args, parmaList);\n          const $$context = getContext(\n            {\n              params,\n              nodeRefs: parentContext.nodeRefs,\n              // 使用空函数，避免获取到父节点的方法或者函数\n              getProps: function (): void {},\n              callEventMethod: function (): void {},\n            },\n            parentContext\n          );\n          return renderComponent(PropNodeRender, {\n            $$context,\n            key,\n          });\n        };\n        const res = {\n          component: PropNodeFuncWrap,\n          key,\n        };\n        return res;\n      };\n      if (Array.isArray(tempVal)) {\n        const renderList = tempVal?.map((it: any) => {\n          return handleSingleSlot(it);\n        });\n        return (...args: any[]) => {\n          return renderList.map((renderItem) => {\n            const isClassComponent = shouldConstruct(renderItem.component);\n\n            if (isClassComponent) {\n              const comp = renderItem.component as any;\n              return React.createElement(comp, {\n                $$context: parentContext,\n                key: renderItem.key,\n              });\n            } else {\n              const comp = renderItem.component as any;\n              return comp?.(...args);\n            }\n          });\n        };\n      } else {\n        return handleSingleSlot(tempVal).component;\n      }\n    } else if (isExpression(propVal)) {\n      const expProp = propVal;\n      const newVal = runExpression(expProp.value, {\n        nodeContext: parentContext,\n        storeManager: option.storeManager,\n        nodeModel: option.nodeModel,\n      });\n      return newVal;\n    } else if (isFunction(propVal)) {\n      const funcProp = propVal as FunctionPropType;\n      return convertCodeStringToFunction({\n        funcBody: funcProp.value,\n        funcName: funcProp.name || '',\n        nodeContext: parentContext,\n        storeManager: storeManager,\n        nodeModel: option.nodeModel,\n      });\n    } else if (isAction(propVal)) {\n      const newVal = transformActionNode(propVal, {\n        context: parentContext,\n        storeManager: storeManager,\n        actionVariableSpace: {},\n        nodeModel: option.nodeModel,\n      });\n      return newVal;\n    } else if (isPlainObject(propVal)) {\n      // 可能是 普通的 props 模型\n      let specialPropVal: any = propVal;\n      if (isPropModel(propVal)) {\n        specialPropVal = (propVal as CProp).value;\n      }\n      const objPropVal = specialPropVal as Record<string, any>;\n      const newVal: Record<string, any> = {};\n      Object.keys(specialPropVal).forEach((k) => {\n        newVal[k] = handlePropVal(objPropVal[k]);\n      });\n      return newVal;\n    } else {\n      return propVal;\n    }\n  };\n  const newProps: Record<string, any> = {};\n  Object.keys(propsModel).forEach((propKey) => {\n    const propVal = propsModel[propKey];\n    newProps[propKey] = handlePropVal(propVal);\n  });\n\n  return newProps;\n};\n"
  },
  {
    "path": "packages/render/src/core/ReactAdapter/type.ts",
    "content": "import { CNode, CRootNode } from '@chamn/model';\nimport { AdapterOptionType } from '../adapter';\nimport { StoreManager } from '../storeManager';\nimport { VariableManager } from '../variableManager';\nimport { StoreApi } from 'zustand';\n\nexport type TRenderBaseOption = {\n  storeManager: StoreManager;\n  variableManager: VariableManager;\n  runtimeComponentCache: Map<string, { component: any }>;\n  getComponent: (currentNode: CNode | CRootNode) => any;\n  /** render 支持的所有的组件映射 */\n  components: Record<string, any>;\n  doc: Document;\n} & Pick<\n  Required<AdapterOptionType>,\n  'onGetRef' | 'processNodeConfigHook' | 'refManager' | 'onComponentDestroy' | 'onComponentMount' | 'renderMode'\n> &\n  Pick<AdapterOptionType, 'requestAPI'>;\n\nexport interface IDynamicComponent {\n  _CONDITION: boolean;\n  _DESIGN_BOX: boolean;\n  _NODE_MODEL: CNode | CRootNode;\n  _NODE_ID: string;\n  UNIQUE_ID: string;\n  targetComponentRef: React.MutableRefObject<any>;\n  listenerHandler: (() => void)[];\n  storeState: StoreApi<any>;\n  storeListenDisposeList: (() => void)[];\n  domHeader?: HTMLHeadElement;\n  mediaStyleDomMap: Record<string, HTMLStyleElement>;\n  variableSpace: {\n    staticVar: Record<any, any>;\n    methods: Record<any, (...args: any) => any>;\n  };\n  nodeName: string;\n  // 方法定义\n  updateState(newState: any): void;\n  connectStore(): void;\n  addMediaCSS(): void;\n  removeMediaCSS(): void;\n  getStyleDomById(id: string): HTMLStyleElement;\n  rebuildNode(): void;\n  componentDidMount(): void;\n  componentWillUnmount(): void;\n  render(): React.ReactNode;\n}\n"
  },
  {
    "path": "packages/render/src/core/ReactErrorBoundary.ts",
    "content": "import { CNode, CRootNode } from '@chamn/model';\nimport React from 'react';\n\ntype Props = {\n  node: CNode | CRootNode;\n  targetComponent: any;\n  onError?: (error: React.ErrorInfo) => void;\n  children?: React.ReactNode | React.ReactNode[];\n};\nclass ErrorBoundary extends React.Component<\n  Props,\n  {\n    error: any;\n    hasError: boolean;\n  }\n> {\n  constructor(props: Props) {\n    super(props);\n    this.state = { hasError: false, error: '' };\n  }\n\n  static getDerivedStateFromError(error: any) {\n    // Update state to enable the next render to show the degraded UI\n    return { hasError: true, error };\n  }\n\n  componentDidCatch(_: Error, errorInfo: React.ErrorInfo): void {\n    this.props.onError?.(errorInfo);\n  }\n\n  onDoubleClick = () => {\n    this.setState({\n      hasError: false,\n      error: null,\n    });\n  };\n\n  render() {\n    if (this.state.hasError) {\n      const { onDoubleClick } = this;\n      const schema = this.props.node.value;\n      console.error(this.props.node, this.props.children);\n      const errorView = React.createElement(\n        'div',\n        {\n          style: {\n            backgroundColor: 'rgb(255 206 215 / 13%)',\n            padding: '5px',\n            color: '#ff0000b0',\n            textAlign: 'center',\n            fontSize: '12px',\n          },\n        },\n        'Render error, node id: ',\n        schema.id,\n        ', node name：',\n        schema.title,\n        ' component name：',\n        schema.title || schema.componentName,\n        React.createElement('p', null, 'msg: ', String(this.state.error)),\n        React.createElement(\n          'button',\n          {\n            onDoubleClick: onDoubleClick,\n            style: {\n              border: '1px solid rgba(100,100,100,0.1)',\n              backgroundColor: '#fff',\n              padding: '5px 10px',\n              borderRadius: '2px',\n              color: 'gray',\n              cursor: 'pointer',\n              marginTop: '5px',\n            },\n          },\n          'double click to refresh'\n        ),\n        React.createElement('div', {\n          style: {\n            display: 'none',\n          },\n        })\n      );\n      // return errorView; onlyRenderChild from  errorView\n      return React.createElement(this.props.targetComponent, { onlyRenderChild: true }, errorView);\n    }\n\n    return this.props.children;\n  }\n}\n\nexport default ErrorBoundary;\n"
  },
  {
    "path": "packages/render/src/core/adapter.ts",
    "content": "import { CPage, CNode, CRootNode } from '@chamn/model';\nimport { ReactInstance } from 'react';\nimport { RefManager } from './refManager';\nimport { RenderInstance } from './type';\nimport { StoreManager } from './storeManager';\n\nexport type ContextType = {\n  /** 渲染函数的入口参数 */\n  params?: Record<any, any>;\n  /** 全局状态 */\n  globalState?: Record<any, any>;\n  getGlobalState?: () => Record<any, any>;\n  /** 更新全局状态 */\n  updateGlobalState?: (newState: any) => void;\n  /** 存储当前节点的数据，不具有响应性 **/\n  staticVar?: Record<string | number, any>;\n  getStaticVar?: () => Record<string, any>;\n  getStaticVarById?: (nodeId: string) => Record<string, any>;\n  methods?: Record<string, (...arg: any) => any>;\n  // 获取节点的方法 包含自定义的方法 和 ref 上的方法\n  getMethods?: () => Record<string, (...arg: any) => any>;\n  getMethodsById?: (nodeId: string) => Record<string, (...arg: any) => any>;\n  /** 当前节点状态 **/\n  state?: Record<any, any>;\n  /** 更新当前节点状态 */\n  updateState?: (newState: any) => void;\n  getState?: () => Record<any, any>;\n  getStateById?: (nodeId: string) => Record<any, any>;\n  updateStateById?: (nodeId: string, newState: Record<any, any>) => void;\n  getStateObj?: () => {\n    state?: Record<any, any>;\n    updateState?: (newState: any) => void;\n  };\n  getStateObjById?: (nodeId: string) => {\n    state?: Record<any, any>;\n    updateState?: (newState: any) => void;\n  };\n  /** 获取当前节点 props 值 */\n  getProps: () => void;\n  /**  用于访访问和管理页面被注册为全局的局部 state 快照，在闭包中使用会存在值不是最新的情况 */\n  stateManager?: Record<string, any>;\n  /** 循环数据 */\n  loopData?: Record<any, any>;\n  /** 组件节点的 Ref */\n  nodeRefs?: RefManager;\n  /** 模拟调用节点上的事件方法 */\n  callEventMethod: (eventName: string, event: any) => void;\n  storeManager?: StoreManager;\n  /** 第三方辅助库 */\n  thirdLibs?: Record<string, any>;\n  requestAPI?: AdapterOptionType['requestAPI'];\n};\n\nexport type RuntimeRenderHelper = {\n  renderComponent: (\n    node: CNode | CRootNode,\n    options: {\n      $$context: ContextType;\n      idx?: number;\n    }\n  ) => any;\n};\n\nexport type ComponentsType = Record<any, any>;\n\nexport type AdapterOptionType = {\n  libs: Record<string, any>;\n  components: ComponentsType;\n  $$context: ContextType;\n  refManager: RefManager;\n  onGetRef?: (ref: React.RefObject<React.ReactInstance>, nodeMode: CNode | CRootNode, instance: RenderInstance) => void;\n  onGetComponent?: (component: (...args: any) => any, currentNode: CNode | CRootNode) => void;\n  onComponentMount?: (instance: ReactInstance, node: CNode | CRootNode) => void;\n  onComponentDestroy?: (instance: ReactInstance, node: CNode | CRootNode) => void;\n  processNodeConfigHook?: (\n    config: {\n      condition: boolean;\n      props: Record<string, any>;\n    },\n    node: CNode\n  ) => {\n    condition: boolean;\n    props: Record<string, any>;\n  };\n  renderMode: 'design' | 'normal';\n  /** 请求 API */\n  requestAPI?: (params: {\n    url: string;\n    method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\n    header: Record<any, any>;\n    body?: Record<any, any>;\n    query?: Record<any, any>;\n  }) => Promise<any>;\n  /** 为了更好的适配跨 iframe, 需要将 doc 从外部传入 */\n  doc: Document;\n};\n\n// TODO: 后续考虑去掉\nexport interface AdapterType {\n  renderMode?: AdapterOptionType['renderMode'];\n  customPageRootRender?: (pageModel: CPage, options: AdapterOptionType) => any;\n  // 页面渲染\n  pageRender: (pageModel: CPage, options: AdapterOptionType) => any;\n  // 将一个 组件 model 节点 转换为一个可被运行的渲染函数\n  convertModelToComponent: (\n    originalComponent: any,\n    nodeModal: CNode | CRootNode,\n    options: {\n      pageModel: CPage;\n      idx?: number;\n    } & AdapterOptionType\n  ) => any;\n\n  /** 请求 API */\n  requestAPI?: AdapterOptionType['requestAPI'];\n  // 渲染一个组件\n  render: (originalComponent: any, props?: Record<any, any>, ...children: any[]) => any;\n  // find target component render function\n  getComponent: (currentNode: CNode | CRootNode, components: ComponentsType) => void;\n  getContext: (data: Record<any, any>, ctx: ContextType | null) => ContextType;\n  getUtils: () => void;\n  transformProps: (originalProps: Record<any, any>, options: { $$context: Record<any, any> }) => Record<any, any>;\n  errorCatch: () => void;\n  // clear memory\n  clear: () => void;\n}\n\nconst notImplements = (msg: string) => {\n  return () => {\n    console.warn(`${msg} need to be implement getComponent`);\n  };\n};\n\n// 高级的 render 渲染器，可以完全自定义\nconst CustomAdvanceAdapter = ['customPageRootRender'] as const;\n\nconst AdapterMethodList = [\n  // 页面渲染\n  'pageRender',\n  // 渲染一个组件\n  'render',\n  // 将一个 组件 model 节点 转换为一个可被运行的渲染函数\n  'convertModelToComponent',\n  'getComponent',\n  'getContext',\n  'getUtils',\n  'transformProps',\n  'errorCatch',\n  'requestAPI',\n  'clear',\n] as const;\n\nexport type CustomAdvanceAdapterMethodListType = typeof CustomAdvanceAdapter[number];\n\n// 必须实现的方法\ntype AdapterMethodListType = typeof AdapterMethodList[number];\n\nexport const getAdapter = (defineAdapter: Partial<AdapterType>): AdapterType => {\n  const adapter: AdapterType = [...AdapterMethodList, ...CustomAdvanceAdapter].reduce<\n    Record<AdapterMethodListType, any>\n  >((res, funcName) => {\n    if (defineAdapter?.[funcName]) {\n      res[funcName as AdapterMethodListType] = defineAdapter[funcName]?.bind(defineAdapter);\n    } else {\n      if (AdapterMethodList.includes(funcName as AdapterMethodListType)) {\n        res[funcName as AdapterMethodListType] = notImplements;\n      }\n    }\n    return res;\n  }, {} as any);\n\n  return adapter;\n};\n"
  },
  {
    "path": "packages/render/src/core/designReactRender.ts",
    "content": "import {\n  CNode,\n  ContainerConfig,\n  CPage,\n  CPageDataType,\n  CRootNode,\n  getRandomStr,\n  InnerComponentNameEnum,\n} from '@chamn/model';\nimport { isArray, isPlainObject, merge } from 'lodash-es';\nimport React, { useMemo, useRef } from 'react';\nimport { RenderPropsType, Render, UseRenderReturnType } from './render';\nimport ErrorBoundary from './ReactErrorBoundary';\nimport { RenderInstance } from './type';\nimport { findDOMNode } from '../util/reactHelp';\n\nexport class ComponentInstanceManager {\n  private instanceMap = new Map<string, RenderInstance[]>();\n\n  get(id: string) {\n    return this.instanceMap.get(id);\n  }\n  add(id: string, handle: any) {\n    const val = this.instanceMap.get(id);\n    if (val) {\n      val.push(handle);\n    } else {\n      this.instanceMap.set(id, [handle]);\n    }\n  }\n\n  remove(id: string, val?: any) {\n    const valList = this.instanceMap.get(id);\n    if (val !== undefined && Array.isArray(valList)) {\n      const newList = valList.filter((el) => el !== val);\n      this.instanceMap.set(id, newList);\n    } else {\n      this.instanceMap.delete(id);\n    }\n  }\n\n  destroy() {\n    this.instanceMap.clear();\n  }\n}\n\nexport type DesignRenderProp = Omit<RenderPropsType, 'ref' | 'render'> & {\n  ref?: React.MutableRefObject<DesignRender | null>;\n  render?: UseDesignRenderReturnType;\n  onMount?: (instance: DesignRender) => void;\n  dropPlaceholder?:\n    | React.ComponentClass<{ node: CNode | CRootNode }>\n    | React.FunctionComponent<{ node: CNode | CRootNode }>\n    | string;\n};\n\nexport const DefaultDropPlaceholder: React.FC<{ node: CNode | CRootNode }> = (props) => {\n  const { node } = props;\n  const configInfo = useMemo(() => {\n    const isContainer = node.isContainer();\n    if (isPlainObject(isContainer)) {\n      return isContainer as ContainerConfig;\n    } else {\n      return {\n        placeholder: 'Drag the component to place it',\n        width: '100%',\n        height: '100%',\n        style: {},\n      };\n    }\n  }, [node]);\n\n  const { placeholder, height, width, style } = configInfo;\n\n  return React.createElement(\n    'div',\n    {\n      style: {\n        margin: 0,\n        padding: 0,\n        display: 'flex',\n        alignItems: 'center',\n        justifyContent: 'center',\n        backgroundColor: 'rgba(200,200,200,0.1)',\n        border: '1px solid rgba(0,0,0,0.1)',\n        borderRadius: '2px',\n        fontSize: '14px',\n        color: 'gray',\n        cursor: 'default',\n        minHeight: '50px',\n        width: width,\n        height: height,\n        boxSizing: 'border-box',\n        ...style,\n      },\n    },\n    placeholder\n  );\n};\n\nexport class DesignRender extends React.Component<DesignRenderProp> {\n  instanceManager = new ComponentInstanceManager();\n  renderRef: React.MutableRefObject<Render | null>;\n  dropPlaceholder: Required<DesignRenderProp>['dropPlaceholder'] = DefaultDropPlaceholder;\n  _components: Record<string, any> = {};\n\n  constructor(props: DesignRenderProp) {\n    super(props);\n    this._components = Object.assign(this._components, this.props.components);\n    this.renderRef = React.createRef<Render>();\n    if (props.dropPlaceholder) {\n      this.dropPlaceholder = props.dropPlaceholder;\n    }\n  }\n\n  updateComponents(newComponents: Record<string, string> = {}) {\n    // 只能 assign, 保持变量引用地址不变\n    this._components = Object.assign(this._components, newComponents);\n    this.forceUpdate();\n  }\n\n  componentDidMount(): void {\n    // 添加特殊运行时标记\n    (window as any).__CHAMN_RENDER_MODE = 'DESIGN';\n    this.updateComponents(this.props.components);\n    this.props.onMount?.(this);\n  }\n  getPageModel() {\n    return this.renderRef.current?.state.pageModel;\n  }\n\n  onGetComponent = (comp: any, node: CNode | CRootNode) => {\n    // eslint-disable-next-line @typescript-eslint/no-this-alias\n    const self = this;\n    let innerComp = comp;\n    const wrapComponent = node.material?.value.advanceCustom?.wrapComponent;\n    if (wrapComponent) {\n      innerComp = wrapComponent(comp, {\n        node,\n        ctx: (window as any).__C_ENGINE_DESIGNER_PLUGIN_CTX__,\n      });\n    }\n\n    class DesignWrap extends React.Component<any> {\n      _DESIGN_BOX = true;\n      _NODE_MODEL = node;\n      _NODE_ID = node.id;\n      _UNIQUE_ID = `${node.id}_${getRandomStr()}`;\n      _STATUS?: 'DESTROY';\n      _design_target_component = React.createRef<any>();\n\n      _dom?: HTMLElement | null;\n\n      componentDidMount(): void {\n        self.instanceManager.add(node.id, this);\n      }\n\n      getTargetComponentRef() {\n        return this._design_target_component;\n      }\n\n      componentWillUnmount(): void {\n        this._STATUS = 'DESTROY';\n        self.instanceManager.remove(node.id, this);\n      }\n\n      getDom() {\n        const autoGetDom = node.material?.value.advanceCustom?.autoGetDom ?? true;\n        if (autoGetDom) {\n          // // 返回第一个孩子节点, 模拟 ReactDOM.findDOMNode 行为\n          // return this._dom?.children?.[0];\n          const dom = findDOMNode(this);\n          const rootSelector = node.material?.value.rootSelector;\n          // 如果指定了 rootSelector (Modal 浮窗场景)\n          if (rootSelector) {\n            return (dom as any)?.querySelector?.(rootSelector) || dom;\n          }\n          return dom;\n        } else {\n          return this._dom;\n        }\n      }\n\n      render() {\n        const { children = [], onlyRenderChild, ...restProps } = this.props;\n        const finalRestProps = {\n          ...restProps,\n          ref: (ref: any) => {\n            (this._design_target_component.current as any) = ref;\n          },\n        };\n        let newChildren = children;\n        if (!isArray(children)) {\n          newChildren = [children];\n        }\n\n        const hasChildren = Boolean(newChildren.filter(Boolean).length);\n        if (\n          !hasChildren &&\n          (node.isContainer() || node.value.componentName === InnerComponentNameEnum.ROOT_CONTAINER)\n        ) {\n          const nodeDropPlaceholder = node.material?.value.advanceCustom?.dropPlaceholder;\n          newChildren.push(\n            React.createElement(nodeDropPlaceholder || self.dropPlaceholder, {\n              node: node,\n            })\n          );\n        }\n        if (onlyRenderChild) {\n          return newChildren;\n        }\n        const autoGetDom = node.material?.value.advanceCustom?.autoGetDom ?? true;\n\n        if (autoGetDom) {\n          const coreEl = React.createElement(innerComp, finalRestProps, ...newChildren);\n\n          return coreEl;\n        } else {\n          const coreEl = React.createElement(\n            innerComp,\n            {\n              ...finalRestProps,\n              //  注入到组件中, 配合设计器使用\n              $SET_DOM: (ref: any) => {\n                this._dom = ref;\n              },\n            },\n            ...newChildren\n          );\n          return coreEl;\n        }\n      }\n    }\n    return React.forwardRef(function ErrorWrap(props: any, ref) {\n      return React.createElement(\n        ErrorBoundary,\n        {\n          node,\n          targetComponent: DesignWrap,\n        },\n        React.createElement(DesignWrap, {\n          ref,\n          ...props,\n        })\n      );\n    });\n  };\n\n  rerender(newPage?: CPageDataType | CPage) {\n    return this.renderRef.current?.rerender(newPage);\n  }\n\n  getInstancesById(id: string, uniqueId?: string): RenderInstance[] {\n    let res = [...(this.instanceManager.get(id) || [])];\n    if (uniqueId !== undefined) {\n      res = res.filter((el) => {\n        return uniqueId === el?._UNIQUE_ID;\n      });\n    }\n    return res;\n  }\n\n  getInstanceByDom(el: HTMLHtmlElement | Element): RenderInstance | null {\n    const fiberNode = findClosetFiberNode(el);\n    if (!fiberNode) {\n      return null;\n    }\n    const containerFiber = findClosetContainerFiberNode(fiberNode);\n    return containerFiber?.stateNode || null;\n  }\n  getDomsById(id: string, selector?: string) {\n    const instances = this.getInstancesById(id);\n    const doms: HTMLElement[] = [];\n    instances?.forEach((el) => {\n      if (el?._STATUS === 'DESTROY') {\n        return;\n      }\n      const dom = el.getDom();\n      if (dom && !(dom instanceof Text)) {\n        if (selector) {\n          // 判断是不是数组\n          const list: HTMLElement[] = Array.from(dom.querySelectorAll?.(selector) || []);\n          doms.push(...list);\n        } else {\n          doms.push(dom as unknown as HTMLElement);\n        }\n      }\n    });\n\n    return doms;\n  }\n  getDomRectById(id: string, selector?: string) {\n    const domList = this.getDomsById(id, selector) as HTMLElement[];\n    // 判断是不是数组\n    const rectList = domList\n      .map((el) => {\n        return el?.getBoundingClientRect();\n      })\n      .filter(Boolean);\n    return rectList;\n  }\n\n  render() {\n    const { props, onGetComponent } = this;\n    const { render, ...renderProps } = props;\n    if (render) {\n      render.ref.current = this;\n    }\n\n    return React.createElement(Render, {\n      onGetComponent,\n      ...renderProps,\n      components: this._components,\n      // 拦截特殊属性配置, 配合设计模式使用\n      /**\n       * 可以对节点的一些配置数据做统一处理\n       * 比如: fixedProps 强制将 fixedProps 内的值覆盖 node 本省的配置\n       * 比如： 统一处理节点的 Condition 属性，控制节点是否显示\n       * @param config\n       * @param node\n       * @returns\n       */\n      processNodeConfigHook: (config, node) => {\n        if (node.nodeType !== 'NODE') {\n          return config;\n        }\n        const { props, condition } = config;\n        let newProps = { ...props };\n        const tempDevState = node.value.configure?.devState || {};\n        const fixedPropsObj = node.material?.value.fixedProps;\n        if (fixedPropsObj !== undefined) {\n          if (isPlainObject(fixedPropsObj)) {\n            newProps = {\n              ...newProps,\n              ...fixedPropsObj,\n            };\n          } else if (typeof fixedPropsObj === 'function') {\n            const tempProps = fixedPropsObj(newProps);\n            newProps = {\n              ...newProps,\n              ...tempProps,\n            };\n          }\n        }\n        let newCondition = condition;\n        if (tempDevState.condition === false) {\n          newCondition = tempDevState.condition as boolean;\n        }\n        return {\n          props: merge(newProps, tempDevState.props || {}),\n          condition: newCondition,\n        };\n      },\n      renderMode: 'design',\n      ref: this.renderRef,\n    });\n  }\n}\n\nexport type UseDesignRenderReturnType = Pick<UseRenderReturnType, 'rerender'> & {\n  ref: React.MutableRefObject<DesignRender | null>;\n  getInstancesById: (id: string, uid?: string) => RenderInstance[];\n  getInstanceByDom: (dom: HTMLHtmlElement | Element) => RenderInstance | null;\n  getDomsById: (id: string, selector?: string) => HTMLElement[];\n  getDomRectById: (id: string, selector?: string) => DOMRect | DOMRect[];\n};\n\nconst findClosetFiberNode = (el: Element | null): SimpleFiberNodeType | null => {\n  if (!el) {\n    return null;\n  }\n  const REACT_KEY =\n    Object.keys(el).find((key) => key.startsWith('__reactInternalInstance$') || key.startsWith('__reactFiber$')) || '';\n\n  if (REACT_KEY) {\n    return (el as any)[REACT_KEY];\n  } else {\n    return findClosetFiberNode(el.parentElement);\n  }\n};\n\ntype SimpleFiberNodeType = {\n  return: SimpleFiberNodeType;\n  stateNode: (Element | HTMLElement) & RenderInstance;\n};\n\nconst findClosetContainerFiberNode = (fiberNode: SimpleFiberNodeType): SimpleFiberNodeType | null => {\n  if (!fiberNode) {\n    return null;\n  }\n  if (fiberNode?.stateNode?._DESIGN_BOX) {\n    return fiberNode;\n  } else {\n    return findClosetContainerFiberNode(fiberNode.return);\n  }\n};\n\nexport const useDesignRender = (): UseDesignRenderReturnType => {\n  const ref = useRef<DesignRender>(null);\n  return {\n    ref: ref,\n    rerender: function (...args) {\n      if (ref.current) {\n        ref.current.rerender(...args);\n      }\n    },\n    getInstancesById(id, uid) {\n      return ref.current?.getInstancesById(id, uid) || [];\n    },\n    getInstanceByDom(el) {\n      return ref.current?.getInstanceByDom(el) || null;\n    },\n    getDomsById(id: string, selector?: string) {\n      return ref.current?.getDomsById(id, selector) || [];\n    },\n    getDomRectById(id: string, selector?: string) {\n      return ref.current?.getDomRectById(id, selector) || [];\n    },\n  };\n};\n"
  },
  {
    "path": "packages/render/src/core/refManager.ts",
    "content": "export class RefManager {\n  private refMap = new Map();\n\n  get(id: string) {\n    return this.refMap.get(id);\n  }\n  add(id: string, handle: any) {\n    this.refMap.set(id, handle);\n  }\n\n  remove(id: string) {\n    this.refMap.delete(id);\n  }\n\n  destroy() {\n    this.refMap.clear();\n  }\n}\n"
  },
  {
    "path": "packages/render/src/core/render.ts",
    "content": "import { checkPage, CPage, CPageDataType } from '@chamn/model';\nimport { isPlainObject } from 'lodash-es';\nimport React, { useMemo, useRef } from 'react';\nimport { InnerComponent } from '../commonComponent';\nimport { AdapterOptionType, AdapterType } from './adapter';\nimport { RefManager } from './refManager';\nimport { RenderInstance } from './type';\n\nexport type RenderPropsType = {\n  page?: CPageDataType;\n  pageModel?: CPage;\n  adapter: AdapterType;\n  render?: UseRenderReturnType;\n  ref?: React.MutableRefObject<Render | null>;\n  renderMode?: 'design' | 'normal';\n  doc?: Document;\n} & Partial<AdapterOptionType>;\n\nexport class Render extends React.Component<\n  RenderPropsType,\n  {\n    pageModel: CPage;\n  }\n> {\n  refManager: RefManager;\n  // save component instance\n  dynamicComponentInstanceMap = new Map<string, RenderInstance>();\n  constructor(props: RenderPropsType) {\n    super(props);\n    this.state = {\n      pageModel: props.pageModel || new CPage(props.page!),\n    };\n    this.refManager = new RefManager();\n  }\n\n  getPageModel() {\n    return this.state.pageModel;\n  }\n\n  componentDidMount(): void {\n    const { render } = this.props;\n    if (render) {\n      render.ref.current = this;\n    }\n\n    // 将当前 window 暴露给\n    if (window.parent) {\n      (window.parent as any).__CB_RENDER_WIN__ = window;\n    }\n  }\n\n  componentWillUnmount(): void {\n    this.refManager.destroy();\n  }\n\n  onGetRef: AdapterOptionType['onGetRef'] = (ref, nodeModel, instance) => {\n    this.props.onGetRef?.(ref, nodeModel, instance);\n    this.dynamicComponentInstanceMap.set(nodeModel.id, instance);\n    this.refManager.add(nodeModel.value.refId || nodeModel.id, ref);\n  };\n\n  render() {\n    const { props } = this;\n    const { adapter, onGetComponent, onComponentDestroy, onComponentMount } = props;\n    const { pageModel } = this.state;\n    // todo: 加载 page 资源\n    // todo: 收集所有的 第三方库\n    if (!pageModel) {\n      console.warn('pageModel is null');\n      return null;\n    }\n    const finalComponents = {\n      ...InnerComponent,\n      ...props.components,\n    };\n\n    const $$context: any = this.props.$$context || {};\n    let newDoc = this.props.doc;\n    if (typeof window !== 'undefined') {\n      newDoc = this.props.doc || window.document;\n    }\n    const PageRoot = adapter.pageRender(pageModel, {\n      libs: {},\n      components: finalComponents,\n      refManager: this.refManager,\n      onGetRef: this.onGetRef,\n      onGetComponent,\n      onComponentMount,\n      onComponentDestroy,\n      $$context: {\n        ...$$context,\n        nodeRefs: this.refManager,\n      },\n      renderMode: props.renderMode || 'normal',\n      requestAPI: props.requestAPI ?? adapter.requestAPI,\n      processNodeConfigHook: props.processNodeConfigHook,\n      doc: newDoc!,\n    });\n\n    return PageRoot;\n  }\n\n  rerender = (newPage?: CPageDataType | CPage) => {\n    this.props.adapter.clear();\n    if ((newPage as CPage)?.nodeType === 'PAGE' && newPage) {\n      this.setState({\n        pageModel: newPage as CPage,\n      });\n    } else if (isPlainObject(newPage) && checkPage(newPage)) {\n      const newP = newPage as CPageDataType;\n      this.setState({\n        pageModel: new CPage(newP, {\n          materials: this.state.pageModel.materialsModel.rawValue,\n        }),\n      });\n    }\n  };\n}\n\nexport type UseRenderReturnType = {\n  ref: React.MutableRefObject<Render | null>;\n  rerender: (newPage: CPageDataType) => void;\n};\n\nexport const useRender = (): UseRenderReturnType => {\n  const ref = useRef<Render>(null);\n\n  const res = useMemo<UseRenderReturnType>(() => {\n    return {\n      ref: ref,\n      rerender: function (...args) {\n        if (ref.current) {\n          ref.current.rerender(...args);\n        }\n      },\n    };\n  }, []);\n\n  return res;\n};\n\nexport * from './type';\n"
  },
  {
    "path": "packages/render/src/core/storeManager.ts",
    "content": "import { StateCreator, StoreApi, createStore } from 'zustand/vanilla';\n\nexport class StoreManager {\n  storeMap: Map<string, StoreApi<any>> = new Map();\n\n  addStore(storeName: string, storeState: StateCreator<any>) {\n    const store = createStore(storeState);\n    this.storeMap.set(storeName, store);\n    (store as any).name = storeName;\n\n    return store;\n  }\n\n  setStore(storeName: string, store: StoreApi<any>) {\n    this.storeMap.set(storeName, store);\n  }\n\n  removeStore(storeName: string) {\n    this.storeMap.delete(storeName);\n  }\n\n  getStore(storeName: string) {\n    return this.storeMap.get(storeName);\n  }\n\n  getState(nodeId: string) {\n    return this.storeMap.get(nodeId)?.getState();\n  }\n\n  getStateObj(nodeId: string) {\n    return {\n      state: this.getState(nodeId),\n      updateState: (newState: Record<any, any>) => {\n        this.setState(nodeId, newState);\n      },\n    };\n  }\n\n  setState(nodeId: string, newState: Record<any, any>) {\n    return this.storeMap.get(nodeId)?.setState(newState);\n  }\n\n  connect<T extends Record<any, any> = any>(name: string, cb: (newState: T) => void) {\n    const store = this.storeMap.get(name);\n    if (store) {\n      return store.subscribe(cb);\n    } else {\n      console.warn('store not exits');\n      return () => {};\n    }\n  }\n\n  /**\n   * storeManger 赋值以及 获取需要做转换\n   */\n  getStateSnapshot() {\n    const res: Record<string, any> = {};\n    this.storeMap.forEach((_, key) => {\n      res[key] = this.getStateObj(key);\n    });\n    return res;\n  }\n\n  destroy() {\n    this.storeMap = new Map();\n  }\n}\n"
  },
  {
    "path": "packages/render/src/core/type.ts",
    "content": "import { CNode, CRootNode } from '@chamn/model';\n\nexport type RenderInstance = React.ReactInstance & {\n  _DESIGN_BOX: boolean;\n  _NODE_MODEL: CNode | CRootNode;\n  _NODE_ID: string;\n  _UNIQUE_ID: string;\n  _STATUS?: 'DESTROY';\n  _CONDITION?: boolean;\n  _dom?: HTMLElement;\n  getDom: () => HTMLElement | null | undefined;\n};\n"
  },
  {
    "path": "packages/render/src/core/variableManager.ts",
    "content": "/** 变量空间，没有响应性 */\nexport class VariableManager {\n  private varSpace = new Map();\n\n  get(id: string) {\n    return this.varSpace.get(id);\n  }\n  add(id: string, handle: any) {\n    this.varSpace.set(id, handle);\n  }\n\n  remove(id: string) {\n    this.varSpace.delete(id);\n  }\n\n  destroy() {\n    this.varSpace.clear();\n  }\n\n  getStateSnapshot() {\n    const res: Record<string, any> = {};\n    this.varSpace.forEach((val, key) => {\n      res[key] = val;\n    });\n    return res;\n  }\n}\n"
  },
  {
    "path": "packages/render/src/index.ts",
    "content": "export * from './core/adapter';\nexport * from './core/ReactAdapter';\nexport * from './core/render';\nexport * from './core/designReactRender';\nexport * from './core/refManager';\nexport * from './util/assetsLoader';\nexport * from './util';\n\nexport * from './core/ReactAdapter/type';\nexport * from './const/index';\n"
  },
  {
    "path": "packages/render/src/util/assetsLoader.ts",
    "content": "import { AssetPackage, getRandomStr } from '@chamn/model';\nimport loadjs from 'loadjs';\n\nexport type Asset = AssetPackage;\n\nexport class AssetLoader {\n  assets: Asset[];\n  loadStatus: 'INIT' | 'SUCCESS' | 'ERROR';\n  win: Window = window;\n  private _onSuccessList: (() => void)[] = [];\n  private _onErrorList: ((depsNotFound: string[]) => void)[] = [];\n  constructor(\n    assets: Asset[],\n    options?: {\n      window?: Window;\n    }\n  ) {\n    this.assets = JSON.parse(JSON.stringify(assets || []));\n    this.loadStatus = 'INIT';\n    if (options?.window) {\n      this.win = options.window;\n    }\n  }\n\n  load(options?: {\n    /** 是否并行加载资源 */\n    async: boolean;\n  }) {\n    const assets = this.assets || [];\n    const ids: string[] = [];\n\n    for (let i = 0; i < assets.length; i++) {\n      const item = assets[i];\n      if (!item.id) {\n        item.id = getRandomStr();\n      }\n      ids.push(item.id);\n      const srcList = item.resources.map((el) => el.src);\n      if (srcList.length) {\n        loadjs(srcList, item.id, {\n          async: options?.async ?? false,\n          before: (_path, scriptEl) => {\n            this.win.document.body.appendChild(scriptEl);\n            /* return `false` to bypass default DOM insertion mechanism */\n            return false;\n          },\n        });\n      }\n    }\n\n    return new Promise((resolve, reject) => {\n      if (assets.length === 0) {\n        this._onSuccessList.forEach((el) => el());\n        resolve('');\n        return;\n      }\n\n      // 在下一个事件循环执行，确保 onSuccess 和 onError 被注册\n      loadjs.ready(ids, {\n        success: () => {\n          this._onSuccessList.forEach((el) => el());\n          resolve('');\n        },\n        error: (depsNotFound) => {\n          this._onErrorList.forEach((el) => el(depsNotFound));\n          reject(depsNotFound);\n        },\n      });\n    });\n  }\n\n  onSuccess(cb: () => void) {\n    this._onSuccessList.push(cb);\n    return this;\n  }\n\n  onError(cb: () => void) {\n    this._onErrorList.push(cb);\n    return this;\n  }\n}\n"
  },
  {
    "path": "packages/render/src/util/codeRuntimeHelper.ts",
    "content": "export const generateObjVarProxy = (\n  varName: string,\n  options: {\n    keyListVar: string;\n    getReadValCode: string;\n  } = {\n    keyListVar: 'ALL_NODE_IDS',\n    getReadValCode: '',\n  }\n) => {\n  let tempVarName = varName;\n  // id 以数字开头, 自动添加下划线\n  if (tempVarName[0] === String(Number(tempVarName[0]))) {\n    tempVarName = `_${tempVarName}`;\n  }\n\n  return `\nvar ${varName} = {};\n\n${options.keyListVar}.forEach(function (key) {\n  var tempK = /^\\\\d/.test(key) ? ('_' + key) : key;\n  Object.defineProperty(${varName}, tempK, {\n    get: function () {\n      ${options.getReadValCode}\n    },\n    set: function (value) {},\n    enumerable: true\n  });\n});\n`;\n};\n"
  },
  {
    "path": "packages/render/src/util/index.ts",
    "content": "import { capitalize } from 'lodash-es';\nimport { Component, createElement } from 'react';\nimport { ContextType } from '../core/adapter';\nimport { StoreManager } from '../core/storeManager';\nimport { AssetPackage, CNode, CNodeModelDataType, CRootNode, ComponentMetaType, LibMetaType } from '@chamn/model';\nimport { generateObjVarProxy } from './codeRuntimeHelper';\n\nexport const isClass = function (val: any) {\n  if (!val) {\n    return false;\n  }\n  if (typeof val !== 'function') {\n    return false;\n  }\n  if (!val.prototype) {\n    return false;\n  }\n  return true;\n};\n\nexport function shouldConstruct(Component: any) {\n  const prototype = Component?.prototype;\n  return !!(prototype && prototype.isReactComponent);\n}\n\nexport function canAcceptsRef(Comp: any) {\n  const hasSymbol = typeof Symbol === 'function' && Symbol.for;\n  const REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for('react.forward_ref') : 0xead0;\n  return (\n    Comp?.$$typeof === REACT_FORWARD_REF_TYPE ||\n    Comp?.prototype?.isReactComponent ||\n    Comp?.prototype?.setState ||\n    Comp._forwardRef\n  );\n}\n\nexport function compWrapper(Comp: any) {\n  class WrapperForRef extends Component {\n    render() {\n      // 需要直接调用 react api ,避免被拦截 !!!!\n      return createElement(Comp, this.props);\n    }\n  }\n  (WrapperForRef as any).displayName = Comp.displayName;\n\n  return WrapperForRef as any;\n}\n\nexport const runExpression = (\n  expStr: string,\n  options: {\n    nodeContext: ContextType;\n    storeManager: StoreManager;\n    /** 最近一个 API 的返回响应 */\n    $$response?: any;\n    actionVariableSpace?: Record<any, any>;\n    nodeModel: CNode;\n  }\n) => {\n  const run = (expStr: string) => {\n    let funcBody = `function run () { return ${expStr}; }`;\n    if (String(expStr).includes('return ')) {\n      funcBody = `function runExp () { ${expStr} }`;\n    }\n\n    return convertCodeStringToFunction({\n      funcBody: funcBody,\n      funcName: 'run',\n      nodeContext: options.nodeContext,\n      storeManager: options.storeManager,\n      nodeModel: options.nodeModel,\n    })();\n  };\n  try {\n    return run(expStr);\n  } catch (e) {\n    console.warn(e);\n    const msg = `[${expStr}] expression run failed`;\n    console.warn(msg);\n    return null;\n  }\n};\n\n/** 后续考虑是否需要做沙箱 */\nexport const convertCodeStringToFunction = (params: {\n  funcBody: string;\n  funcName: string;\n  nodeContext: ContextType;\n  storeManager: StoreManager;\n  /** 最近一个 API 的返回响应 */\n  $$response?: any;\n  actionVariableSpace?: Record<any, any>;\n  nodeModel: CNode;\n}) => {\n  const {\n    funcBody,\n    funcName: functionName,\n    nodeContext: $$context,\n    storeManager,\n    $$response,\n    nodeModel,\n    actionVariableSpace,\n  } = params;\n  const funcName = functionName || 'anonymous';\n\n  const tempObj = {\n    [funcName]: function (...args: any[]) {\n      let codeBody;\n      const actionVariableSpaceKeyList = Object.keys(actionVariableSpace || {});\n      const varListCode = actionVariableSpaceKeyList.map((key) => {\n        return `var ${key} = $ACTION_VAR_SPACE[${JSON.stringify(key)}];`;\n      });\n      try {\n        codeBody = `\n  var $$$__args__$$$ = Array.from(arguments);\n  function $$_run_$$() {\n    var __extraParams = $$$__args__$$$.pop();\n    var __$$storeManager__ = __extraParams.storeManager;\n    var $ALL_NODE_IDS = Array.from(__$$storeManager__.storeMap.keys().filter(Boolean));;\n    var $RESPONSE = __extraParams.$$response;;\n    var $CTX =  __extraParams.$$context;\n    var $$context = $CTX;\n    var $PARAMS = $$context.params || {};\n    var $EVENT_PARAMS = $$$__args__$$$[0];\n    var $Event = $$$__args__$$$[0];\n    var $EVENT = $$$__args__$$$[0];\n    var $ARGS = $$$__args__$$$;\n\n    var $ACTION_VAR_SPACE = __extraParams.actionVariableSpace;\n    // 新增的变量\n    var $N_ID = ${JSON.stringify(nodeModel.id)};\n    var $NODE_MODAL = __extraParams.nodeModel;\n    var $IDS = $ALL_NODE_IDS.reduce(function (res, id) {\n        res[id] = id;\n        return res;\n    }, {});\n    var $M = {};\n    var $G_STATE = __$$storeManager__.getState('globalState');\n    var $STATE = __$$storeManager__.getState($N_ID);\n    var $LOOP_DATA = $CTX.loopData;\n\n\n${generateObjVarProxy('$ALL_STATE', {\n  keyListVar: '$ALL_NODE_IDS ',\n  getReadValCode: 'return __$$storeManager__.getState(key);',\n})}\n\n${generateObjVarProxy('$U_STATE', {\n  keyListVar: '$ALL_NODE_IDS ',\n  getReadValCode: 'return function (newVal) { __$$storeManager__.setState(key, newVal); }',\n})}\n\n    $$context.stateManager = __$$storeManager__.getStateSnapshot();\n    // action flowACTION_VAR_SPACE 中定义的变量\n    ${varListCode.join(';\\n')}\n\n    var $$_f_$$ = ${funcBody.trim() || 'function () {}'};\n\n    return $$_f_$$.apply($$_f_$$, $$$__args__$$$);\n  }\n  return $$_run_$$();\n        `;\n        const f = new Function(codeBody);\n        return f(...args, { $$context, storeManager, $$response, actionVariableSpace, nodeModel });\n      } catch (e) {\n        console.warn(e);\n      }\n    },\n  };\n  return tempObj[funcName];\n};\n\n/**\n *\n * @param args 对象的值\n * @param argsName 对因位置的 名称\n * @returns\n */\nexport const getObjFromArrayMap = (args: any[], argsName: string[]) => {\n  const params: Record<any, any> = {};\n  argsName.forEach((paramName, index) => {\n    params[paramName] = args[index];\n  });\n\n  return params;\n};\n\nexport const formatSourceStylePropertyName = (styleList: CNodeModelDataType['style'] = []) => {\n  const newStyle: Record<string, string> = {};\n  styleList.forEach(({ property: key, value }) => {\n    // 处理 css 前缀\n    let preKey = key.replace('-webkit', 'Webkit');\n    preKey = preKey.replace('-ms', 'ms');\n    preKey = preKey.replace('-moz', 'Moz');\n    preKey = preKey.replace('-o', 'O');\n    let newKey = preKey.split('-');\n    if (newKey.length >= 2) {\n      newKey = newKey.map((val, index) => {\n        if (index !== 0) {\n          return capitalize(val);\n        } else {\n          return val;\n        }\n      });\n    }\n    newStyle[newKey.join('')] = value as string;\n  });\n\n  return newStyle;\n};\n\nexport const getCSSTextValue = (cssObj: Record<string, string>) => {\n  let res = '';\n  Object.keys(cssObj || {}).forEach((key) => {\n    res += `${key}:${cssObj[key]};`;\n  });\n  return res;\n};\n\nexport const collectVariable = (assetPackages: AssetPackage[], win: Window) => {\n  const res: Record<string, any> = {};\n  assetPackages.forEach((el) => {\n    if (el.globalName) {\n      const target = (win as any)[el.globalName];\n      if (target) {\n        res[el.globalName] = target;\n        if (target.__esModule && target.default) {\n          res[el.globalName] = target.default;\n        }\n      }\n    }\n  });\n  return res;\n};\n\nexport const flatObject = (obj: Record<string, any>, level = 1) => {\n  let count = 0;\n  let currentObj = obj;\n  let newObj: Record<string, any> = {};\n  let res = {};\n  while (count < level) {\n    Object.keys(currentObj).forEach((key) => {\n      newObj = {\n        ...newObj,\n        ...currentObj[key],\n      };\n    });\n    res = newObj;\n    currentObj = newObj;\n    newObj = {};\n    count += 1;\n  }\n\n  return res;\n};\n\nexport const getMatchVal = (val: string, regArr: RegExp[]) => {\n  const list: string[] = [];\n  regArr.forEach((el) => {\n    const reg = el;\n    const res = reg.exec(val);\n    if (res?.length) {\n      list.push(res[1]);\n    }\n  });\n  return list;\n};\n\nconst getLibsByList = (libs: Record<string, any>, list: LibMetaType[]) => {\n  const newRes: Record<string, any> = {};\n  list.forEach((el) => {\n    if (libs[el.name]) {\n      newRes[el.name] = libs[el.name];\n    }\n  });\n\n  return newRes;\n};\n\nexport const getComponentsLibs = (libs: Record<string, any>, list: ComponentMetaType[]) => {\n  return getLibsByList(libs, list);\n};\n\nexport const getThirdLibs = (libs: Record<string, any>, list: LibMetaType[]) => {\n  return getLibsByList(libs, list);\n};\n\nexport const getNodeCssClassName = (node: CNode | CRootNode) => {\n  const nodeClassName = node.value.css?.class || `c_${node.id}`;\n  return nodeClassName;\n};\n\nexport const findComponentByChainRefer = (componentReferPath: string, components: Record<string, any>) => {\n  const componentPath = componentReferPath.split('.');\n  let res;\n  let tempMap = components;\n\n  componentPath.forEach((key) => {\n    res = tempMap?.[key];\n    tempMap = res;\n  });\n\n  return res || (() => `Component [${componentReferPath}] not found`);\n};\n\n/**\n * 优先访问 sourceObj 上的方法，找不到时访问 targetObj 上的方法\n * @param sourceObj\n * @param targetObj\n * @returns\n */\nexport const getInheritObj = (sourceObj: Record<any, any>, targetObj: Record<any, any>) => {\n  const newObj = {\n    ...sourceObj,\n  };\n\n  (newObj as any).__proto__ = targetObj;\n  return newObj;\n};\n"
  },
  {
    "path": "packages/render/src/util/reactHelp.ts",
    "content": "import { Component } from 'react';\nimport { findCurrentHostFiber } from 'react-reconciler/reflection';\n\n// https://github.com/facebook/react/blob/main/packages/shared/ReactInstanceMap.js#L18\nexport function getInstance(key: any) {\n  return key._reactInternals;\n}\n\n// https://github.com/facebook/react/blob/main/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js#L167\ntype PublicInstance = Element | Text;\n\n// https://github.com/facebook/react/blob/main/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js#L322\nfunction getPublicInstance(instance: PublicInstance): PublicInstance {\n  return instance;\n}\n\n// https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberReconciler.js#L153\nfunction findHostInstance(component: Component): PublicInstance | null {\n  const fiber = getInstance(component);\n  if (fiber === undefined) {\n    if (typeof component.render === 'function') {\n      throw new Error('Unable to find node on an unmounted component.');\n    } else {\n      const keys = Object.keys(component).join(',');\n      throw new Error(`Argument appears to not be a ReactComponent. Keys: ${keys}`);\n    }\n  }\n  const hostFiber = findCurrentHostFiber(fiber);\n  if (hostFiber === null) {\n    return null;\n  }\n  return getPublicInstance(hostFiber.stateNode);\n}\n\n// https://github.com/facebook/react/blob/main/packages/react-dom/src/client/ReactDOMClient.js#L43\nexport function findDOMNode(componentOrElement: Component<any, any>): null | Element | Text {\n  return findHostInstance(componentOrElement);\n}\n"
  },
  {
    "path": "packages/render/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ESNext\",\n    \"useDefineForClassFields\": true,\n    \"lib\": [\n      \"DOM\",\n      \"DOM.Iterable\",\n      \"ESNext\"\n    ],\n    \"allowJs\": false,\n    \"skipLibCheck\": true,\n    \"esModuleInterop\": false,\n    \"allowSyntheticDefaultImports\": true,\n    \"strict\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": false,\n    \"noEmit\": true,\n    \"jsx\": \"react\"\n  },\n  \"include\": [\n    \"src\"\n  ],\n}"
  },
  {
    "path": "pnpm-workspace.yaml",
    "content": "packages:\n  # all packages in direct subdirs of packages/\n  - 'packages/*'\n  # exclude packages that are inside test directories\n  - '!**/test/**'\n"
  }
]