Browse Source

feat:初始化项目结构

wanghao 8 months ago
parent
commit
643c232edc
100 changed files with 7022 additions and 1 deletions
  1. 11 0
      .editorconfig
  2. 45 0
      .env
  3. 7 0
      .env.prod
  4. 7 0
      .env.test
  5. 13 0
      .gitattributes
  6. 90 0
      .github/ISSUE_TEMPLATE/bug-report_cn.yaml
  7. 90 0
      .github/ISSUE_TEMPLATE/bug-report_en.yaml
  8. 48 0
      .github/ISSUE_TEMPLATE/feature_request_cn.yaml
  9. 48 0
      .github/ISSUE_TEMPLATE/feature_request_en.yaml
  10. 50 0
      .github/PULL_REQUEST_TEMPLATE/pr_cn.md
  11. 51 0
      .github/PULL_REQUEST_TEMPLATE/pr_en.md
  12. 30 0
      .github/workflows/linter.yml
  13. 25 0
      .github/workflows/release.yml
  14. 36 0
      .gitignore
  15. 4 0
      .npmrc
  16. 22 0
      .vscode/extensions.json
  17. 20 0
      .vscode/launch.json
  18. 19 0
      .vscode/settings.json
  19. 450 0
      CHANGELOG.md
  20. 21 0
      LICENSE
  21. 177 1
      README.md
  22. 179 0
      README.zh_CN.md
  23. 2 0
      build/config/index.ts
  24. 36 0
      build/config/proxy.ts
  25. 12 0
      build/config/time.ts
  26. 13 0
      build/plugins/html.ts
  27. 28 0
      build/plugins/index.ts
  28. 55 0
      build/plugins/router.ts
  29. 32 0
      build/plugins/unocss.ts
  30. 49 0
      build/plugins/unplugin.ts
  31. 24 0
      eslint.config.js
  32. 14 0
      index.html
  33. 112 0
      package.json
  34. 21 0
      packages/axios/package.json
  35. 5 0
      packages/axios/src/constant.ts
  36. 181 0
      packages/axios/src/index.ts
  37. 48 0
      packages/axios/src/options.ts
  38. 28 0
      packages/axios/src/shared.ts
  39. 101 0
      packages/axios/src/type.ts
  40. 20 0
      packages/axios/tsconfig.json
  41. 16 0
      packages/color/package.json
  42. 2 0
      packages/color/src/constant/index.ts
  43. 1579 0
      packages/color/src/constant/name.ts
  44. 356 0
      packages/color/src/constant/palette.ts
  45. 7 0
      packages/color/src/index.ts
  46. 176 0
      packages/color/src/palette/antd.ts
  47. 45 0
      packages/color/src/palette/index.ts
  48. 152 0
      packages/color/src/palette/recommend.ts
  49. 93 0
      packages/color/src/shared/colord.ts
  50. 2 0
      packages/color/src/shared/index.ts
  51. 49 0
      packages/color/src/shared/name.ts
  52. 58 0
      packages/color/src/types/index.ts
  53. 20 0
      packages/color/tsconfig.json
  54. 15 0
      packages/hooks/package.json
  55. 10 0
      packages/hooks/src/index.ts
  56. 31 0
      packages/hooks/src/use-boolean.ts
  57. 96 0
      packages/hooks/src/use-context.ts
  58. 49 0
      packages/hooks/src/use-count-down.ts
  59. 16 0
      packages/hooks/src/use-loading.ts
  60. 79 0
      packages/hooks/src/use-request.ts
  61. 50 0
      packages/hooks/src/use-svg-icon-render.ts
  62. 151 0
      packages/hooks/src/use-table.ts
  63. 20 0
      packages/hooks/tsconfig.json
  64. 20 0
      packages/materials/package.json
  65. 7 0
      packages/materials/src/index.ts
  66. 63 0
      packages/materials/src/libs/admin-layout/index.module.css
  67. 17 0
      packages/materials/src/libs/admin-layout/index.module.css.d.ts
  68. 5 0
      packages/materials/src/libs/admin-layout/index.ts
  69. 237 0
      packages/materials/src/libs/admin-layout/index.vue
  70. 68 0
      packages/materials/src/libs/admin-layout/shared.ts
  71. 3 0
      packages/materials/src/libs/color-picker/index.ts
  72. 116 0
      packages/materials/src/libs/color-picker/index.vue
  73. 53 0
      packages/materials/src/libs/page-tab/button-tab.vue
  74. 31 0
      packages/materials/src/libs/page-tab/chrome-tab-bg.vue
  75. 58 0
      packages/materials/src/libs/page-tab/chrome-tab.vue
  76. 97 0
      packages/materials/src/libs/page-tab/index.module.css
  77. 14 0
      packages/materials/src/libs/page-tab/index.module.css.d.ts
  78. 3 0
      packages/materials/src/libs/page-tab/index.ts
  79. 85 0
      packages/materials/src/libs/page-tab/index.vue
  80. 31 0
      packages/materials/src/libs/page-tab/shared.ts
  81. 31 0
      packages/materials/src/libs/page-tab/svg-close.vue
  82. 3 0
      packages/materials/src/libs/simple-scrollbar/index.ts
  83. 18 0
      packages/materials/src/libs/simple-scrollbar/index.vue
  84. 294 0
      packages/materials/src/types/index.ts
  85. 20 0
      packages/materials/tsconfig.json
  86. 15 0
      packages/ofetch/package.json
  87. 10 0
      packages/ofetch/src/index.ts
  88. 20 0
      packages/ofetch/tsconfig.json
  89. 3 0
      packages/scripts/bin.ts
  90. 27 0
      packages/scripts/package.json
  91. 10 0
      packages/scripts/src/commands/changelog.ts
  92. 5 0
      packages/scripts/src/commands/cleanup.ts
  93. 86 0
      packages/scripts/src/commands/git-commit.ts
  94. 6 0
      packages/scripts/src/commands/index.ts
  95. 12 0
      packages/scripts/src/commands/release.ts
  96. 90 0
      packages/scripts/src/commands/router.ts
  97. 5 0
      packages/scripts/src/commands/update-pkg.ts
  98. 55 0
      packages/scripts/src/config/index.ts
  99. 101 0
      packages/scripts/src/index.ts
  100. 7 0
      packages/scripts/src/shared/index.ts

+ 11 - 0
.editorconfig

@@ -0,0 +1,11 @@
+# Editor configuration, see http://editorconfig.org
+
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+end_of_line = lf
+trim_trailing_whitespace = true
+insert_final_newline = true

+ 45 - 0
.env

@@ -0,0 +1,45 @@
+VITE_BASE_URL=/
+
+VITE_APP_TITLE=SoybeanAdmin
+
+VITE_APP_DESC=SoybeanAdmin is a fresh and elegant admin template
+
+# the prefix of the icon name
+VITE_ICON_PREFIX=icon
+
+# the prefix of the local svg icon component, must include VITE_ICON_PREFIX
+# format {VITE_ICON_PREFIX}-{local icon name}
+VITE_ICON_LOCAL_PREFIX=icon-local
+
+# auth route mode: static | dynamic
+VITE_AUTH_ROUTE_MODE=static
+
+# static auth route home
+VITE_ROUTE_HOME=home
+
+# default menu icon
+VITE_MENU_ICON=mdi:menu
+
+# whether to enable http proxy when is dev mode
+VITE_HTTP_PROXY=N
+
+# vue-router mode: hash | history | memory
+VITE_ROUTER_HISTORY_MODE=history
+
+# success code of backend service, when the code is received, the request is successful
+VITE_SERVICE_SUCCESS_CODE=0000
+
+# logout codes of backend service, when the code is received, the user will be logged out and redirected to login page
+VITE_SERVICE_LOGOUT_CODES=8888,8889
+
+# modal logout codes of backend service, when the code is received, the user will be logged out by displaying a modal
+VITE_SERVICE_MODAL_LOGOUT_CODES=7777,7778
+
+# token expired codes of backend service, when the code is received, it will refresh the token and resend the request
+VITE_SERVICE_EXPIRED_TOKEN_CODES=9999,9998
+
+# when the route mode is static, the defined super role
+VITE_STATIC_SUPER_ROLE=R_SUPER
+
+# Used to differentiate storage across different domains
+VITE_STORAGE_PREFIX=SOY_

+ 7 - 0
.env.prod

@@ -0,0 +1,7 @@
+# backend service base url, prod environment
+VITE_SERVICE_BASE_URL=https://mock.apifox.cn/m1/3109515-0-default
+
+# other backend service base url, prod environment
+VITE_OTHER_SERVICE_BASE_URL= `{
+  "demo": "http://localhost:9529"
+}`

+ 7 - 0
.env.test

@@ -0,0 +1,7 @@
+# backend service base url, test environment
+VITE_SERVICE_BASE_URL=https://mock.apifox.cn/m1/3109515-0-default
+
+# other backend service base url, test environment
+VITE_OTHER_SERVICE_BASE_URL= `{
+  "demo": "http://localhost:9528"
+}`

+ 13 - 0
.gitattributes

@@ -0,0 +1,13 @@
+"*.vue"    eol=lf
+"*.js"     eol=lf
+"*.ts"     eol=lf
+"*.jsx"    eol=lf
+"*.tsx"    eol=lf
+"*.mjs"    eol=lf
+"*.json"   eol=lf
+"*.html"   eol=lf
+"*.css"    eol=lf
+"*.scss"   eol=lf
+"*.md"     eol=lf
+"*.yaml"   eol=lf
+"*.yml"    eol=lf

+ 90 - 0
.github/ISSUE_TEMPLATE/bug-report_cn.yaml

@@ -0,0 +1,90 @@
+name: 🐞 Bug提交
+description: 在使用软件或功能的过程中遇到了错误
+title: '[Bug]: '
+labels: [ "bug?" ]
+
+body:
+  - type: markdown
+    attributes:
+      value: |
+        ## 请按照以下要求进行提交
+        ### 1. 提交后需要指定标签和截止时间。
+        ---
+
+  - type: markdown
+    attributes:
+      value: |
+        ## 环境信息
+        请根据实际使用环境修改以下信息。
+
+  - type: input
+    id: env-program-ver
+    attributes:
+      label: 软件版本
+    validations:
+      required: true
+
+  - type: dropdown
+    id: env-vm-ver
+    attributes:
+      label: 运行环境
+      description: 选择运行软件的系统版本
+      options:
+        - Windows (64)
+        - Windows (32/x84)
+        - MacOS
+        - Linux
+        - Ubuntu
+        - CentOS
+        - ArchLinux
+        - UNIX (Android)
+        - 其它(请在下方说明)
+    validations:
+      required: true
+
+  - type: dropdown
+    id: env-vm-arch
+    attributes:
+      label: 运行架构
+      description: (可选) 选择运行软件的系统架构
+      options:
+        - AMD64
+        - x86
+        - ARM [32] (别名:AArch32 / ARMv7)
+        - ARM [64] (别名:AArch64 / ARMv8)
+        - 其它
+
+  - type: textarea
+    id: reproduce-steps
+    attributes:
+      label: 重现步骤
+      description: |
+        我们需要执行哪些操作才能让 bug 出现?
+        简洁清晰的重现步骤能够帮助我们更迅速地定位问题所在。
+    validations:
+      required: true
+
+  - type: textarea
+    id: expected
+    attributes:
+      label: 期望的结果是什么?
+    validations:
+      required: true
+
+  - type: textarea
+    id: actual
+    attributes:
+      label: 实际的结果是什么?
+    validations:
+      required: true
+
+  - type: textarea
+    id: logging
+    attributes:
+      label: 日志记录(可选)
+      render: golang
+
+  - type: textarea
+    id: extra-desc
+    attributes:
+      label: 补充说明(可选)

+ 90 - 0
.github/ISSUE_TEMPLATE/bug-report_en.yaml

@@ -0,0 +1,90 @@
+name: 🐞 Bug Report
+description: Encountered an error while using the software or feature
+title: '[Bug]: '
+labels: [ "bug?" ]
+
+body:
+  - type: markdown
+    attributes:
+      value: |
+        ## Please submit according to the following requirements
+        ### 1. After submission, you need to specify the label and deadline.
+        ---
+
+  - type: markdown
+    attributes:
+      value: |
+        ## Environment Information
+        Please modify the following information according to the actual usage environment.
+
+  - type: input
+    id: env-program-ver
+    attributes:
+      label: Software Version
+    validations:
+      required: true
+
+  - type: dropdown
+    id: env-vm-ver
+    attributes:
+      label: Operating Environment
+      description: Select the system version on which the software is running
+      options:
+        - Windows (64)
+        - Windows (32/x84)
+        - MacOS
+        - Linux
+        - Ubuntu
+        - CentOS
+        - ArchLinux
+        - UNIX (Android)
+        - Other (please specify below)
+    validations:
+      required: true
+
+  - type: dropdown
+    id: env-vm-arch
+    attributes:
+      label: Operating Architecture
+      description: (Optional) Select the system architecture on which the software is running
+      options:
+        - AMD64
+        - x86
+        - ARM [32] (Alias:AArch32 / ARMv7)
+        - ARM [64] (Alias:AArch64 / ARMv8)
+        - Other
+
+  - type: textarea
+    id: reproduce-steps
+    attributes:
+      label: Reproduce Steps
+      description: |
+        What operations do we need to perform to make the bug appear?
+        The concise and clear reproduction steps can help us locate the problem more quickly.
+    validations:
+      required: true
+
+  - type: textarea
+    id: expected
+    attributes:
+      label: What is the expected result?
+    validations:
+      required: true
+
+  - type: textarea
+    id: actual
+    attributes:
+      label: What is the actual result?
+    validations:
+      required: true
+
+  - type: textarea
+    id: logging
+    attributes:
+      label: Logging (Optional)
+      render: golang
+
+  - type: textarea
+    id: extra-desc
+    attributes:
+      label: Additional Description (Optional)

+ 48 - 0
.github/ISSUE_TEMPLATE/feature_request_cn.yaml

@@ -0,0 +1,48 @@
+---
+name: 🚀 功能请求
+description: 提出一个想法以帮助我们改进W&B
+title: "[功能]: "
+labels:
+  - "功能请求"
+
+body:
+  - type: markdown
+    attributes:
+      value: |
+        **感谢 :heart: 您花时间填写此功能请求报告!**
+        我们恳请您搜索看看您的功能是否[已经存在](https://github.com/soybeanjs/soybean-admin/issues?q=is%3Aissue+sort%3Acreated-desc+)。
+
+        我们也很乐意接受用户的贡献。有关更多详细信息,请参阅[此处](https://github.com/soybeanjs/soybean-admin/blob/main/README.zh_CN.md#%E5%A6%82%E4%BD%95%E8%B4%A1%E7%8C%AE)。
+
+  - type: textarea
+    attributes:
+      label: 描述
+      description: |
+        对您感兴趣的功能的清晰简洁的描述。
+    validations:
+      required: true
+
+  - type: textarea
+    attributes:
+      label: 建议的解决方案
+      description: |
+        描述您想要的解决方案。对您希望发生的事情的清晰简洁的描述。
+    validations:
+      required: true
+
+  - type: textarea
+    attributes:
+      label: 替代方案
+      description: |
+        描述您考虑过的替代方案。
+        对您考虑过的任何替代解决方案或功能的清晰简洁的描述。
+    validations:
+      required: false
+
+  - type: textarea
+    attributes:
+      label: 额外的上下文
+      description: |
+        在此处添加有关问题的其他上下文。
+    validations:
+      required: false

+ 48 - 0
.github/ISSUE_TEMPLATE/feature_request_en.yaml

@@ -0,0 +1,48 @@
+---
+name: 🚀 Feature Request
+description: Suggest an idea to help us improve W&B
+title: "[Feature]: "
+labels:
+  - "feature_request"
+
+body:
+  - type: markdown
+    attributes:
+      value: |
+        **Thanks :heart: for taking the time to fill out this feature request report!**
+        We kindly ask that you search to see if an issue [already exists](https://github.com/soybeanjs/soybean-admin/issues?q=is%3Aissue+sort%3Acreated-desc+) for your feature.
+
+        We are also happy to accept contributions from our users. For more details see [here](https://github.com/soybeanjs/soybean-admin/blob/main/README.md#how-to-contribute).
+
+  - type: textarea
+    attributes:
+      label: Description
+      description: |
+        A clear and concise description of the feature you're interested in.
+    validations:
+      required: true
+
+  - type: textarea
+    attributes:
+      label: Suggested Solution
+      description: |
+        Describe the solution you'd like. A clear and concise description of what you want to happen.
+    validations:
+      required: true
+
+  - type: textarea
+    attributes:
+      label: Alternatives
+      description: |
+        Describe alternatives you've considered.
+        A clear and concise description of any alternative solutions or features you've considered.
+    validations:
+      required: false
+
+  - type: textarea
+    attributes:
+      label: Additional Context
+      description: |
+        Add any other context about the problem here.
+    validations:
+      required: false

+ 50 - 0
.github/PULL_REQUEST_TEMPLATE/pr_cn.md

@@ -0,0 +1,50 @@
+首先,感谢你的贡献! 😄
+
+新特性请提交至 feature 分支,其余可提交至 main 分支。在一个维护者审核通过后合并。请确保填写以下 pull request 的信息,谢谢!~
+
+[[English Template / 英文模板](./pr_en.md)]
+
+### 这个变动的性质是
+
+- [ ] 新特性提交
+- [ ] 日常 bug 修复
+- [ ] 站点、文档改进
+- [ ] 组件样式改进
+- [ ] TypeScript 定义更新
+- [ ] 重构
+- [ ] 代码风格优化
+- [ ] 分支合并
+- [ ] 其他改动(是关于什么的改动?)
+
+### 需求背景
+
+> 1. 描述相关需求的来源。
+> 2. 要解决的问题。
+> 3. 相关的 issue 讨论链接。
+
+### 实现方案和 API(非新功能可选)
+
+> 1. 基本的解决思路和其他可选方案。
+> 2. 列出最终的 API 实现和用法。
+> 3. 涉及 UI/交互变动需要有截图或 GIF。
+
+### 对用户的影响和可能的风险(非新功能可选)
+
+> 1. 这个改动对用户端是否有影响?影响的方面有哪些?
+> 2. 是否有可能隐含的 break change 和其他风险?
+
+### Changelog 描述(非新功能可选)
+
+> 1. 英文描述
+> 2. 中文描述(可选)
+
+### 请求合并前的自查清单
+
+- [ ] 文档已补充或无须补充
+- [ ] 代码演示已提供或无须提供
+- [ ] TypeScript 定义已补充或无须补充
+- [ ] Changelog 已提供或无须提供
+
+### 后续计划(非新功能可选)
+
+> 如果这个提交后面还有相关的其他提交和跟进信息,可以写在这里。

+ 51 - 0
.github/PULL_REQUEST_TEMPLATE/pr_en.md

@@ -0,0 +1,51 @@
+First of all, thank you for your contribution! 😄
+
+New feature please send pull request to feature branch, and rest to main branch. Pull request will be merged after one of collaborators approve. Please makes sure that these form are filled before submitting your pull request, thank you!
+
+[[中文版模板 / Chinese template](./pr_cn.md)]
+
+### This is a ...
+
+- [ ] New feature
+- [ ] Bug fix
+- [ ] Site / document update
+- [ ] Component style update
+- [ ] TypeScript definition update
+- [ ] Refactoring
+- [ ] Code style optimization
+- [ ] Branch merge
+- [ ] Other (about what?)
+
+### What's the background?
+
+> 1. Describe the source of requirement.
+> 2. Resolve what problem.
+> 3. Related issue link.
+
+### API Realization (Optional if not new feature)
+
+> 1. Basic thought of solution and other optional proposal.
+> 2. List final API realization and usage sample.
+> 3. GIF or snapshot should be provided if includes UI/interactive modification.
+
+### What's the effect? (Optional if not new feature)
+
+> 1. Does this PR affect user? Which part will be affected?
+> 2. What will say in changelog?
+> 3. Does this PR contains potential break change or other risk?
+
+### Changelog description (Optional if not new feature)
+
+> 1. English description
+> 2. Chinese description (optional)
+
+### Self Check before Merge
+
+- [ ] Doc is updated/provided or not needed
+- [ ] Demo is updated/provided or not needed
+- [ ] TypeScript definition is updated/provided or not needed
+- [ ] Changelog is provided or not needed
+
+### Additional Plan? (Optional if not new feature)
+
+> If this PR related with other PR or following info. You can type here.

+ 30 - 0
.github/workflows/linter.yml

@@ -0,0 +1,30 @@
+---
+name: Lint Code
+
+permissions:
+  contents: write
+
+on:
+  pull_request:
+    branches: [main]
+
+jobs:
+  lint:
+    name: Lint All Code
+    runs-on: ubuntu-latest
+
+    steps:
+      - name: Checkout Code
+        uses: actions/checkout@v3
+        with:
+          fetch-depth: 0
+
+      - name: Lint Code Base
+        uses: github/super-linter@v4
+        env:
+          VALIDATE_ALL_CODEBASE: false
+          DEFAULT_BRANCH: main
+          # To change branch master or main
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          FILTER_REGEX_EXCLUDE: (docs|.github)
+          VALIDATE_MARKDOWN: false

+ 25 - 0
.github/workflows/release.yml

@@ -0,0 +1,25 @@
+name: Release
+
+permissions:
+  contents: write
+
+on:
+  push:
+    tags:
+      - "v*"
+
+jobs:
+  release:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v3
+        with:
+          fetch-depth: 0
+
+      - uses: actions/setup-node@v3
+        with:
+          node-version: 18.x
+
+      - run: npx githublogen
+        env:
+          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}

+ 36 - 0
.gitignore

@@ -0,0 +1,36 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+.DS_Store
+dist
+dist-ssr
+coverage
+*.local
+
+/cypress/videos/
+/cypress/screenshots/
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+!.vscode/settings.json
+!.vscode/launch.json
+.idea
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+
+package-lock.json
+yarn.lock
+
+.VSCodeCounter
+**/.vitepress/cache

+ 4 - 0
.npmrc

@@ -0,0 +1,4 @@
+registry=https://registry.npmmirror.com/
+shamefully-hoist=true
+ignore-workspace-root-check=true
+link-workspace-packages=true

+ 22 - 0
.vscode/extensions.json

@@ -0,0 +1,22 @@
+{
+  "recommendations": [
+    "afzalsayed96.icones",
+    "antfu.iconify",
+    "antfu.unocss",
+    "dbaeumer.vscode-eslint",
+    "editorconfig.editorconfig",
+    "esbenp.prettier-vscode",
+    "formulahendry.auto-close-tag",
+    "formulahendry.auto-complete-tag",
+    "formulahendry.auto-rename-tag",
+    "lokalise.i18n-ally",
+    "mhutchie.git-graph",
+    "mikestead.dotenv",
+    "naumovs.color-highlight",
+    "pkief.material-icon-theme",
+    "sdras.vue-vscode-snippets",
+    "vue.volar",
+    "whtouche.vscode-js-console-utils",
+    "zhuangtongfa.material-theme"
+  ]
+}

+ 20 - 0
.vscode/launch.json

@@ -0,0 +1,20 @@
+{
+  "version": "0.2.0",
+  "configurations": [
+    {
+      "type": "chrome",
+      "request": "launch",
+      "name": "Vue Debugger",
+      "url": "http://localhost:9527",
+      "webRoot": "${workspaceFolder}"
+    },
+    {
+      "type": "node",
+      "request": "launch",
+      "name": "TS Debugger",
+      "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/tsx",
+      "skipFiles": ["<node_internals>/**", "${workspaceFolder}/node_modules/**"],
+      "program": "${file}"
+    }
+  ]
+}

+ 19 - 0
.vscode/settings.json

@@ -0,0 +1,19 @@
+{
+  "editor.codeActionsOnSave": {
+    "source.fixAll.eslint": "explicit",
+    "source.organizeImports": "never"
+  },
+  "eslint.experimental.useFlatConfig": true,
+  "editor.formatOnSave": false,
+  "eslint.validate": ["html", "css", "scss", "json", "jsonc"],
+  "i18n-ally.displayLanguage": "zh-cn",
+  "i18n-ally.enabledParsers": ["ts"],
+  "i18n-ally.enabledFrameworks": ["vue"],
+  "i18n-ally.editor.preferEditor": true,
+  "i18n-ally.keystyle": "nested",
+  "i18n-ally.localesPaths": ["src/locales/langs"],
+  "prettier.enable": false,
+  "typescript.tsdk": "node_modules/typescript/lib",
+  "unocss.root": ["./"],
+  "vue.server.hybridMode": true
+}

+ 450 - 0
CHANGELOG.md

@@ -0,0 +1,450 @@
+# Changelog
+
+
+## [v1.2.6](https://github.com/soybeanjs/soybean-admin-antd/compare/v1.2.5...v1.2.6) (2024-06-21)
+
+### &nbsp;&nbsp;&nbsp;🐞 Bug Fixes
+
+- **projects**:
+  - request modal title use i18n &nbsp;-&nbsp; by @honghuangdc [<samp>(f97e7)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/f97e74e)
+  - add `getDataByPage` for `useTable` &nbsp;-&nbsp; by @honghuangdc [<samp>(cf265)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/cf2659d)
+  - fix login redirect to routeHome when routeHome of dynamic route is not same as static route &nbsp;-&nbsp; by @honghuangdc [<samp>(f6c66)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/f6c66ec)
+
+### &nbsp;&nbsp;&nbsp;📖 Documentation
+
+- **projects**: update README &nbsp;-&nbsp; by @honghuangdc [<samp>(a769b)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/a769b1a)
+
+### &nbsp;&nbsp;&nbsp;🏡 Chore
+
+- **deps**: update deps. &nbsp;-&nbsp; by @Azir-11 in https://github.com/soybeanjs/soybean-admin-antd/issues/30 [<samp>(e110d)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/e110dd0)
+
+### &nbsp;&nbsp;&nbsp;🤖 CI
+
+- **projects**: add and update github issues template &nbsp;-&nbsp; by @honghuangdc [<samp>(a3619)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/a361927)
+
+### &nbsp;&nbsp;&nbsp;❤️ Contributors
+
+[![honghuangdc](https://github.com/honghuangdc.png?size=48)](https://github.com/honghuangdc)&nbsp;&nbsp;[![Azir-11](https://github.com/Azir-11.png?size=48)](https://github.com/Azir-11)&nbsp;&nbsp;
+
+## [v1.2.5](https://github.com/soybeanjs/soybean-admin-antd/compare/v1.2.4...v1.2.5) (2024-06-15)
+
+### &nbsp;&nbsp;&nbsp;🐞 Bug Fixes
+
+- **projects**: Fix the issue of abnormal tab caching after logout &nbsp;-&nbsp; by @soybeanjs [<samp>(e6349)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/e63490e)
+
+### &nbsp;&nbsp;&nbsp;🛠 Optimizations
+
+- **projects**: Initializing the static routing function does not require asynchronization &nbsp;-&nbsp; by @soybeanjs [<samp>(02d66)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/02d66cb)
+- **types**: Enhance compatibility of global types &nbsp;-&nbsp; by @soybeanjs [<samp>(dc359)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/dc359f5)
+- **utils**: Reduce code indentation and improve readability &nbsp;-&nbsp; by @soybeanjs [<samp>(54ae8)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/54ae82b)
+
+### &nbsp;&nbsp;&nbsp;🏡 Chore
+
+- **deps**: update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(423fa)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/423fafc)
+
+### &nbsp;&nbsp;&nbsp;❤️ Contributors
+
+[![soybeanjs](https://github.com/soybeanjs.png?size=48)](https://github.com/soybeanjs)&nbsp;&nbsp;
+
+## [v1.2.4](https://github.com/soybeanjs/soybean-admin-antd/compare/v1.2.3...v1.2.4) (2024-06-14)
+
+### &nbsp;&nbsp;&nbsp;🛠 Optimizations
+
+- **projects**:
+  - optimize `setupAppVersionNotification` &nbsp;-&nbsp; by @soybeanjs [<samp>(f061d)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/f061d8b)
+  - get buildTime with timezone 'Asia/Shanghai' &nbsp;-&nbsp; by @soybeanjs [<samp>(b256a)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/b256a3e)
+
+### &nbsp;&nbsp;&nbsp;🤖 CI
+
+- **projects**: add github config &nbsp;-&nbsp; by @soybeanjs [<samp>(fe15a)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/fe15a9a)
+
+### &nbsp;&nbsp;&nbsp;❤️ Contributors
+
+[![soybeanjs](https://github.com/soybeanjs.png?size=48)](https://github.com/soybeanjs)&nbsp;&nbsp;
+
+## [v1.2.3](https://github.com/soybeanjs/soybean-admin-antd/compare/v1.2.2...v1.2.3) (2024-06-13)
+
+### &nbsp;&nbsp;&nbsp;🐞 Bug Fixes
+
+- **projects**:
+  - fix mobile browser theme issue by adding color-scheme meta tag to index.html &nbsp;-&nbsp; by @KickCashew in https://github.com/soybeanjs/soybean-admin-antd/issues/27 [<samp>(6bf97)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/6bf972d)
+  - Fix secondary directory components is empty &nbsp;-&nbsp; by @paynezhuang in https://github.com/soybeanjs/soybean-admin-antd/issues/29 [<samp>(fb48f)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/fb48f41)
+
+### &nbsp;&nbsp;&nbsp;🛠 Optimizations
+
+- **projects**:
+  - optimize table scrollConfig. fixed #25 &nbsp;-&nbsp; by @soybeanjs in https://github.com/soybeanjs/soybean-admin-antd/issues/25 [<samp>(d0f8e)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/d0f8e55)
+  - optimize table rowSelection &nbsp;-&nbsp; by @soybeanjs [<samp>(49c57)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/49c572b)
+
+### &nbsp;&nbsp;&nbsp;📖 Documentation
+
+- **projects**:
+  - Fixed the hyperlink pointing error &nbsp;-&nbsp; by **Azir** [<samp>(d5602)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/d5602ed)
+  - update README &nbsp;-&nbsp; by @soybeanjs [<samp>(9527e)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/9527ead)
+
+### &nbsp;&nbsp;&nbsp;🏡 Chore
+
+- **deps**:
+  - update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(9954c)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/9954c8e)
+  - update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(d9f9f)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/d9f9f9f)
+
+### &nbsp;&nbsp;&nbsp;🎨 Styles
+
+- **projects**:
+  - unify button icon style. close #26 &nbsp;-&nbsp; by @soybeanjs in https://github.com/soybeanjs/soybean-admin-antd/issues/26 [<samp>(ef60e)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/ef60e2c)
+  - fit table column width of checkbox &nbsp;-&nbsp; by @soybeanjs [<samp>(ebcfb)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/ebcfbe1)
+
+### &nbsp;&nbsp;&nbsp;❤️ Contributors
+
+[![soybeanjs](https://github.com/soybeanjs.png?size=48)](https://github.com/soybeanjs)&nbsp;&nbsp;[![paynezhuang](https://github.com/paynezhuang.png?size=48)](https://github.com/paynezhuang)&nbsp;&nbsp;[![KickCashew](https://github.com/KickCashew.png?size=48)](https://github.com/KickCashew)&nbsp;&nbsp;
+[Azir](mailto:2075125282@qq.com)
+
+## [v1.2.2](https://github.com/soybeanjs/soybean-admin-antd/compare/v1.2.1...v1.2.2) (2024-06-12)
+
+### &nbsp;&nbsp;&nbsp;🚀 Features
+
+- **projects**: reset scroll position when tab change &nbsp;-&nbsp; by @soybeanjs [<samp>(804f9)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/804f9e6)
+
+### &nbsp;&nbsp;&nbsp;🐞 Bug Fixes
+
+- **projects**:
+  - hide AppVersionNotification in DEV mode &nbsp;-&nbsp; by @soybeanjs [<samp>(e3d91)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/e3d91df)
+  - fix menu-toggler hidden in mobile layout &nbsp;-&nbsp; by @soybeanjs [<samp>(a73d2)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/a73d2e1)
+
+### &nbsp;&nbsp;&nbsp;📖 Documentation
+
+- **projects**: update README &nbsp;-&nbsp; by @soybeanjs [<samp>(bab68)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/bab6817)
+
+### &nbsp;&nbsp;&nbsp;❤️ Contributors
+
+[![soybeanjs](https://github.com/soybeanjs.png?size=48)](https://github.com/soybeanjs)&nbsp;&nbsp;
+
+## [v1.2.1](https://github.com/soybeanjs/soybean-admin-antd/compare/v1.2.0...v1.2.1) (2024-06-07)
+
+### &nbsp;&nbsp;&nbsp;🐞 Bug Fixes
+
+- **projects**:
+  - fix get user info when page reload &nbsp;-&nbsp; by @soybeanjs [<samp>(0f6b7)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/0f6b73f)
+  - fix setupAppVersionNotification render &nbsp;-&nbsp; by @soybeanjs [<samp>(63d5d)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/63d5dc5)
+
+### &nbsp;&nbsp;&nbsp;🏡 Chore
+
+- **deps**: update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(a6c0e)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/a6c0eae)
+
+### &nbsp;&nbsp;&nbsp;❤️ Contributors
+
+[![soybeanjs](https://github.com/soybeanjs.png?size=48)](https://github.com/soybeanjs)&nbsp;&nbsp;
+
+## [v1.2.0](https://github.com/soybeanjs/soybean-admin-antd/compare/undefined...v1.2.0) (2024-06-07)
+
+### &nbsp;&nbsp;&nbsp;🚀 Features
+
+- **hooks**:
+  - add @sa/hooks &nbsp;-&nbsp; by @soybeanjs [<samp>(7677a)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/7677af5)
+  - add use-echarts &nbsp;-&nbsp; by @soybeanjs [<samp>(ada3f)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/ada3f98)
+  - @sa/hooks: add useTable &nbsp;-&nbsp; by @soybeanjs [<samp>(348b4)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/348b4f6)
+  - add setOptions for useEcharts &nbsp;-&nbsp; by @soybeanjs [<samp>(40ceb)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/40cebab)
+- **packages**:
+  - @sa/scripts: add new commit type optimize and commit scope packages &nbsp;-&nbsp; by @soybeanjs [<samp>(ee111)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/ee1119e)
+- **projects**:
+  - init &nbsp;-&nbsp; by @soybeanjs [<samp>(77b6e)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/77b6e97)
+  - add switch-lang, add look-forward &nbsp;-&nbsp; by @soybeanjs [<samp>(0c666)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/0c66695)
+  - add logout &nbsp;-&nbsp; by @soybeanjs [<samp>(486de)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/486dec4)
+  - add login module &nbsp;-&nbsp; by @soybeanjs [<samp>(314e8)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/314e813)
+  - add theme palette and colors support unocss color opacity &nbsp;-&nbsp; by @soybeanjs [<samp>(88e64)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/88e6438)
+  - nprogress color feat theme color &nbsp;-&nbsp; by @soybeanjs [<samp>(d7621)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/d7621d8)
+  - perf route meta and route i18n &nbsp;-&nbsp; by @soybeanjs [<samp>(a2e0f)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/a2e0f47)
+  - route store &nbsp;-&nbsp; by @soybeanjs [<samp>(a7784)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/a77843c)
+  - menu in route store &nbsp;-&nbsp; by @soybeanjs [<samp>(81e59)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/81e59d3)
+  - perf menu and add cache in route store &nbsp;-&nbsp; by @soybeanjs [<samp>(5c60b)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/5c60b15)
+  - antd menu &nbsp;-&nbsp; by @soybeanjs [<samp>(542e5)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/542e5d3)
+  - add menu icon &nbsp;-&nbsp; by @soybeanjs [<samp>(60f29)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/60f29ed)
+  - add axios &nbsp;-&nbsp; by @soybeanjs [<samp>(110fc)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/110fce6)
+  - update @elegant-router/vue and fix types &nbsp;-&nbsp; by @soybeanjs [<samp>(b7632)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/b76329b)
+  - service and proxy &nbsp;-&nbsp; by @soybeanjs [<samp>(ed68c)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/ed68c8f)
+  - login, antdv useApp, form &nbsp;-&nbsp; by @soybeanjs [<samp>(53c20)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/53c2010)
+  - router guide &nbsp;-&nbsp; by @soybeanjs [<samp>(5cf87)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/5cf87e1)
+  - add login loading to confirm button &nbsp;-&nbsp; by @soybeanjs [<samp>(cb6c7)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/cb6c7d1)
+  - add page manage and remove page user &nbsp;-&nbsp; by @soybeanjs [<samp>(73b02)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/73b023c)
+  - menu click to switch route &nbsp;-&nbsp; by @soybeanjs [<samp>(60ef0)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/60ef0af)
+  - keep antd menu transition same as system &nbsp;-&nbsp; by @soybeanjs [<samp>(77c96)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/77c9623)
+  - add tab store, PageTab &nbsp;-&nbsp; by @soybeanjs [<samp>(ecd0d)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/ecd0df2)
+  - add LangSwitch to component, fix login page i18n &nbsp;-&nbsp; by @soybeanjs [<samp>(e435b)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/e435b18)
+  - add tab dropdown, FullScreen &nbsp;-&nbsp; by @soybeanjs [<samp>(dcc4c)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/dcc4c86)
+  - add ThemeDrawer &nbsp;-&nbsp; by @soybeanjs [<samp>(22447)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/2244788)
+  - add ReloadButton &nbsp;-&nbsp; by @soybeanjs [<samp>(66b90)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/66b9070)
+  - add ButtonIcon with tooltip &nbsp;-&nbsp; by @soybeanjs [<samp>(4649d)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/4649dec)
+  - support mobile layout &nbsp;-&nbsp; by @soybeanjs [<samp>(cfcf8)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/cfcf835)
+  - add page animate &nbsp;-&nbsp; by @soybeanjs [<samp>(41c38)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/41c384f)
+  - add beadcrumb &nbsp;-&nbsp; by @soybeanjs [<samp>(d9279)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/d927989)
+  - routes cache &nbsp;-&nbsp; by @soybeanjs [<samp>(073e6)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/073e615)
+  - cache tabs &nbsp;-&nbsp; by @soybeanjs [<samp>(1f439)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/1f4398d)
+  - add reset routes &nbsp;-&nbsp; by @soybeanjs [<samp>(76400)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/76400a8)
+  - themeDrawer layout mode &nbsp;-&nbsp; by @soybeanjs [<samp>(8c36d)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/8c36db7)
+  - add horizontal menu &nbsp;-&nbsp; by @soybeanjs [<samp>(012d1)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/012d1e8)
+  - menu style adapter system &nbsp;-&nbsp; by @soybeanjs [<samp>(32a2d)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/32a2d98)
+  - vertical-mix sider &nbsp;-&nbsp; by @soybeanjs [<samp>(c9329)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/c9329f9)
+  - HorizontalMixMenu &nbsp;-&nbsp; by @soybeanjs [<samp>(2d59d)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/2d59dd6)
+  - theme settings theme colors &nbsp;-&nbsp; by @soybeanjs [<samp>(5d883)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/5d883f5)
+  - finish theme settings &nbsp;-&nbsp; by @soybeanjs [<samp>(92b4c)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/92b4c18)
+  - theme settings copy &nbsp;-&nbsp; by @soybeanjs [<samp>(95e38)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/95e38c9)
+  - iconify offline &nbsp;-&nbsp; by @soybeanjs [<samp>(388e5)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/388e504)
+  - antd locale, dayjs locale &nbsp;-&nbsp; by @soybeanjs [<samp>(3b33c)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/3b33ce9)
+  - support Vite5 &nbsp;-&nbsp; by @soybeanjs [<samp>(8e41a)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/8e41a8f)
+  - request pkgs &nbsp;-&nbsp; by @soybeanjs [<samp>(81a31)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/81a312f)
+  - update @sa/axios &nbsp;-&nbsp; by @soybeanjs [<samp>(fd790)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/fd790fa)
+  - @sa/axios: createRequest, createFlatRequest, createHookRequest &nbsp;-&nbsp; by @soybeanjs [<samp>(c5c45)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/c5c456b)
+  - add sort routes &nbsp;-&nbsp; by @soybeanjs [<samp>(637c6)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/637c64a)
+  - add app loading &nbsp;-&nbsp; by @soybeanjs [<samp>(a7ef1)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/a7ef1a5)
+  - add vitepress docs &nbsp;-&nbsp; by @soybeanjs [<samp>(5f72f)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/5f72fa8)
+  - add copyright, unocss shortcut: card-wrapper, update package.json &nbsp;-&nbsp; by @soybeanjs [<samp>(a11b9)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/a11b955)
+  - add page: about &nbsp;-&nbsp; by @soybeanjs [<samp>(a2b6b)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/a2b6b4e)
+  - add custom route exception &nbsp;-&nbsp; by @soybeanjs [<samp>(0bab9)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/0bab941)
+  - filter tabs which are not in routes &nbsp;-&nbsp; by @soybeanjs [<samp>(b047c)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/b047c2e)
+  - packages/scripts: add command changelog,release &nbsp;-&nbsp; by @soybeanjs [<samp>(50926)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/50926f3)
+  - add script: gen-route &nbsp;-&nbsp; by @soybeanjs [<samp>(e9b15)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/e9b1571)
+  - @sa/axios: add qs stringify for params &nbsp;-&nbsp; by @soybeanjs [<samp>(6ee5a)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/6ee5a21)
+  - page home & perf useEcharts &nbsp;-&nbsp; by @soybeanjs [<samp>(37c92)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/37c928f)
+  - add page function_tab &nbsp;-&nbsp; by @soybeanjs [<samp>(bd645)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/bd64563)
+  - support directory menu hide all child menus. &nbsp;-&nbsp; by @soybeanjs [<samp>(cd72e)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/cd72e7a)
+  - support route query in menu &nbsp;-&nbsp; by @soybeanjs [<samp>(063f5)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/063f563)
+  - login page: code-login &nbsp;-&nbsp; by @soybeanjs [<samp>(f4e7c)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/f4e7c92)
+  - login page: register &nbsp;-&nbsp; by @soybeanjs [<samp>(b6420)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/b642032)
+  - @sa/axios: support instance state, fix flatRequest error type &nbsp;-&nbsp; by @soybeanjs [<samp>(3f418)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/3f41811)
+  - add request exception &nbsp;-&nbsp; by @soybeanjs [<samp>(0b1ec)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/0b1ece5)
+  - add function page: request &nbsp;-&nbsp; by @soybeanjs [<samp>(757d1)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/757d1d6)
+  - add function page: hide-child,super-page,toggle-auth &nbsp;-&nbsp; by @soybeanjs [<samp>(886ea)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/886ea57)
+  - manage page: manage_role &nbsp;-&nbsp; by @soybeanjs [<samp>(658dc)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/658dc94)
+  - perf page manage_role &nbsp;-&nbsp; by @soybeanjs [<samp>(55945)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/5594558)
+  - Synchronize role management and menu management &nbsp;-&nbsp; by @mufeng889 in https://github.com/soybeanjs/soybean-admin-antd/issues/13 [<samp>(7943c)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/7943ce2)
+  - Syncronize user management list page &nbsp;-&nbsp; by @mufeng889 in https://github.com/soybeanjs/soybean-admin-antd/issues/18 [<samp>(64be7)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/64be7d2)
+  - Synchronous user management &nbsp;-&nbsp; by @mufeng889 in https://github.com/soybeanjs/soybean-admin-antd/issues/19 [<samp>(68231)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/6823194)
+  - support grayscale &nbsp;-&nbsp; by @soybeanjs [<samp>(db2a5)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/db2a5bb)
+  - add prefix to local storage &nbsp;-&nbsp; by @soybeanjs [<samp>(0423c)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/0423cb1)
+  - support iframe page with diffrent url of custom route &nbsp;-&nbsp; by @soybeanjs [<samp>(4975d)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/4975d11)
+  - add recommend color switch &nbsp;-&nbsp; by @soybeanjs [<samp>(7fa51)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/7fa5136)
+  - support repeated request errors occur once in a short time &nbsp;-&nbsp; by @soybeanjs [<samp>(6e809)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/6e80963)
+  - close tab by mouse wheel button click &nbsp;-&nbsp; by @soybeanjs [<samp>(31e1d)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/31e1d4e)
+  - support system new version update notification &nbsp;-&nbsp; by @soybeanjs [<samp>(cee03)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/cee03ed)
+  - get user info in router guard and remove in localStorage &nbsp;-&nbsp; by @soybeanjs [<samp>(ccbe3)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/ccbe39d)
+
+### &nbsp;&nbsp;&nbsp;🐞 Bug Fixes
+
+- **components**:
+  - 修复主题模式按钮显示与点击的异常 &nbsp;-&nbsp; by @Azir-11 in https://github.com/soybeanjs/soybean-admin-antd/issues/21 [<samp>(03e09)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/03e094e)
+- **hooks**:
+  - Fixed abnormal display of table pagers &nbsp;-&nbsp; by @mufeng889 in https://github.com/soybeanjs/soybean-admin-antd/issues/17 [<samp>(cc256)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/cc2562b)
+- **projects**:
+  - fix elegant-router plugin &nbsp;-&nbsp; by @soybeanjs [<samp>(282b6)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/282b602)
+  - keep hsla and rgba consistent with unocss &nbsp;-&nbsp; by @soybeanjs [<samp>(566cc)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/566cce2)
+  - fix menu style &nbsp;-&nbsp; by @soybeanjs [<samp>(b80a0)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/b80a086)
+  - update tab label when locale change &nbsp;-&nbsp; by @soybeanjs [<samp>(50b72)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/50b722c)
+  - fix menu style &nbsp;-&nbsp; by @soybeanjs [<samp>(231fc)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/231fc8b)
+  - fix zIndex &nbsp;-&nbsp; by @soybeanjs [<samp>(eed33)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/eed33a6)
+  - fix ThemeButton tooltip trigger &nbsp;-&nbsp; by @soybeanjs [<samp>(19671)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/196710d)
+  - fix menu collapsed &nbsp;-&nbsp; by @soybeanjs [<samp>(586d6)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/586d6d7)
+  - fix siderCollapse &nbsp;-&nbsp; by @soybeanjs [<samp>(d348b)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/d348bd8)
+  - fix tab click when is mobile &nbsp;-&nbsp; by @soybeanjs [<samp>(ac6c3)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/ac6c324)
+  - notification add auto close &nbsp;-&nbsp; by @soybeanjs [<samp>(2cfcb)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/2cfcb44)
+  - hide some content when is mobile layout &nbsp;-&nbsp; by @soybeanjs [<samp>(b175f)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/b175fea)
+  - fix breadcrumb when is not route menu &nbsp;-&nbsp; by @soybeanjs [<samp>(59d35)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/59d3511)
+  - fix button style &nbsp;-&nbsp; by @soybeanjs [<samp>(93f18)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/93f1806)
+  - fix themeVars color &nbsp;-&nbsp; by @soybeanjs [<samp>(6329c)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/6329c5d)
+  - fix cached tabs locale &nbsp;-&nbsp; by @soybeanjs [<samp>(0973b)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/0973b08)
+  - fix inverted menu when is horizontal-mix &nbsp;-&nbsp; by @soybeanjs [<samp>(1530d)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/1530dae)
+  - fix horizontal menu style &nbsp;-&nbsp; by @soybeanjs [<samp>(b3fce)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/b3fce71)
+  - theme setting adapter mobile &nbsp;-&nbsp; by @soybeanjs [<samp>(8c729)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/8c72966)
+  - fix theme colors &nbsp;-&nbsp; by @soybeanjs [<samp>(ab60e)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/ab60edc)
+  - fix customize info color &nbsp;-&nbsp; by @soybeanjs [<samp>(f0f02)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/f0f0236)
+  - fix scrollbar style &nbsp;-&nbsp; by @soybeanjs [<samp>(cadf2)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/cadf2e8)
+  - fix ColorPicker &nbsp;-&nbsp; by @soybeanjs [<samp>(c735f)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/c735fd3)
+  - sider inverted visible &nbsp;-&nbsp; by @soybeanjs [<samp>(7b46f)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/7b46f2b)
+  - fix override theme &nbsp;-&nbsp; by @soybeanjs [<samp>(76eda)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/76edab3)
+  - fix default menu icon &nbsp;-&nbsp; by @soybeanjs [<samp>(68e0d)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/68e0d19)
+  - fix vertical-mix menu when collapse &nbsp;-&nbsp; by @soybeanjs [<samp>(c4e64)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/c4e6471)
+  - fix layout-card &nbsp;-&nbsp; by @soybeanjs [<samp>(b2b92)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/b2b9256)
+  - fix scripts &nbsp;-&nbsp; by @soybeanjs [<samp>(780eb)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/780ebf5)
+  - fix app loading theme color &nbsp;-&nbsp; by @soybeanjs [<samp>(1ad8d)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/1ad8dbf)
+  - fix base-menu style &nbsp;-&nbsp; by @soybeanjs [<samp>(dcbb3)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/dcbb302)
+  - remove space in tab content &nbsp;-&nbsp; by @soybeanjs [<samp>(16c76)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/16c765f)
+  - fix add import $t &nbsp;-&nbsp; by @soybeanjs [<samp>(2e36f)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/2e36fbf)
+  - fix request msg &nbsp;-&nbsp; by @soybeanjs [<samp>(47b54)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/47b54b0)
+  - fix tab error displaying parent localIcon &nbsp;-&nbsp; by @soybeanjs [<samp>(9d112)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/9d11258)
+  - fix build [unocss]: build failed to load icon "close" &nbsp;-&nbsp; by @soybeanjs [<samp>(6a5aa)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/6a5aaec)
+  - fix home when is dynamic mode router &nbsp;-&nbsp; by @soybeanjs [<samp>(3c229)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/3c2296e)
+  - fix homeTab closeRight and disable colseLeft &nbsp;-&nbsp; by @soybeanjs [<samp>(53f61)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/53f6166)
+  - fix GlobalBreadcrumb warning &nbsp;-&nbsp; by @soybeanjs [<samp>(dd6fc)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/dd6fce3)
+  - add maxWidth for GlobalTab to fix bg with gap &nbsp;-&nbsp; by @soybeanjs [<samp>(423ea)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/423ea0d)
+  - fix css var is inserted repeatedly &nbsp;-&nbsp; by @soybeanjs [<samp>(e58cf)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/e58cf26)
+  - fix SvgIcon inheritAttrs warning &nbsp;-&nbsp; by @soybeanjs [<samp>(f4305)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/f430584)
+  - fix event name &nbsp;-&nbsp; by @soybeanjs [<samp>(642b6)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/642b6f8)
+  - fix axios createRequest: add default state &nbsp;-&nbsp; by @soybeanjs [<samp>(466bc)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/466bc7a)
+  - fix type error &nbsp;-&nbsp; by @soybeanjs [<samp>(77c20)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/77c2023)
+  - fix disabled page animate &nbsp;-&nbsp; by @soybeanjs [<samp>(e6910)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/e691088)
+  - fix routes data when role is change &nbsp;-&nbsp; by @soybeanjs [<samp>(03c80)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/03c8075)
+  - fix tabs data when role is change &nbsp;-&nbsp; by @soybeanjs [<samp>(9fd89)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/9fd89aa)
+  - add missing `$t` import &nbsp;-&nbsp; by @soybeanjs [<samp>(8f9ee)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/8f9ee2e)
+  - add responseType judgment &nbsp;-&nbsp; by @soybeanjs [<samp>(2246f)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/2246f78)
+  - fix mix-menu blank & cache mixMenuFixed &nbsp;-&nbsp; by @soybeanjs [<samp>(9fdb6)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/9fdb6c1)
+  - fix PinToggler label &nbsp;-&nbsp; by @soybeanjs [<samp>(b1f9c)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/b1f9c4c)
+  - recovery the layout config before is mobile &nbsp;-&nbsp; by @soybeanjs [<samp>(851ae)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/851ae38)
+  - fix manage page drawer operate about data reset &nbsp;-&nbsp; by @soybeanjs [<samp>(91dfa)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/91dfa57)
+  - fix menu data when role is changed &nbsp;-&nbsp; by @soybeanjs [<samp>(7cc5f)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/7cc5f9c)
+  - fix useRouter in tab store &nbsp;-&nbsp; by @soybeanjs [<samp>(962c4)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/962c403)
+  - fix header style & fix button highlight when click global-tab &nbsp;-&nbsp; by @soybeanjs [<samp>(f2983)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/f298388)
+  - fix multi tab page only render once &nbsp;-&nbsp; by @soybeanjs [<samp>(b98b0)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/b98b075)
+  - fix login Register component name &nbsp;-&nbsp; by @soybeanjs [<samp>(a3ba5)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/a3ba516)
+- **styles**:
+  - fix FirstLevelMenu style &nbsp;-&nbsp; by @soybeanjs [<samp>(5df82)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/5df82d1)
+  - fix PinToggler style &nbsp;-&nbsp; by @soybeanjs [<samp>(593ce)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/593ceea)
+- **view-manage-role**:
+  - fix PopConfirm content to title &nbsp;-&nbsp; by @zyhnbyyds [<samp>(d95bd)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/d95bd84)
+
+### &nbsp;&nbsp;&nbsp;🔥 Performance
+
+- **projects**:
+  - remove system logo fill &nbsp;-&nbsp; by @soybeanjs [<samp>(e02d0)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/e02d025)
+  - perf code &nbsp;-&nbsp; by @soybeanjs [<samp>(c9e34)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/c9e34f6)
+  - remove as any &nbsp;-&nbsp; by @soybeanjs [<samp>(7f61b)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/7f61bdc)
+  - perf code &nbsp;-&nbsp; by @soybeanjs [<samp>(3f22e)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/3f22efa)
+  - perf code &nbsp;-&nbsp; by @soybeanjs [<samp>(cb369)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/cb3694c)
+  - perf css of base-menu &nbsp;-&nbsp; by @soybeanjs [<samp>(4046a)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/4046a8b)
+  - perf theme store &nbsp;-&nbsp; by @soybeanjs [<samp>(b0d95)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/b0d95f1)
+  - perf code &nbsp;-&nbsp; by @soybeanjs [<samp>(324cf)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/324cffd)
+  - perf code &nbsp;-&nbsp; by @soybeanjs [<samp>(c2aae)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/c2aaea9)
+  - env config &nbsp;-&nbsp; by @soybeanjs [<samp>(07423)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/0742326)
+  - add detailed annotations for route role &nbsp;-&nbsp; by @soybeanjs [<samp>(2a0ec)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/2a0ec38)
+  - remove useless file &nbsp;-&nbsp; by @soybeanjs [<samp>(da280)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/da28076)
+  - perf card style &nbsp;-&nbsp; by @soybeanjs [<samp>(9895a)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/9895a37)
+  - echarts loading style &nbsp;-&nbsp; by @soybeanjs [<samp>(dca53)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/dca53b0)
+  - perf code &nbsp;-&nbsp; by @soybeanjs [<samp>(8b960)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/8b9607f)
+  - remove useless code &nbsp;-&nbsp; by @soybeanjs [<samp>(3115e)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/3115ef4)
+  - perf code &nbsp;-&nbsp; by @soybeanjs [<samp>(a1220)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/a122088)
+  - perf judgement the fixed tab &nbsp;-&nbsp; by @soybeanjs [<samp>(90c20)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/90c205d)
+
+### &nbsp;&nbsp;&nbsp;🛠 Optimizations
+
+- **components**:
+  - ButtonIcon with smarter class merging and tippy &nbsp;-&nbsp; by @mufeng889 [<samp>(95960)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/95960b0)
+  - accuracy draggable area for TableColumnSetting with animation &nbsp;-&nbsp; by @soybeanjs [<samp>(8a2e0)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/8a2e0eb)
+- **projects**:
+  - optimize manage_menu &nbsp;-&nbsp; by @soybeanjs [<samp>(bf406)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/bf40642)
+  - remove deprecated code &nbsp;-&nbsp; by @soybeanjs [<samp>(00361)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/0036172)
+  - unocss border shortcut &nbsp;-&nbsp; by @soybeanjs [<samp>(4ebeb)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/4ebeb90)
+
+### &nbsp;&nbsp;&nbsp;💅 Refactors
+
+- **hooks**:
+  - deleting the route export of useRoutePush, use vue-router &nbsp;-&nbsp; by @soybeanjs [<samp>(b81f7)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/b81f749)
+  - refactor @sa/color-palette to @sa/color &nbsp;-&nbsp; by @soybeanjs [<samp>(fda42)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/fda42d6)
+- **projects**:
+  - perf code &nbsp;-&nbsp; by @soybeanjs [<samp>(0b2e0)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/0b2e046)
+  - init route store in router guide &nbsp;-&nbsp; by @soybeanjs [<samp>(bc056)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/bc056de)
+  - remove requiresAuth option of route meta &nbsp;-&nbsp; by @soybeanjs [<samp>(5a137)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/5a13749)
+  - fix tooltop of ButtonIcon &nbsp;-&nbsp; by @soybeanjs [<samp>(a5b16)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/a5b1666)
+  - useStore variable name &nbsp;-&nbsp; by @soybeanjs [<samp>(29291)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/2929177)
+  - add createRoutes replace export routes directly &nbsp;-&nbsp; by @soybeanjs [<samp>(16bca)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/16bca45)
+  - refactor ColorPicker &nbsp;-&nbsp; by @soybeanjs [<samp>(f80c1)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/f80c1ae)
+  - themeStore: isCustomizeInfoColor to isInfoFollowPrimary &nbsp;-&nbsp; by @soybeanjs [<samp>(0867f)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/0867f4b)
+  - rename zh-cn locale &nbsp;-&nbsp; by @soybeanjs [<samp>(ba10c)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/ba10c3f)
+  - rename zh-cn locale &nbsp;-&nbsp; by @soybeanjs [<samp>(26714)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/26714e9)
+  - split @sa/request to @sa/axios, @sa/fetch &nbsp;-&nbsp; by @soybeanjs [<samp>(9ecb5)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/9ecb516)
+  - remove @sa/eslint-config &nbsp;-&nbsp; by @soybeanjs [<samp>(23002)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/230027c)
+  - remove docs to soybean-admin-docs &nbsp;-&nbsp; by @soybeanjs [<samp>(be008)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/be0087c)
+  - refactor app-loading &nbsp;-&nbsp; by @soybeanjs [<samp>(c3c14)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/c3c143b)
+  - login components => modules &nbsp;-&nbsp; by @soybeanjs [<samp>(49237)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/4923781)
+  - keep directory "locales/langs" same with version naive-ui &nbsp;-&nbsp; by @soybeanjs [<samp>(5ecf3)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/5ecf384)
+  - refactor service env config &nbsp;-&nbsp; by @soybeanjs [<samp>(d89ee)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/d89ee4f)
+  - refactor unocss shortcuts: wh-full => size-full &nbsp;-&nbsp; by @soybeanjs [<samp>(835cb)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/835cb4f)
+  - use enquirer replace prompts &nbsp;-&nbsp; by @soybeanjs [<samp>(64b27)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/64b272c)
+  - perf code &nbsp;-&nbsp; by @soybeanjs [<samp>(81f08)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/81f08f6)
+  - constant routes, route guard &nbsp;-&nbsp; by @soybeanjs [<samp>(ddec7)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/ddec7d8)
+  - useMixMenuContext replace useMixMenu &nbsp;-&nbsp; by @soybeanjs [<samp>(e0aa7)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/e0aa752)
+  - Soybean Admin to SoybeanAdmin &nbsp;-&nbsp; by @soybeanjs [<samp>(9427d)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/9427d07)
+- **styles**:
+  - sort defineProps, defineEmits with TS type &nbsp;-&nbsp; by @soybeanjs [<samp>(37a64)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/37a64a5)
+
+### &nbsp;&nbsp;&nbsp;📖 Documentation
+
+- **projects**:
+  - add README &nbsp;-&nbsp; by @soybeanjs [<samp>(67fbf)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/67fbfc7)
+  - update README.md &nbsp;-&nbsp; by @soybeanjs [<samp>(3b31b)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/3b31b19)
+  - update README.md &nbsp;-&nbsp; by @soybeanjs [<samp>(43743)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/43743a0)
+  - update README.md &nbsp;-&nbsp; by @soybeanjs [<samp>(d71b6)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/d71b6ed)
+  - update README.md &nbsp;-&nbsp; by @soybeanjs [<samp>(427a0)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/427a0a5)
+  - update README &nbsp;-&nbsp; by @soybeanjs [<samp>(4045e)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/4045e41)
+  - add CHANGELOG &nbsp;-&nbsp; by @soybeanjs [<samp>(01fbb)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/01fbbac)
+
+### &nbsp;&nbsp;&nbsp;🏡 Chore
+
+- **deps**:
+  - update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(4caa4)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/4caa45d)
+  - update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(bd3ef)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/bd3efe6)
+  - update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(56931)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/56931a2)
+  - update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(56e7d)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/56e7dc3)
+  - update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(892e2)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/892e2d6)
+  - updtae deps &nbsp;-&nbsp; by @soybeanjs [<samp>(7088d)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/7088ddc)
+  - update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(b0b48)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/b0b4830)
+  - update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(7f049)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/7f049ba)
+  - update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(854ce)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/854ce36)
+  - update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(82ccf)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/82ccfbc)
+  - update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(b0546)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/b054661)
+  - update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(adc36)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/adc3656)
+  - update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(c655a)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/c655a24)
+  - update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(345b6)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/345b687)
+  - update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(70962)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/70962f5)
+  - update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(2d4d1)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/2d4d107)
+  - update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(9e493)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/9e493e5)
+  - update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(eba20)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/eba2014)
+  - update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(909ce)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/909ceb8)
+- **other**:
+  - correct spell mistake &nbsp;-&nbsp; by @soybeanjs [<samp>(308bf)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/308bff6)
+- **projects**:
+  - rename project &nbsp;-&nbsp; by @soybeanjs [<samp>(5dfe9)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/5dfe968)
+  - update version and perf code &nbsp;-&nbsp; by @soybeanjs [<samp>(97035)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/970352c)
+  - fix pkg version &nbsp;-&nbsp; by @soybeanjs [<samp>(4b375)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/4b37577)
+  - add sourcemap &nbsp;-&nbsp; by @soybeanjs [<samp>(d25ed)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/d25ed44)
+  - add pnpm-lock &nbsp;-&nbsp; by @soybeanjs [<samp>(0e4cb)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/0e4cbd9)
+  - update deps and perf code &nbsp;-&nbsp; by @soybeanjs [<samp>(19c12)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/19c1200)
+  - update gitignore &nbsp;-&nbsp; by @soybeanjs [<samp>(910fe)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/910fe4e)
+  - update pnpm-lock &nbsp;-&nbsp; by @soybeanjs [<samp>(1d0b0)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/1d0b061)
+  - update vscode settings &nbsp;-&nbsp; by @soybeanjs [<samp>(c2b6a)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/c2b6aa6)
+  - use eslint flat config &nbsp;-&nbsp; by @soybeanjs [<samp>(11adc)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/11adc8c)
+  - add config files &nbsp;-&nbsp; by @soybeanjs [<samp>(87aa0)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/87aa069)
+  - update deps & update @sa/scripts config &nbsp;-&nbsp; by @soybeanjs [<samp>(31f67)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/31f676a)
+  - update pnpm version &nbsp;-&nbsp; by @soybeanjs [<samp>(47819)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/478194d)
+  - create LICENSE &nbsp;-&nbsp; by @soybeanjs [<samp>(853f5)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/853f5c1)
+  - update pnpm version &nbsp;-&nbsp; by @soybeanjs [<samp>(91121)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/9112193)
+  - update eslint config &nbsp;-&nbsp; by @soybeanjs [<samp>(7029b)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/7029b49)
+  - lock deps version &nbsp;-&nbsp; by @soybeanjs [<samp>(8ff39)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/8ff398f)
+  - remove @soybeanjs/cli &nbsp;-&nbsp; by @soybeanjs [<samp>(02c05)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/02c0539)
+  - update pnpm version &nbsp;-&nbsp; by @soybeanjs [<samp>(c6b00)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/c6b00bb)
+  - update mock api &nbsp;-&nbsp; by @soybeanjs [<samp>(c0079)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/c007935)
+  - update pnpm version &nbsp;-&nbsp; by @soybeanjs [<samp>(88f26)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/88f2641)
+  - update repository url &nbsp;-&nbsp; by @soybeanjs [<samp>(f46ce)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/f46ce2a)
+  - update vscode config &nbsp;-&nbsp; by @soybeanjs [<samp>(bfaec)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/bfaecce)
+  - update component.d.ts &nbsp;-&nbsp; by @soybeanjs [<samp>(1bebf)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/1bebf8b)
+  - add unocss eslint config &nbsp;-&nbsp; by @soybeanjs [<samp>(6f004)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/6f004ce)
+  - update .gitattributes &nbsp;-&nbsp; by @soybeanjs [<samp>(e5235)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/e5235e0)
+  - update pnpm version &nbsp;-&nbsp; by @soybeanjs [<samp>(30c97)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/30c9729)
+  - disabled unocss eslint rule: order-attributify &nbsp;-&nbsp; by @soybeanjs [<samp>(87838)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/8783804)
+  - update pnpm-lock.yaml &nbsp;-&nbsp; by @soybeanjs [<samp>(8f5a6)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/8f5a6b1)
+  - update deps & update pnpm version &nbsp;-&nbsp; by @soybeanjs [<samp>(5260d)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/5260d67)
+  - update mock url &nbsp;-&nbsp; by @Azir-11 in https://github.com/soybeanjs/soybean-admin-antd/issues/22 [<samp>(32c00)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/32c006a)
+  - update deps & add lock file &nbsp;-&nbsp; by @soybeanjs [<samp>(e1777)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/e1777b4)
+  - synchronize recent changes &nbsp;-&nbsp; by @mufeng889 in https://github.com/soybeanjs/soybean-admin-antd/issues/23 [<samp>(d5348)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/d53487b)
+  - update eslint config &nbsp;-&nbsp; by @soybeanjs [<samp>(9c450)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/9c45009)
+  - update .npmrc &nbsp;-&nbsp; by @soybeanjs [<samp>(5d15b)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/5d15bda)
+  - update vscode settings &nbsp;-&nbsp; by @soybeanjs [<samp>(e77bd)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/e77bda1)
+
+### &nbsp;&nbsp;&nbsp;🎨 Styles
+
+- **projects**:
+  - fix menu style &nbsp;-&nbsp; by @soybeanjs [<samp>(d8b9b)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/d8b9b94)
+  - perf layout card style &nbsp;-&nbsp; by @soybeanjs [<samp>(fc409)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/fc409db)
+  - fix event style &nbsp;-&nbsp; by @soybeanjs [<samp>(e935f)</samp>](https://github.com/soybeanjs/soybean-admin-antd/commit/e935f77)
+
+### &nbsp;&nbsp;&nbsp;❤️ Contributors
+
+[![soybeanjs](https://github.com/soybeanjs.png?size=48)](https://github.com/soybeanjs)&nbsp;&nbsp;[![mufeng889](https://github.com/mufeng889.png?size=48)](https://github.com/mufeng889)&nbsp;&nbsp;[![Azir-11](https://github.com/Azir-11.png?size=48)](https://github.com/Azir-11)&nbsp;&nbsp;[![zyhnbyyds](https://github.com/zyhnbyyds.png?size=48)](https://github.com/zyhnbyyds)&nbsp;&nbsp;
+

+ 21 - 0
LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 Soybean
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 177 - 1
README.md

@@ -1,2 +1,178 @@
-# observe-admin
+<div align="center">
+	<img src="./public/favicon.svg" width="160" />
+	<h1>SoybeanAdmin</h1>
+  <span>English | <a href="./README.zh_CN.md">中文</a></span>
+</div>
 
+---
+
+[![license](https://img.shields.io/badge/license-MIT-green.svg)](./LICENSE)
+[![github stars](https://img.shields.io/github/stars/soybeanjs/soybean-admin)](https://github.com/soybeanjs/soybean-admin)
+[![github forks](https://img.shields.io/github/forks/soybeanjs/soybean-admin)](https://github.com/soybeanjs/soybean-admin)
+[![gitee stars](https://gitee.com/honghuangdc/soybean-admin/badge/star.svg)](https://gitee.com/honghuangdc/soybean-admin)
+
+<a href="https://hellogithub.com/repository/1298f27d5fe54959a16cf9686516ddb3" target="_blank"><img src="https://abroad.hellogithub.com/v1/widgets/recommend.svg?rid=1298f27d5fe54959a16cf9686516ddb3&claim_uid=IiDXWmP4TEntjbV" alt="Featured|HelloGitHub" style="width: 250px; height: 54px;" width="250" height="54" /></a>
+
+> [!NOTE]
+> If you think `SoybeanAdmin` is helpful to you, or you like our project, please give us a ⭐️ on GitHub. Your support is the driving force for us to continue to improve and add new features! Thank you for your support!
+
+## Introduction
+
+[`SoybeanAdmin`](https://github.com/soybeanjs/soybean-admin) is a clean, elegant, beautiful and powerful admin template, based on the latest front-end technology stack, including Vue3, Vite5, TypeScript, Pinia and UnoCSS. It has built-in rich theme configuration and components, strict code specifications, and an automated file routing system. In addition, it also uses the online mock data solution based on ApiFox. `SoybeanAdmin` provides you with a one-stop admin solution, no additional configuration, and out of the box. It is also a best practice for learning cutting-edge technologies quickly.
+
+
+## Features
+
+- **Cutting-edge technology application**: using the latest popular technology stack such as Vue3, Vite5, TypeScript, Pinia and UnoCSS.
+- **Clear project architecture**: using pnpm monorepo architecture, clear structure, elegant and easy to understand.
+- **Strict code specifications**: follow the [SoybeanJS specification](https://docs.soybeanjs.cn/standard), integrate eslint, prettier and simple-git-hooks to ensure the code is standardized.
+- **TypeScript**: support strict type checking to improve code maintainability.
+- **Rich theme configuration**: built-in a variety of theme configurations, perfectly integrated with UnoCSS.
+- **Built-in internationalization solution**: easily realize multi-language support.
+- **Automated file routing system**: automatically generate route import, declaration and type. For more details, please refer to [Elegant Router](https://github.com/soybeanjs/elegant-router).
+- **Flexible permission routing**: support both front-end static routing and back-end dynamic routing.
+- **Rich page components**: built-in a variety of pages and components, including 403, 404, 500 pages, as well as layout components, tag components, theme configuration components, etc.
+- **Command line tool**: built-in efficient command line tool, git commit, delete file, release, etc.
+- **Mobile adaptation**: perfectly support mobile terminal to realize adaptive layout.
+
+
+## Version
+
+- **NaiveUI Version:**
+  - [Preview Link](https://naive.soybeanjs.cn/)
+  - [Github Repository](https://github.com/soybeanjs/soybean-admin)
+  - [Gitee Repository](https://gitee.com/honghuangdc/soybean-admin)
+
+- **AntDesignVue Version:**
+  - [Preview Link](https://antd.soybeanjs.cn/)
+  - [Github Repository](https://github.com/soybeanjs/soybean-admin-antd)
+  - [Gitee Repository](https://gitee.com/honghuangdc/soybean-admin-antd)
+
+- **Legacy Version:**
+  - [Preview Link](https://legacy.soybeanjs.cn/)
+  - [Github Repository](https://github.com/soybeanjs/soybean-admin/tree/legacy)
+
+
+## Documentation
+
+- [Link](https://docs.soybeanjs.cn)
+- [Legacy Docs](https://legacy-docs.soybeanjs.cn)
+
+## Example Images
+
+![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-01.png)
+![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-02.png)
+![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-03.png)
+![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-04.png)
+![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-05.png)
+![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-06.png)
+![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-07.png)
+![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-08.png)
+![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-09.png)
+![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-10.png)
+![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-mobile.png)
+
+
+## Usage
+
+**Environment Preparation**
+
+Make sure your environment meets the following requirements:
+
+- **git**: you need git to clone and manage project versions.
+- **NodeJS**: >=18.12.0, recommended 18.19.0 or higher.
+- **pnpm**: >= 8.7.0, recommended 8.14.0 or higher.
+
+**Clone Project**
+
+```bash
+git clone https://github.com/soybeanjs/soybean-admin.git
+```
+
+**Install Dependencies**
+
+```bash
+pnpm i
+```
+> Since this project uses the pnpm monorepo management method, please do not use npm or yarn to install dependencies.
+
+**Start Project**
+
+```bash
+pnpm dev
+```
+
+**Build Project**
+
+```bash
+pnpm build
+```
+
+## Ecosystem
+
+- [electron-mock-admin](https://github.com/lixin59/electron-mock-api): A Mock Api management system that helps front-end developers quickly implement interface mocks.
+- [T-Shell](https://github.com/TheBlindM/T-Shell): A terminal emulator and SSH client with configurable command prompts.
+- [pea](https://github.com/haitang1894/pea) : Adopting SpringBoot3.2 + JDK21, MyBatis-Plus, SpringSecurity security framework, etc., suitable for the simple permission system developed by [soybean-admin](https://gitee.com/honghuangdc/soybean-admin).
+- [MalusAdmin](https://github.com/pridejoy/MalusAdmin): A backend management framework developed based on Vue3/TypeScript/NaiveUI and NET7 & Sqlsugar. It is implemented in the most original and simplest way, with a fresh and elegant front-end, a clear and elegant backend structure, and powerful functions.
+- [PanisAdmin](https://github.com/paynezhuang/panis-admin): Adopting SpringBoot 3, SaToken, MySQL and other frameworks to develop and modify [soybean-admin](https://github.com/soybeanjs/soybean-admin) for the second time, adapting dynamic menu/button-level authorization. Retaining the original flavor, fresh and elegant, high-value back-end management system scaffold.
+- [snail-job](https://github.com/aizuda/snail-job): A distributed task retry and task scheduling platform with "high performance, high value and high activity".
+- [SuperApi](https://github.com/TmmTop/SuperApi): Quickly turn your idea into an online stable product! Entity-less library and table building, add, delete, change and check entity-less library table, support 15 kinds of condition query, as well as paging, list, unlimited tree list and other functions of the API deployment! With interface documentation, Auth authorisation, interface flow restriction, access to the client's real IP, advanced server caching components, dynamic APIs and other features, we look forward to your experience!
+- [FastSoyAdmin](https://github.com/sleep1223/fast-soy-admin): A modern Management Platform based on FastAPI+Vue3+Naive UI.
+
+
+## How to Contribute
+
+We warmly welcome and appreciate all forms of contributions. If you have any ideas or suggestions, please feel free to share them by submitting [pull requests](https://github.com/soybeanjs/soybean-admin/pulls) or creating GitHub [issue](https://github.com/soybeanjs/soybean-admin/issues/new).
+
+## Git Commit Guidelines
+
+This project has built-in `commit` command, you can execute `pnpm commit` to generate commit information that conforms to [Conventional Commits](https://www.conventionalcommits.org/) specification. When submitting PR, please be sure to use `commit` command to create commit information to ensure the standardization of information.
+
+## Browser Support
+
+It is recommended to use the latest version of Chrome in development for a better experience.
+
+| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/archive/internet-explorer_9-11/internet-explorer_9-11_48x48.png" alt="IE" width="24px" height="24px"  />](http://godban.github.io/browsers-support-badges/) | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt=" Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) |
+| --- | --- | --- | --- | --- |
+| not support | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
+
+## OpenSource Author
+
+[Soybean](https://github.com/honghuangdc)
+
+## Contributors
+
+Thanks the following people for their contributions. If you want to contribute to this project, please refer to [How to Contribute](#how-to-contribute).
+
+<a href="https://github.com/soybeanjs/soybean-admin/graphs/contributors">
+  <img src="https://contrib.rocks/image?repo=soybeanjs/soybean-admin" />
+</a>
+
+## Communication
+
+`SoybeanAdmin` is a completely open source and free project, helping developers to develop medium and large-scale management systems more conveniently. It also provides WeChat and QQ communication groups. If you have any questions, please feel free to ask in the group.
+
+  <div>
+  	<p>QQ Group</p>
+    <img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/qq-soybean-admin-2.jpg" style="width:200px" />
+  </div>
+	<!-- <div>
+		<p>WeChat Group</p>
+		<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/picgo/soybean-admin-wechat-0620.jpg" style="width:200px" />
+	</div> -->
+	<div>
+		<p>Add the following WeChat to invite to the WeChat group</p>
+		<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/wechat-soybeanjs.jpg" style="width:200px" />
+	</div>
+  <div>
+    <p>Add Soybean's WeChat for business consultation, cooperation, project architecture, one-on-one guidance, etc.</p>
+    <img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/wechat-soybean.jpg" style="width:200px" />
+  </div>
+
+## Star Trend
+
+[![Star History Chart](https://api.star-history.com/svg?repos=soybeanjs/soybean-admin&type=Date)](https://star-history.com/#soybeanjs/soybean-admin&Date)
+
+## License
+
+This project is based on the [MIT © 2021 Soybean](./LICENSE) protocol, for learning purposes only, please retain the author's copyright information for commercial use, the author does not guarantee and is not responsible for the software.

+ 179 - 0
README.zh_CN.md

@@ -0,0 +1,179 @@
+<div align="center">
+	<img src="./public/favicon.svg" width="160" />
+	<h1>SoybeanAdmin</h1>
+  <span><a href="./README.md">English</a> | 中文</span>
+</div>
+
+---
+
+[![license](https://img.shields.io/badge/license-MIT-green.svg)](./LICENSE)
+[![github stars](https://img.shields.io/github/stars/honghuangdc/soybean-admin)](https://github.com/soybeanjs/soybean-admin)
+[![github forks](https://img.shields.io/github/forks/honghuangdc/soybean-admin)](https://github.com/soybeanjs/soybean-admin)
+[![gitee stars](https://gitee.com/honghuangdc/soybean-admin/badge/star.svg)](https://gitee.com/honghuangdc/soybean-admin)
+
+<a href="https://hellogithub.com/repository/1298f27d5fe54959a16cf9686516ddb3" target="_blank"><img src="https://abroad.hellogithub.com/v1/widgets/recommend.svg?rid=1298f27d5fe54959a16cf9686516ddb3&claim_uid=IiDXWmP4TEntjbV" alt="Featured|HelloGitHub" style="width: 250px; height: 54px;" width="250" height="54" /></a>
+
+> [!NOTE]
+> 如果您觉得 `SoybeanAdmin`对您有所帮助,或者您喜欢我们的项目,请在 GitHub 上给我们一个 ⭐️。您的支持是我们持续改进和增加新功能的动力!感谢您的支持!
+
+## 简介
+
+[`SoybeanAdmin`](https://github.com/soybeanjs/soybean-admin) 是一个清新优雅、高颜值且功能强大的后台管理模板,基于最新的前端技术栈,包括 Vue3, Vite5, TypeScript, Pinia 和 UnoCSS。它内置了丰富的主题配置和组件,代码规范严谨,实现了自动化的文件路由系统。此外,它还采用了基于 ApiFox 的在线Mock数据方案。`SoybeanAdmin` 为您提供了一站式的后台管理解决方案,无需额外配置,开箱即用。同样是一个快速学习前沿技术的最佳实践。
+
+## 特性
+
+- **前沿技术应用**:采用 Vue3, Vite5, TypeScript, Pinia 和 UnoCSS 等最新流行的技术栈。
+- **清晰的项目架构**:采用 pnpm monorepo 架构,结构清晰,优雅易懂。
+- **严格的代码规范**:遵循 [SoybeanJS 规范](https://docs.soybeanjs.cn/zh/standard),集成了eslint, prettier 和 simple-git-hooks,保证代码的规范性。
+- **TypeScript**: 支持严格的类型检查,提高代码的可维护性。
+- **丰富的主题配置**:内置多样的主题配置,与 UnoCSS 完美结合。
+- **内置国际化方案**:轻松实现多语言支持。
+- **自动化文件路由系统**:自动生成路由导入、声明和类型。更多细节请查看 [Elegant Router](https://github.com/soybeanjs/elegant-router)。
+- **灵活的权限路由**:同时支持前端静态路由和后端动态路由。
+- **丰富的页面组件**:内置多样页面和组件,包括403、404、500页面,以及布局组件、标签组件、主题配置组件等。
+- **命令行工具**:内置高效的命令行工具,git提交、删除文件、发布等。
+- **移动端适配**:完美支持移动端,实现自适应布局。
+
+
+## 版本
+
+- **NaiveUI 版本:**
+  - [预览地址](https://naive.soybeanjs.cn/)
+  - [Github 仓库](https://github.com/soybeanjs/soybean-admin)
+  - [Gitee 仓库](https://gitee.com/honghuangdc/soybean-admin)
+
+- **AntDesignVue 版本:**
+  - [预览地址](https://antd.soybeanjs.cn/)
+  - [Github 仓库](https://github.com/soybeanjs/soybean-admin-antd)
+  - [Gitee 仓库](https://gitee.com/honghuangdc/soybean-admin-antd)
+
+- **旧版:**
+  - [预览地址](https://legacy.soybeanjs.cn/)
+  - [Github 仓库](https://github.com/soybeanjs/soybean-admin/tree/legacy)
+
+
+## 文档
+
+- [地址](https://docs.soybeanjs.cn)
+- [旧版文档](https://legacy-docs.soybeanjs.cn)
+
+## 示例图片
+
+![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-01.png)
+![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-02.png)
+![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-03.png)
+![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-04.png)
+![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-05.png)
+![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-06.png)
+![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-07.png)
+![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-08.png)
+![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-09.png)
+![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-10.png)
+![](https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin-v1-mobile.png)
+
+
+## 使用
+
+**环境准备**
+
+确保你的环境满足以下要求:
+
+- **git**: 你需要git来克隆和管理项目版本。
+- **NodeJS**: >=18.12.0,推荐 18.19.0 或更高。
+- **pnpm**: >= 8.7.0,推荐 8.14.0 或更高。
+
+**克隆项目**
+
+```bash
+git clone https://github.com/soybeanjs/soybean-admin.git
+```
+
+**安装依赖**
+
+```bash
+pnpm i
+```
+> 由于本项目采用了 pnpm monorepo 的管理方式,因此请不要使用 npm 或 yarn 来安装依赖。
+
+**启动项目**
+
+```bash
+pnpm dev
+```
+
+**构建项目**
+
+```bash
+pnpm build
+```
+
+## 周边生态
+
+- [electron-mock-admin](https://github.com/lixin59/electron-mock-api): 一个 Mock Api 管理系统,帮助前端开发伙伴快速实现接口的 mock。
+- [T-Shell](https://github.com/TheBlindM/T-Shell): 是一个可配置命令提示的终端模拟器和 SSH 客户端。
+- [pea](https://github.com/haitang1894/pea) : 采用SpringBoot3.2 + JDK21、MyBatis-Plus、SpringSecurity安全框架等,适配 [soybean-admin](https://gitee.com/honghuangdc/soybean-admin) 开发的简单权限系统。
+- [MalusAdmin](https://github.com/pridejoy/MalusAdmin): 基于 Vue3/TypeScript/NaiveUI 和 NET7 & Sqlsugar 开发的后台管理框架。采用最原生最简洁的方式来实现, 前端清新优雅高颜值,后端 结构清晰,优雅易懂,功能强大。
+- [PanisAdmin](https://github.com/paynezhuang/panis-admin): 采用SpringBoot3、SaToken、MySQL等框架开发,二次修改 [soybean-admin](https://github.com/soybeanjs/soybean-admin),适配动态菜单/按钮级别的鉴权,保留原汁原味、清新优雅、高颜值的后台管理系统脚手架。
+- [snail-job](https://github.com/aizuda/snail-job): 一款兼具 “高性能、高颜值、高活跃” 的分布式任务重试和分布式任务调度平台。
+- [SuperApi](https://github.com/TmmTop/SuperApi): 快速将你的 idea 变成线上稳定运行的产品! 无实体建库建表,对无实体库表进行增删改查,支持 15 种条件查询,以及分页,列表,无限级树形列表 等功能的 API 部署! 拥有接口文档,Auth 授权,接口限流,获取客户端真实 IP,先进的服务器缓存组件,动态 API 等功能,期待您的体验!
+- [FastSoyAdmin](https://github.com/sleep1223/fast-soy-admin): 基于 FastAPI+Vue3+Naive UI 的现代化轻量管理平台.
+
+
+## 如何贡献
+
+我们热烈欢迎并感谢所有形式的贡献。如果您有任何想法或建议,欢迎通过提交 [pull requests](https://github.com/soybeanjs/soybean-admin/pulls) 或创建 GitHub [issue](https://github.com/soybeanjs/soybean-admin/issues/new) 来分享。
+
+## Git 提交规范
+
+本项目已内置 `commit` 命令,您可以通过执行 `pnpm commit` 来生成符合 [Conventional Commits]([conventionalcommits](https://www.conventionalcommits.org/)) 规范的提交信息。在提交PR时,请务必使用 `commit` 命令来创建提交信息,以确保信息的规范性。
+
+
+## 浏览器支持
+
+推荐使用最新版的 Chrome 浏览器进行开发,以获得更好的体验。
+
+| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/archive/internet-explorer_9-11/internet-explorer_9-11_48x48.png" alt="IE" width="24px" height="24px"  />](http://godban.github.io/browsers-support-badges/) | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt=" Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) |
+| --- | --- | --- | --- | --- |
+| not support | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
+
+## 开源作者
+
+[Soybean](https://github.com/honghuangdc)
+
+
+## 贡献者
+
+感谢以下贡献者的贡献。如果您想为本项目做出贡献,请参考 [如何贡献](#如何贡献)。
+
+<a href="https://github.com/soybeanjs/soybean-admin/graphs/contributors">
+  <img src="https://contrib.rocks/image?repo=soybeanjs/soybean-admin" />
+</a>
+
+## 交流
+
+`SoybeanAdmin` 是完全开源免费的项目,在帮助开发者更方便地进行中大型管理系统开发,同时也提供微信和 QQ 交流群,使用问题欢迎在群内提问。
+
+  <div>
+  	<p>QQ交流群</p>
+    <img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/qq-soybean-admin-2.jpg" style="width:200px" />
+  </div>
+	<!-- <div>
+		<p>微信群</p>
+		<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/picgo/soybean-admin-wechat-0620.jpg" style="width:200px" />
+	</div> -->
+	<div>
+		<p>添加下面微信邀请进微信群</p>
+		<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/wechat-soybeanjs.jpg" style="width:200px" />
+	</div>
+  <div>
+    <p>添加 Soybean 的微信,业务咨询、合作、项目架构、一对一指导等</p>
+    <img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/wechat-soybean.jpg" style="width:200px" />
+  </div>
+
+## Star 趋势
+
+[![Star History Chart](https://api.star-history.com/svg?repos=soybeanjs/soybean-admin&type=Date)](https://star-history.com/#soybeanjs/soybean-admin&Date)
+
+## 开源协议
+
+项目基于 [MIT © 2021 Soybean](./LICENSE) 协议,仅供学习参考,商业使用请保留作者版权信息,作者不保证也不承担任何软件的使用风险。

+ 2 - 0
build/config/index.ts

@@ -0,0 +1,2 @@
+export * from './proxy';
+export * from './time';

+ 36 - 0
build/config/proxy.ts

@@ -0,0 +1,36 @@
+import type { ProxyOptions } from 'vite';
+import { createServiceConfig } from '../../src/utils/service';
+
+/**
+ * Set http proxy
+ *
+ * @param env - The current env
+ * @param isDev - Is development environment
+ */
+export function createViteProxy(env: Env.ImportMeta, isDev: boolean) {
+  const isEnableHttpProxy = isDev && env.VITE_HTTP_PROXY === 'Y';
+
+  if (!isEnableHttpProxy) return undefined;
+
+  const { baseURL, proxyPattern, other } = createServiceConfig(env);
+
+  const proxy: Record<string, ProxyOptions> = createProxyItem({ baseURL, proxyPattern });
+
+  other.forEach(item => {
+    Object.assign(proxy, createProxyItem(item));
+  });
+
+  return proxy;
+}
+
+function createProxyItem(item: App.Service.ServiceConfigItem) {
+  const proxy: Record<string, ProxyOptions> = {};
+
+  proxy[item.proxyPattern] = {
+    target: item.baseURL,
+    changeOrigin: true,
+    rewrite: path => path.replace(new RegExp(`^${item.proxyPattern}`), '')
+  };
+
+  return proxy;
+}

+ 12 - 0
build/config/time.ts

@@ -0,0 +1,12 @@
+import dayjs from 'dayjs';
+import utc from 'dayjs/plugin/utc';
+import timezone from 'dayjs/plugin/timezone';
+
+export function getBuildTime() {
+  dayjs.extend(utc);
+  dayjs.extend(timezone);
+
+  const buildTime = dayjs.tz(Date.now(), 'Asia/Shanghai').format('YYYY-MM-DD HH:mm:ss');
+
+  return buildTime;
+}

+ 13 - 0
build/plugins/html.ts

@@ -0,0 +1,13 @@
+import type { Plugin } from 'vite';
+
+export function setupHtmlPlugin(buildTime: string) {
+  const plugin: Plugin = {
+    name: 'html-plugin',
+    apply: 'build',
+    transformIndexHtml(html) {
+      return html.replace('<head>', `<head>\n    <meta name="buildTime" content="${buildTime}">`);
+    }
+  };
+
+  return plugin;
+}

+ 28 - 0
build/plugins/index.ts

@@ -0,0 +1,28 @@
+import type { PluginOption } from 'vite';
+import vue from '@vitejs/plugin-vue';
+import vueJsx from '@vitejs/plugin-vue-jsx';
+import VueDevtools from 'vite-plugin-vue-devtools';
+import progress from 'vite-plugin-progress';
+import { setupElegantRouter } from './router';
+import { setupUnocss } from './unocss';
+import { setupUnplugin } from './unplugin';
+import { setupHtmlPlugin } from './html';
+
+export function setupVitePlugins(viteEnv: Env.ImportMeta, buildTime: string) {
+  const plugins: PluginOption = [
+    vue({
+      script: {
+        defineModel: true
+      }
+    }),
+    vueJsx(),
+    VueDevtools(),
+    setupElegantRouter(),
+    setupUnocss(viteEnv),
+    ...setupUnplugin(viteEnv),
+    progress(),
+    setupHtmlPlugin(buildTime)
+  ];
+
+  return plugins;
+}

+ 55 - 0
build/plugins/router.ts

@@ -0,0 +1,55 @@
+import type { RouteMeta } from 'vue-router';
+import ElegantVueRouter from '@elegant-router/vue/vite';
+import type { RouteKey } from '@elegant-router/types';
+
+export function setupElegantRouter() {
+  return ElegantVueRouter({
+    layouts: {
+      base: 'src/layouts/base-layout/index.vue',
+      blank: 'src/layouts/blank-layout/index.vue'
+    },
+    customRoutes: {
+      names: [
+        'exception_403',
+        'exception_404',
+        'exception_500',
+        'document_project',
+        'document_project-link',
+        'document_vue',
+        'document_vite',
+        'document_unocss',
+        'document_naive',
+        'document_antd'
+      ]
+    },
+    routePathTransformer(routeName, routePath) {
+      const key = routeName as RouteKey;
+
+      if (key === 'login') {
+        const modules: UnionKey.LoginModule[] = ['pwd-login', 'code-login', 'register', 'reset-pwd', 'bind-wechat'];
+
+        const moduleReg = modules.join('|');
+
+        return `/login/:module(${moduleReg})?`;
+      }
+
+      return routePath;
+    },
+    onRouteMetaGen(routeName) {
+      const key = routeName as RouteKey;
+
+      const constantRoutes: RouteKey[] = ['login', '403', '404', '500'];
+
+      const meta: Partial<RouteMeta> = {
+        title: key,
+        i18nKey: `route.${key}` as App.I18n.I18nKey
+      };
+
+      if (constantRoutes.includes(key)) {
+        meta.constant = true;
+      }
+
+      return meta;
+    }
+  });
+}

+ 32 - 0
build/plugins/unocss.ts

@@ -0,0 +1,32 @@
+import process from 'node:process';
+import path from 'node:path';
+import unocss from '@unocss/vite';
+import presetIcons from '@unocss/preset-icons';
+import { FileSystemIconLoader } from '@iconify/utils/lib/loader/node-loaders';
+
+export function setupUnocss(viteEnv: Env.ImportMeta) {
+  const { VITE_ICON_PREFIX, VITE_ICON_LOCAL_PREFIX } = viteEnv;
+
+  const localIconPath = path.join(process.cwd(), 'src/assets/svg-icon');
+
+  /** The name of the local icon collection */
+  const collectionName = VITE_ICON_LOCAL_PREFIX.replace(`${VITE_ICON_PREFIX}-`, '');
+
+  return unocss({
+    presets: [
+      presetIcons({
+        prefix: `${VITE_ICON_PREFIX}-`,
+        scale: 1,
+        extraProperties: {
+          display: 'inline-block'
+        },
+        collections: {
+          [collectionName]: FileSystemIconLoader(localIconPath, svg =>
+            svg.replace(/^<svg\s/, '<svg width="1em" height="1em" ')
+          )
+        },
+        warn: true
+      })
+    ]
+  });
+}

+ 49 - 0
build/plugins/unplugin.ts

@@ -0,0 +1,49 @@
+import process from 'node:process';
+import path from 'node:path';
+import type { PluginOption } from 'vite';
+import Icons from 'unplugin-icons/vite';
+import IconsResolver from 'unplugin-icons/resolver';
+import Components from 'unplugin-vue-components/vite';
+import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers';
+import { FileSystemIconLoader } from 'unplugin-icons/loaders';
+import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
+
+export function setupUnplugin(viteEnv: Env.ImportMeta) {
+  const { VITE_ICON_PREFIX, VITE_ICON_LOCAL_PREFIX } = viteEnv;
+
+  const localIconPath = path.join(process.cwd(), 'src/assets/svg-icon');
+
+  /** The name of the local icon collection */
+  const collectionName = VITE_ICON_LOCAL_PREFIX.replace(`${VITE_ICON_PREFIX}-`, '');
+
+  const plugins: PluginOption[] = [
+    Icons({
+      compiler: 'vue3',
+      customCollections: {
+        [collectionName]: FileSystemIconLoader(localIconPath, svg =>
+          svg.replace(/^<svg\s/, '<svg width="1em" height="1em" ')
+        )
+      },
+      scale: 1,
+      defaultClass: 'inline-block'
+    }),
+    Components({
+      dts: 'src/typings/components.d.ts',
+      types: [{ from: 'vue-router', names: ['RouterLink', 'RouterView'] }],
+      resolvers: [
+        AntDesignVueResolver({
+          importStyle: false
+        }),
+        IconsResolver({ customCollections: [collectionName], componentPrefix: VITE_ICON_PREFIX })
+      ]
+    }),
+    createSvgIconsPlugin({
+      iconDirs: [localIconPath],
+      symbolId: `${VITE_ICON_LOCAL_PREFIX}-[dir]-[name]`,
+      inject: 'body-last',
+      customDomId: '__SVG_ICON_LOCAL__'
+    })
+  ];
+
+  return plugins;
+}

+ 24 - 0
eslint.config.js

@@ -0,0 +1,24 @@
+import { defineConfig } from '@soybeanjs/eslint-config';
+
+export default defineConfig(
+  { vue: true, unocss: true },
+  {
+    rules: {
+      'vue/multi-word-component-names': [
+        'warn',
+        {
+          ignores: ['index', 'App', 'Register', '[id]', '[url]']
+        }
+      ],
+      'vue/component-name-in-template-casing': [
+        'warn',
+        'PascalCase',
+        {
+          registeredComponentsOnly: false,
+          ignores: ['/^icon-/']
+        }
+      ],
+      'unocss/order-attributify': 'off'
+    }
+  }
+);

+ 14 - 0
index.html

@@ -0,0 +1,14 @@
+<!doctype html>
+<html lang="zh-cmn-Hans">
+  <head>
+    <meta charset="UTF-8" />
+    <link rel="icon" href="/favicon.svg" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <meta name="color-scheme" content="light dark" />
+    <title>%VITE_APP_TITLE%</title>
+  </head>
+  <body>
+    <div id="app"></div>
+    <script type="module" src="/src/main.ts"></script>
+  </body>
+</html>

+ 112 - 0
package.json

@@ -0,0 +1,112 @@
+{
+  "name": "soybean-admin-antd",
+  "type": "module",
+  "version": "1.2.6",
+  "description": "A fresh and elegant admin template, based on Vue3,Vite3,TypeScript,AntDesign and UnoCSS.",
+  "author": {
+    "name": "Soybean",
+    "email": "soybeanjs@outlook.com",
+    "url": "https://github.com/soybeanjs"
+  },
+  "license": "MIT",
+  "homepage": "https://github.com/soybeanjs/soybean-admin-antd",
+  "repository": {
+    "url": "https://github.com/soybeanjs/soybean-admin-antd.git"
+  },
+  "bugs": {
+    "url": "https://github.com/soybeanjs/soybean-admin-antd/issues"
+  },
+  "keywords": [
+    "Vue3 admin ",
+    "vue-admin-template",
+    "Vite5",
+    "TypeScript",
+    "naive-ui",
+    "naive-ui-admin",
+    "ant-design-vue v4",
+    "UnoCSS"
+  ],
+  "engines": {
+    "node": ">=18.12.0",
+    "pnpm": ">=8.7.0"
+  },
+  "scripts": {
+    "build": "vite build --mode prod",
+    "build:test": "vite build --mode test",
+    "cleanup": "sa cleanup",
+    "commit": "sa git-commit",
+    "dev": "vite --mode test",
+    "dev:prod": "vite --mode prod",
+    "gen-route": "sa gen-route",
+    "lint": "eslint . --fix",
+    "prepare": "simple-git-hooks",
+    "preview": "vite preview",
+    "release": "sa release",
+    "typecheck": "vue-tsc --noEmit --skipLibCheck",
+    "update-pkg": "sa update-pkg"
+  },
+  "dependencies": {
+    "@better-scroll/core": "2.5.1",
+    "@iconify/vue": "4.1.2",
+    "@sa/axios": "workspace:*",
+    "@sa/color": "workspace:*",
+    "@sa/fetch": "workspace:*",
+    "@sa/hooks": "workspace:*",
+    "@sa/materials": "workspace:*",
+    "@sa/utils": "workspace:*",
+    "@vueuse/core": "10.11.0",
+    "ant-design-vue": "4.2.3",
+    "clipboard": "2.0.11",
+    "dayjs": "1.11.11",
+    "echarts": "5.5.0",
+    "lodash-es": "4.17.21",
+    "nprogress": "0.2.0",
+    "pinia": "2.1.7",
+    "tailwind-merge": "2.3.0",
+    "vue": "3.4.29",
+    "vue-draggable-plus": "0.5.0",
+    "vue-i18n": "9.13.1",
+    "vue-router": "4.3.3"
+  },
+  "devDependencies": {
+    "@elegant-router/vue": "0.3.7",
+    "@iconify/json": "2.2.220",
+    "@sa/scripts": "workspace:*",
+    "@sa/uno-preset": "workspace:*",
+    "@soybeanjs/eslint-config": "1.3.7",
+    "@types/lodash-es": "4.17.12",
+    "@types/node": "20.14.6",
+    "@types/nprogress": "0.2.3",
+    "@unocss/eslint-config": "0.61.0",
+    "@unocss/preset-icons": "0.61.0",
+    "@unocss/preset-uno": "0.61.0",
+    "@unocss/transformer-directives": "0.61.0",
+    "@unocss/transformer-variant-group": "0.61.0",
+    "@unocss/vite": "0.61.0",
+    "@vitejs/plugin-vue": "5.0.5",
+    "@vitejs/plugin-vue-jsx": "4.0.0",
+    "eslint": "9.5.0",
+    "eslint-plugin-vue": "9.26.0",
+    "lint-staged": "15.2.7",
+    "sass": "1.77.6",
+    "simple-git-hooks": "2.11.1",
+    "tsx": "4.15.6",
+    "typescript": "5.4.5",
+    "unplugin-icons": "0.19.0",
+    "unplugin-vue-components": "0.27.0",
+    "vite": "5.3.1",
+    "vite-plugin-progress": "0.0.7",
+    "vite-plugin-svg-icons": "2.0.1",
+    "vite-plugin-vue-devtools": "7.3.2",
+    "vue-eslint-parser": "9.4.3",
+    "vue-tsc": "2.0.21"
+  },
+  "simple-git-hooks": {
+    "commit-msg": "pnpm sa git-commit-verify",
+    "pre-commit": "pnpm typecheck && pnpm lint-staged"
+  },
+  "lint-staged": {
+    "*": "eslint --fix"
+  },
+  "website": "https://antd.soybeanjs.cn"
+}

+ 21 - 0
packages/axios/package.json

@@ -0,0 +1,21 @@
+{
+  "name": "@sa/axios",
+  "version": "1.2.6",
+  "exports": {
+    ".": "./src/index.ts"
+  },
+  "typesVersions": {
+    "*": {
+      "*": ["./src/*"]
+    }
+  },
+  "dependencies": {
+    "@sa/utils": "workspace:*",
+    "axios": "1.7.2",
+    "axios-retry": "4.4.0",
+    "qs": "6.12.1"
+  },
+  "devDependencies": {
+    "@types/qs": "6.9.15"
+  }
+}

+ 5 - 0
packages/axios/src/constant.ts

@@ -0,0 +1,5 @@
+/** request id key */
+export const REQUEST_ID_KEY = 'X-Request-Id';
+
+/** the backend error code key */
+export const BACKEND_ERROR_CODE = 'BACKEND_ERROR';

+ 181 - 0
packages/axios/src/index.ts

@@ -0,0 +1,181 @@
+import axios, { AxiosError } from 'axios';
+import type { AxiosResponse, CancelTokenSource, CreateAxiosDefaults, InternalAxiosRequestConfig } from 'axios';
+import axiosRetry from 'axios-retry';
+import { nanoid } from '@sa/utils';
+import { createAxiosConfig, createDefaultOptions, createRetryOptions } from './options';
+import { BACKEND_ERROR_CODE, REQUEST_ID_KEY } from './constant';
+import type {
+  CustomAxiosRequestConfig,
+  FlatRequestInstance,
+  MappedType,
+  RequestInstance,
+  RequestOption,
+  ResponseType
+} from './type';
+
+function createCommonRequest<ResponseData = any>(
+  axiosConfig?: CreateAxiosDefaults,
+  options?: Partial<RequestOption<ResponseData>>
+) {
+  const opts = createDefaultOptions<ResponseData>(options);
+
+  const axiosConf = createAxiosConfig(axiosConfig);
+  const instance = axios.create(axiosConf);
+
+  const cancelTokenSourceMap = new Map<string, CancelTokenSource>();
+
+  // config axios retry
+  const retryOptions = createRetryOptions(axiosConf);
+  axiosRetry(instance, retryOptions);
+
+  instance.interceptors.request.use(conf => {
+    const config: InternalAxiosRequestConfig = { ...conf };
+
+    // set request id
+    const requestId = nanoid();
+    config.headers.set(REQUEST_ID_KEY, requestId);
+
+    // config cancel token
+    const cancelTokenSource = axios.CancelToken.source();
+    config.cancelToken = cancelTokenSource.token;
+    cancelTokenSourceMap.set(requestId, cancelTokenSource);
+
+    // handle config by hook
+    const handledConfig = opts.onRequest?.(config) || config;
+
+    return handledConfig;
+  });
+
+  instance.interceptors.response.use(
+    async response => {
+      const responseType: ResponseType = (response.config?.responseType as ResponseType) || 'json';
+
+      if (responseType !== 'json' || opts.isBackendSuccess(response)) {
+        return Promise.resolve(response);
+      }
+
+      const fail = await opts.onBackendFail(response, instance);
+      if (fail) {
+        return fail;
+      }
+
+      const backendError = new AxiosError<ResponseData>(
+        'the backend request error',
+        BACKEND_ERROR_CODE,
+        response.config,
+        response.request,
+        response
+      );
+
+      await opts.onError(backendError);
+
+      return Promise.reject(backendError);
+    },
+    async (error: AxiosError<ResponseData>) => {
+      await opts.onError(error);
+
+      return Promise.reject(error);
+    }
+  );
+
+  function cancelRequest(requestId: string) {
+    const cancelTokenSource = cancelTokenSourceMap.get(requestId);
+    if (cancelTokenSource) {
+      cancelTokenSource.cancel();
+      cancelTokenSourceMap.delete(requestId);
+    }
+  }
+
+  function cancelAllRequest() {
+    cancelTokenSourceMap.forEach(cancelTokenSource => {
+      cancelTokenSource.cancel();
+    });
+    cancelTokenSourceMap.clear();
+  }
+
+  return {
+    instance,
+    opts,
+    cancelRequest,
+    cancelAllRequest
+  };
+}
+
+/**
+ * create a request instance
+ *
+ * @param axiosConfig axios config
+ * @param options request options
+ */
+export function createRequest<ResponseData = any, State = Record<string, unknown>>(
+  axiosConfig?: CreateAxiosDefaults,
+  options?: Partial<RequestOption<ResponseData>>
+) {
+  const { instance, opts, cancelRequest, cancelAllRequest } = createCommonRequest<ResponseData>(axiosConfig, options);
+
+  const request: RequestInstance<State> = async function request<T = any, R extends ResponseType = 'json'>(
+    config: CustomAxiosRequestConfig
+  ) {
+    const response: AxiosResponse<ResponseData> = await instance(config);
+
+    const responseType = response.config?.responseType || 'json';
+
+    if (responseType === 'json') {
+      return opts.transformBackendResponse(response);
+    }
+
+    return response.data as MappedType<R, T>;
+  } as RequestInstance<State>;
+
+  request.cancelRequest = cancelRequest;
+  request.cancelAllRequest = cancelAllRequest;
+  request.state = {} as State;
+
+  return request;
+}
+
+/**
+ * create a flat request instance
+ *
+ * The response data is a flat object: { data: any, error: AxiosError }
+ *
+ * @param axiosConfig axios config
+ * @param options request options
+ */
+export function createFlatRequest<ResponseData = any, State = Record<string, unknown>>(
+  axiosConfig?: CreateAxiosDefaults,
+  options?: Partial<RequestOption<ResponseData>>
+) {
+  const { instance, opts, cancelRequest, cancelAllRequest } = createCommonRequest<ResponseData>(axiosConfig, options);
+
+  const flatRequest: FlatRequestInstance<State, ResponseData> = async function flatRequest<
+    T = any,
+    R extends ResponseType = 'json'
+  >(config: CustomAxiosRequestConfig) {
+    try {
+      const response: AxiosResponse<ResponseData> = await instance(config);
+
+      const responseType = response.config?.responseType || 'json';
+
+      if (responseType === 'json') {
+        const data = opts.transformBackendResponse(response);
+
+        return { data, error: null };
+      }
+
+      return { data: response.data as MappedType<R, T>, error: null };
+    } catch (error) {
+      return { data: null, error };
+    }
+  } as FlatRequestInstance<State, ResponseData>;
+
+  flatRequest.cancelRequest = cancelRequest;
+  flatRequest.cancelAllRequest = cancelAllRequest;
+  flatRequest.state = {} as State;
+
+  return flatRequest;
+}
+
+export { BACKEND_ERROR_CODE, REQUEST_ID_KEY };
+export type * from './type';
+export type { CreateAxiosDefaults, AxiosError };

+ 48 - 0
packages/axios/src/options.ts

@@ -0,0 +1,48 @@
+import type { CreateAxiosDefaults } from 'axios';
+import type { IAxiosRetryConfig } from 'axios-retry';
+import { stringify } from 'qs';
+import { isHttpSuccess } from './shared';
+import type { RequestOption } from './type';
+
+export function createDefaultOptions<ResponseData = any>(options?: Partial<RequestOption<ResponseData>>) {
+  const opts: RequestOption<ResponseData> = {
+    onRequest: async config => config,
+    isBackendSuccess: _response => true,
+    onBackendFail: async () => {},
+    transformBackendResponse: async response => response.data,
+    onError: async () => {}
+  };
+
+  Object.assign(opts, options);
+
+  return opts;
+}
+
+export function createRetryOptions(config?: Partial<CreateAxiosDefaults>) {
+  const retryConfig: IAxiosRetryConfig = {
+    retries: 3
+  };
+
+  Object.assign(retryConfig, config);
+
+  return retryConfig;
+}
+
+export function createAxiosConfig(config?: Partial<CreateAxiosDefaults>) {
+  const TEN_SECONDS = 10 * 1000;
+
+  const axiosConfig: CreateAxiosDefaults = {
+    timeout: TEN_SECONDS,
+    headers: {
+      'Content-Type': 'application/json'
+    },
+    validateStatus: isHttpSuccess,
+    paramsSerializer: params => {
+      return stringify(params);
+    }
+  };
+
+  Object.assign(axiosConfig, config);
+
+  return axiosConfig;
+}

+ 28 - 0
packages/axios/src/shared.ts

@@ -0,0 +1,28 @@
+import type { AxiosHeaderValue, AxiosResponse, InternalAxiosRequestConfig } from 'axios';
+
+export function getContentType(config: InternalAxiosRequestConfig) {
+  const contentType: AxiosHeaderValue = config.headers?.['Content-Type'] || 'application/json';
+
+  return contentType;
+}
+
+/**
+ * check if http status is success
+ *
+ * @param status
+ */
+export function isHttpSuccess(status: number) {
+  const isSuccessCode = status >= 200 && status < 300;
+  return isSuccessCode || status === 304;
+}
+
+/**
+ * is response json
+ *
+ * @param response axios response
+ */
+export function isResponseJson(response: AxiosResponse) {
+  const { responseType } = response.config;
+
+  return responseType === 'json' || responseType === undefined;
+}

+ 101 - 0
packages/axios/src/type.ts

@@ -0,0 +1,101 @@
+import type { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from 'axios';
+
+export type ContentType =
+  | 'text/html'
+  | 'text/plain'
+  | 'multipart/form-data'
+  | 'application/json'
+  | 'application/x-www-form-urlencoded'
+  | 'application/octet-stream';
+
+export interface RequestOption<ResponseData = any> {
+  /**
+   * The hook before request
+   *
+   * For example: You can add header token in this hook
+   *
+   * @param config Axios config
+   */
+  onRequest: (config: InternalAxiosRequestConfig) => InternalAxiosRequestConfig | Promise<InternalAxiosRequestConfig>;
+  /**
+   * The hook to check backend response is success or not
+   *
+   * @param response Axios response
+   */
+  isBackendSuccess: (response: AxiosResponse<ResponseData>) => boolean;
+  /**
+   * The hook after backend request fail
+   *
+   * For example: You can handle the expired token in this hook
+   *
+   * @param response Axios response
+   * @param instance Axios instance
+   */
+  onBackendFail: (
+    response: AxiosResponse<ResponseData>,
+    instance: AxiosInstance
+  ) => Promise<AxiosResponse | null> | Promise<void>;
+  /**
+   * transform backend response when the responseType is json
+   *
+   * @param response Axios response
+   */
+  transformBackendResponse(response: AxiosResponse<ResponseData>): any | Promise<any>;
+  /**
+   * The hook to handle error
+   *
+   * For example: You can show error message in this hook
+   *
+   * @param error
+   */
+  onError: (error: AxiosError<ResponseData>) => void | Promise<void>;
+}
+
+interface ResponseMap {
+  blob: Blob;
+  text: string;
+  arrayBuffer: ArrayBuffer;
+  stream: ReadableStream<Uint8Array>;
+  document: Document;
+}
+export type ResponseType = keyof ResponseMap | 'json';
+
+export type MappedType<R extends ResponseType, JsonType = any> = R extends keyof ResponseMap
+  ? ResponseMap[R]
+  : JsonType;
+
+export type CustomAxiosRequestConfig<R extends ResponseType = 'json'> = Omit<AxiosRequestConfig, 'responseType'> & {
+  responseType?: R;
+};
+
+export interface RequestInstanceCommon<T> {
+  cancelRequest: (requestId: string) => void;
+  cancelAllRequest: () => void;
+  /** you can set custom state in the request instance */
+  state: T;
+}
+
+/** The request instance */
+export interface RequestInstance<S = Record<string, unknown>> extends RequestInstanceCommon<S> {
+  <T = any, R extends ResponseType = 'json'>(config: CustomAxiosRequestConfig<R>): Promise<MappedType<R, T>>;
+}
+
+export type FlatResponseSuccessData<T = any> = {
+  data: T;
+  error: null;
+};
+
+export type FlatResponseFailData<ResponseData = any> = {
+  data: null;
+  error: AxiosError<ResponseData>;
+};
+
+export type FlatResponseData<T = any, ResponseData = any> =
+  | FlatResponseSuccessData<T>
+  | FlatResponseFailData<ResponseData>;
+
+export interface FlatRequestInstance<S = Record<string, unknown>, ResponseData = any> extends RequestInstanceCommon<S> {
+  <T = any, R extends ResponseType = 'json'>(
+    config: CustomAxiosRequestConfig<R>
+  ): Promise<FlatResponseData<MappedType<R, T>, ResponseData>>;
+}

+ 20 - 0
packages/axios/tsconfig.json

@@ -0,0 +1,20 @@
+{
+  "compilerOptions": {
+    "target": "ESNext",
+    "jsx": "preserve",
+    "lib": ["DOM", "ESNext"],
+    "baseUrl": ".",
+    "module": "ESNext",
+    "moduleResolution": "node",
+    "resolveJsonModule": true,
+    "types": ["node"],
+    "strict": true,
+    "strictNullChecks": true,
+    "noUnusedLocals": true,
+    "allowSyntheticDefaultImports": true,
+    "esModuleInterop": true,
+    "forceConsistentCasingInFileNames": true
+  },
+  "include": ["src/**/*"],
+  "exclude": ["node_modules", "dist"]
+}

+ 16 - 0
packages/color/package.json

@@ -0,0 +1,16 @@
+{
+  "name": "@sa/color",
+  "version": "1.2.6",
+  "exports": {
+    ".": "./src/index.ts"
+  },
+  "typesVersions": {
+    "*": {
+      "*": ["./src/*"]
+    }
+  },
+  "dependencies": {
+    "@sa/utils": "workspace:*",
+    "colord": "2.9.3"
+  }
+}

+ 2 - 0
packages/color/src/constant/index.ts

@@ -0,0 +1,2 @@
+export * from './name';
+export * from './palette';

+ 1579 - 0
packages/color/src/constant/name.ts

@@ -0,0 +1,1579 @@
+export const colorNames: [hex: string, name: string][] = [
+  ['#000000', 'Black'],
+  ['#000080', 'Navy Blue'],
+  ['#0000c8', 'Dark Blue'],
+  ['#0000ff', 'Blue'],
+  ['#000741', 'Stratos'],
+  ['#001b1c', 'Swamp'],
+  ['#002387', 'Resolution Blue'],
+  ['#002900', 'Deep Fir'],
+  ['#002e20', 'Burnham'],
+  ['#002fa7', 'International Klein Blue'],
+  ['#003153', 'Prussian Blue'],
+  ['#003366', 'Midnight Blue'],
+  ['#003399', 'Smalt'],
+  ['#003532', 'Deep Teal'],
+  ['#003e40', 'Cyprus'],
+  ['#004620', 'Kaitoke Green'],
+  ['#0047ab', 'Cobalt'],
+  ['#004816', 'Crusoe'],
+  ['#004950', 'Sherpa Blue'],
+  ['#0056a7', 'Endeavour'],
+  ['#00581a', 'Camarone'],
+  ['#0066cc', 'Science Blue'],
+  ['#0066ff', 'Blue Ribbon'],
+  ['#00755e', 'Tropical Rain Forest'],
+  ['#0076a3', 'Allports'],
+  ['#007ba7', 'Deep Cerulean'],
+  ['#007ec7', 'Lochmara'],
+  ['#007fff', 'Azure Radiance'],
+  ['#008080', 'Teal'],
+  ['#0095b6', 'Bondi Blue'],
+  ['#009dc4', 'Pacific Blue'],
+  ['#00a693', 'Persian Green'],
+  ['#00a86b', 'Jade'],
+  ['#00cc99', 'Caribbean Green'],
+  ['#00cccc', "Robin's Egg Blue"],
+  ['#00ff00', 'Green'],
+  ['#00ff7f', 'Spring Green'],
+  ['#00ffff', 'Cyan Aqua'],
+  ['#010d1a', 'Blue Charcoal'],
+  ['#011635', 'Midnight'],
+  ['#011d13', 'Holly'],
+  ['#012731', 'Daintree'],
+  ['#01361c', 'Cardin Green'],
+  ['#01371a', 'County Green'],
+  ['#013e62', 'Astronaut Blue'],
+  ['#013f6a', 'Regal Blue'],
+  ['#014b43', 'Aqua Deep'],
+  ['#015e85', 'Orient'],
+  ['#016162', 'Blue Stone'],
+  ['#016d39', 'Fun Green'],
+  ['#01796f', 'Pine Green'],
+  ['#017987', 'Blue Lagoon'],
+  ['#01826b', 'Deep Sea'],
+  ['#01a368', 'Green Haze'],
+  ['#022d15', 'English Holly'],
+  ['#02402c', 'Sherwood Green'],
+  ['#02478e', 'Congress Blue'],
+  ['#024e46', 'Evening Sea'],
+  ['#026395', 'Bahama Blue'],
+  ['#02866f', 'Observatory'],
+  ['#02a4d3', 'Cerulean'],
+  ['#03163c', 'Tangaroa'],
+  ['#032b52', 'Green Vogue'],
+  ['#036a6e', 'Mosque'],
+  ['#041004', 'Midnight Moss'],
+  ['#041322', 'Black Pearl'],
+  ['#042e4c', 'Blue Whale'],
+  ['#044022', 'Zuccini'],
+  ['#044259', 'Teal Blue'],
+  ['#051040', 'Deep Cove'],
+  ['#051657', 'Gulf Blue'],
+  ['#055989', 'Venice Blue'],
+  ['#056f57', 'Watercourse'],
+  ['#062a78', 'Catalina Blue'],
+  ['#063537', 'Tiber'],
+  ['#069b81', 'Gossamer'],
+  ['#06a189', 'Niagara'],
+  ['#073a50', 'Tarawera'],
+  ['#080110', 'Jaguar'],
+  ['#081910', 'Black Bean'],
+  ['#082567', 'Deep Sapphire'],
+  ['#088370', 'Elf Green'],
+  ['#08e8de', 'Bright Turquoise'],
+  ['#092256', 'Downriver'],
+  ['#09230f', 'Palm Green'],
+  ['#09255d', 'Madison'],
+  ['#093624', 'Bottle Green'],
+  ['#095859', 'Deep Sea Green'],
+  ['#097f4b', 'Salem'],
+  ['#0a001c', 'Black Russian'],
+  ['#0a480d', 'Dark Fern'],
+  ['#0a6906', 'Japanese Laurel'],
+  ['#0a6f75', 'Atoll'],
+  ['#0b0b0b', 'Cod Gray'],
+  ['#0b0f08', 'Marshland'],
+  ['#0b1107', 'Gordons Green'],
+  ['#0b1304', 'Black Forest'],
+  ['#0b6207', 'San Felix'],
+  ['#0bda51', 'Malachite'],
+  ['#0c0b1d', 'Ebony'],
+  ['#0c0d0f', 'Woodsmoke'],
+  ['#0c1911', 'Racing Green'],
+  ['#0c7a79', 'Surfie Green'],
+  ['#0c8990', 'Blue Chill'],
+  ['#0d0332', 'Black Rock'],
+  ['#0d1117', 'Bunker'],
+  ['#0d1c19', 'Aztec'],
+  ['#0d2e1c', 'Bush'],
+  ['#0e0e18', 'Cinder'],
+  ['#0e2a30', 'Firefly'],
+  ['#0f2d9e', 'Torea Bay'],
+  ['#10121d', 'Vulcan'],
+  ['#101405', 'Green Waterloo'],
+  ['#105852', 'Eden'],
+  ['#110c6c', 'Arapawa'],
+  ['#120a8f', 'Ultramarine'],
+  ['#123447', 'Elephant'],
+  ['#126b40', 'Jewel'],
+  ['#130000', 'Diesel'],
+  ['#130a06', 'Asphalt'],
+  ['#13264d', 'Blue Zodiac'],
+  ['#134f19', 'Parsley'],
+  ['#140600', 'Nero'],
+  ['#1450aa', 'Tory Blue'],
+  ['#151f4c', 'Bunting'],
+  ['#1560bd', 'Denim'],
+  ['#15736b', 'Genoa'],
+  ['#161928', 'Mirage'],
+  ['#161d10', 'Hunter Green'],
+  ['#162a40', 'Big Stone'],
+  ['#163222', 'Celtic'],
+  ['#16322c', 'Timber Green'],
+  ['#163531', 'Gable Green'],
+  ['#171f04', 'Pine Tree'],
+  ['#175579', 'Chathams Blue'],
+  ['#182d09', 'Deep Forest Green'],
+  ['#18587a', 'Blumine'],
+  ['#19330e', 'Palm Leaf'],
+  ['#193751', 'Nile Blue'],
+  ['#1959a8', 'Fun Blue'],
+  ['#1a1a68', 'Lucky Point'],
+  ['#1ab385', 'Mountain Meadow'],
+  ['#1b0245', 'Tolopea'],
+  ['#1b1035', 'Haiti'],
+  ['#1b127b', 'Deep Koamaru'],
+  ['#1b1404', 'Acadia'],
+  ['#1b2f11', 'Seaweed'],
+  ['#1b3162', 'Biscay'],
+  ['#1b659d', 'Matisse'],
+  ['#1c1208', 'Crowshead'],
+  ['#1c1e13', 'Rangoon Green'],
+  ['#1c39bb', 'Persian Blue'],
+  ['#1c402e', 'Everglade'],
+  ['#1c7c7d', 'Elm'],
+  ['#1d6142', 'Green Pea'],
+  ['#1e0f04', 'Creole'],
+  ['#1e1609', 'Karaka'],
+  ['#1e1708', 'El Paso'],
+  ['#1e385b', 'Cello'],
+  ['#1e433c', 'Te Papa Green'],
+  ['#1e90ff', 'Dodger Blue'],
+  ['#1e9ab0', 'Eastern Blue'],
+  ['#1f120f', 'Night Rider'],
+  ['#1fc2c2', 'Java'],
+  ['#20208d', 'Jacksons Purple'],
+  ['#202e54', 'Cloud Burst'],
+  ['#204852', 'Blue Dianne'],
+  ['#211a0e', 'Eternity'],
+  ['#220878', 'Deep Blue'],
+  ['#228b22', 'Forest Green'],
+  ['#233418', 'Mallard'],
+  ['#240a40', 'Violet'],
+  ['#240c02', 'Kilamanjaro'],
+  ['#242a1d', 'Log Cabin'],
+  ['#242e16', 'Black Olive'],
+  ['#24500f', 'Green House'],
+  ['#251607', 'Graphite'],
+  ['#251706', 'Cannon Black'],
+  ['#251f4f', 'Port Gore'],
+  ['#25272c', 'Shark'],
+  ['#25311c', 'Green Kelp'],
+  ['#2596d1', 'Curious Blue'],
+  ['#260368', 'Paua'],
+  ['#26056a', 'Paris M'],
+  ['#261105', 'Wood Bark'],
+  ['#261414', 'Gondola'],
+  ['#262335', 'Steel Gray'],
+  ['#26283b', 'Ebony Clay'],
+  ['#273a81', 'Bay Of Many'],
+  ['#27504b', 'Plantation'],
+  ['#278a5b', 'Eucalyptus'],
+  ['#281e15', 'Oil'],
+  ['#283a77', 'Astronaut'],
+  ['#286acd', 'Mariner'],
+  ['#290c5e', 'Violent Violet'],
+  ['#292130', 'Bastille'],
+  ['#292319', 'Zeus'],
+  ['#292937', 'Charade'],
+  ['#297b9a', 'Jelly Bean'],
+  ['#29ab87', 'Jungle Green'],
+  ['#2a0359', 'Cherry Pie'],
+  ['#2a140e', 'Coffee Bean'],
+  ['#2a2630', 'Baltic Sea'],
+  ['#2a380b', 'Turtle Green'],
+  ['#2a52be', 'Cerulean Blue'],
+  ['#2b0202', 'Sepia Black'],
+  ['#2b194f', 'Valhalla'],
+  ['#2b3228', 'Heavy Metal'],
+  ['#2c0e8c', 'Blue Gem'],
+  ['#2c1632', 'Revolver'],
+  ['#2c2133', 'Bleached Cedar'],
+  ['#2c8c84', 'Lochinvar'],
+  ['#2d2510', 'Mikado'],
+  ['#2d383a', 'Outer Space'],
+  ['#2d569b', 'St Tropaz'],
+  ['#2e0329', 'Jacaranda'],
+  ['#2e1905', 'Jacko Bean'],
+  ['#2e3222', 'Rangitoto'],
+  ['#2e3f62', 'Rhino'],
+  ['#2e8b57', 'Sea Green'],
+  ['#2ebfd4', 'Scooter'],
+  ['#2f270e', 'Onion'],
+  ['#2f3cb3', 'Governor Bay'],
+  ['#2f519e', 'Sapphire'],
+  ['#2f5a57', 'Spectra'],
+  ['#2f6168', 'Casal'],
+  ['#300529', 'Melanzane'],
+  ['#301f1e', 'Cocoa Brown'],
+  ['#302a0f', 'Woodrush'],
+  ['#304b6a', 'San Juan'],
+  ['#30d5c8', 'Turquoise'],
+  ['#311c17', 'Eclipse'],
+  ['#314459', 'Pickled Bluewood'],
+  ['#315ba1', 'Azure'],
+  ['#31728d', 'Calypso'],
+  ['#317d82', 'Paradiso'],
+  ['#32127a', 'Persian Indigo'],
+  ['#32293a', 'Blackcurrant'],
+  ['#323232', 'Mine Shaft'],
+  ['#325d52', 'Stromboli'],
+  ['#327c14', 'Bilbao'],
+  ['#327da0', 'Astral'],
+  ['#33036b', 'Christalle'],
+  ['#33292f', 'Thunder'],
+  ['#33cc99', 'Shamrock'],
+  ['#341515', 'Tamarind'],
+  ['#350036', 'Mardi Gras'],
+  ['#350e42', 'Valentino'],
+  ['#350e57', 'Jagger'],
+  ['#353542', 'Tuna'],
+  ['#354e8c', 'Chambray'],
+  ['#363050', 'Martinique'],
+  ['#363534', 'Tuatara'],
+  ['#363c0d', 'Waiouru'],
+  ['#36747d', 'Ming'],
+  ['#368716', 'La Palma'],
+  ['#370202', 'Chocolate'],
+  ['#371d09', 'Clinker'],
+  ['#37290e', 'Brown Tumbleweed'],
+  ['#373021', 'Birch'],
+  ['#377475', 'Oracle'],
+  ['#380474', 'Blue Diamond'],
+  ['#381a51', 'Grape'],
+  ['#383533', 'Dune'],
+  ['#384555', 'Oxford Blue'],
+  ['#384910', 'Clover'],
+  ['#394851', 'Limed Spruce'],
+  ['#396413', 'Dell'],
+  ['#3a0020', 'Toledo'],
+  ['#3a2010', 'Sambuca'],
+  ['#3a2a6a', 'Jacarta'],
+  ['#3a686c', 'William'],
+  ['#3a6a47', 'Killarney'],
+  ['#3ab09e', 'Keppel'],
+  ['#3b000b', 'Temptress'],
+  ['#3b0910', 'Aubergine'],
+  ['#3b1f1f', 'Jon'],
+  ['#3b2820', 'Treehouse'],
+  ['#3b7a57', 'Amazon'],
+  ['#3b91b4', 'Boston Blue'],
+  ['#3c0878', 'Windsor'],
+  ['#3c1206', 'Rebel'],
+  ['#3c1f76', 'Meteorite'],
+  ['#3c2005', 'Dark Ebony'],
+  ['#3c3910', 'Camouflage'],
+  ['#3c4151', 'Bright Gray'],
+  ['#3c4443', 'Cape Cod'],
+  ['#3c493a', 'Lunar Green'],
+  ['#3d0c02', 'Bean  '],
+  ['#3d2b1f', 'Bistre'],
+  ['#3d7d52', 'Goblin'],
+  ['#3e0480', 'Kingfisher Daisy'],
+  ['#3e1c14', 'Cedar'],
+  ['#3e2b23', 'English Walnut'],
+  ['#3e2c1c', 'Black Marlin'],
+  ['#3e3a44', 'Ship Gray'],
+  ['#3eabbf', 'Pelorous'],
+  ['#3f2109', 'Bronze'],
+  ['#3f2500', 'Cola'],
+  ['#3f3002', 'Madras'],
+  ['#3f307f', 'Minsk'],
+  ['#3f4c3a', 'Cabbage Pont'],
+  ['#3f583b', 'Tom Thumb'],
+  ['#3f5d53', 'Mineral Green'],
+  ['#3fc1aa', 'Puerto Rico'],
+  ['#3fff00', 'Harlequin'],
+  ['#401801', 'Brown Pod'],
+  ['#40291d', 'Cork'],
+  ['#403b38', 'Masala'],
+  ['#403d19', 'Thatch Green'],
+  ['#405169', 'Fiord'],
+  ['#40826d', 'Viridian'],
+  ['#40a860', 'Chateau Green'],
+  ['#410056', 'Ripe Plum'],
+  ['#411f10', 'Paco'],
+  ['#412010', 'Deep Oak'],
+  ['#413c37', 'Merlin'],
+  ['#414257', 'Gun Powder'],
+  ['#414c7d', 'East Bay'],
+  ['#4169e1', 'Royal Blue'],
+  ['#41aa78', 'Ocean Green'],
+  ['#420303', 'Burnt Maroon'],
+  ['#423921', 'Lisbon Brown'],
+  ['#427977', 'Faded Jade'],
+  ['#431560', 'Scarlet Gum'],
+  ['#433120', 'Iroko'],
+  ['#433e37', 'Armadillo'],
+  ['#434c59', 'River Bed'],
+  ['#436a0d', 'Green Leaf'],
+  ['#44012d', 'Barossa'],
+  ['#441d00', 'Morocco Brown'],
+  ['#444954', 'Mako'],
+  ['#454936', 'Kelp'],
+  ['#456cac', 'San Marino'],
+  ['#45b1e8', 'Picton Blue'],
+  ['#460b41', 'Loulou'],
+  ['#462425', 'Crater Brown'],
+  ['#465945', 'Gray Asparagus'],
+  ['#4682b4', 'Steel Blue'],
+  ['#480404', 'Rustic Red'],
+  ['#480607', 'Bulgarian Rose'],
+  ['#480656', 'Clairvoyant'],
+  ['#481c1c', 'Cocoa Bean'],
+  ['#483131', 'Woody Brown'],
+  ['#483c32', 'Taupe'],
+  ['#49170c', 'Van Cleef'],
+  ['#492615', 'Brown Derby'],
+  ['#49371b', 'Metallic Bronze'],
+  ['#495400', 'Verdun Green'],
+  ['#496679', 'Blue Bayoux'],
+  ['#497183', 'Bismark'],
+  ['#4a2a04', 'Bracken'],
+  ['#4a3004', 'Deep Bronze'],
+  ['#4a3c30', 'Mondo'],
+  ['#4a4244', 'Tundora'],
+  ['#4a444b', 'Gravel'],
+  ['#4a4e5a', 'Trout'],
+  ['#4b0082', 'Pigment Indigo'],
+  ['#4b5d52', 'Nandor'],
+  ['#4c3024', 'Saddle'],
+  ['#4c4f56', 'Abbey'],
+  ['#4d0135', 'Blackberry'],
+  ['#4d0a18', 'Cab Sav'],
+  ['#4d1e01', 'Indian Tan'],
+  ['#4d282d', 'Cowboy'],
+  ['#4d282e', 'Livid Brown'],
+  ['#4d3833', 'Rock'],
+  ['#4d3d14', 'Punga'],
+  ['#4d400f', 'Bronzetone'],
+  ['#4d5328', 'Woodland'],
+  ['#4e0606', 'Mahogany'],
+  ['#4e2a5a', 'Bossanova'],
+  ['#4e3b41', 'Matterhorn'],
+  ['#4e420c', 'Bronze Olive'],
+  ['#4e4562', 'Mulled Wine'],
+  ['#4e6649', 'Axolotl'],
+  ['#4e7f9e', 'Wedgewood'],
+  ['#4eabd1', 'Shakespeare'],
+  ['#4f1c70', 'Honey Flower'],
+  ['#4f2398', 'Daisy Bush'],
+  ['#4f69c6', 'Indigo'],
+  ['#4f7942', 'Fern Green'],
+  ['#4f9d5d', 'Fruit Salad'],
+  ['#4fa83d', 'Apple'],
+  ['#504351', 'Mortar'],
+  ['#507096', 'Kashmir Blue'],
+  ['#507672', 'Cutty Sark'],
+  ['#50c878', 'Emerald'],
+  ['#514649', 'Emperor'],
+  ['#516e3d', 'Chalet Green'],
+  ['#517c66', 'Como'],
+  ['#51808f', 'Smalt Blue'],
+  ['#52001f', 'Castro'],
+  ['#520c17', 'Maroon Oak'],
+  ['#523c94', 'Gigas'],
+  ['#533455', 'Voodoo'],
+  ['#534491', 'Victoria'],
+  ['#53824b', 'Hippie Green'],
+  ['#541012', 'Heath'],
+  ['#544333', 'Judge Gray'],
+  ['#54534d', 'Fuscous Gray'],
+  ['#549019', 'Vida Loca'],
+  ['#55280c', 'Cioccolato'],
+  ['#555b10', 'Saratoga'],
+  ['#556d56', 'Finlandia'],
+  ['#5590d9', 'Havelock Blue'],
+  ['#56b4be', 'Fountain Blue'],
+  ['#578363', 'Spring Leaves'],
+  ['#583401', 'Saddle Brown'],
+  ['#585562', 'Scarpa Flow'],
+  ['#587156', 'Cactus'],
+  ['#589aaf', 'Hippie Blue'],
+  ['#591d35', 'Wine Berry'],
+  ['#592804', 'Brown Bramble'],
+  ['#593737', 'Congo Brown'],
+  ['#594433', 'Millbrook'],
+  ['#5a6e9c', 'Waikawa Gray'],
+  ['#5a87a0', 'Horizon'],
+  ['#5b3013', 'Jambalaya'],
+  ['#5c0120', 'Bordeaux'],
+  ['#5c0536', 'Mulberry Wood'],
+  ['#5c2e01', 'Carnaby Tan'],
+  ['#5c5d75', 'Comet'],
+  ['#5d1e0f', 'Redwood'],
+  ['#5d4c51', 'Don Juan'],
+  ['#5d5c58', 'Chicago'],
+  ['#5d5e37', 'Verdigris'],
+  ['#5d7747', 'Dingley'],
+  ['#5da19f', 'Breaker Bay'],
+  ['#5e483e', 'Kabul'],
+  ['#5e5d3b', 'Hemlock'],
+  ['#5f3d26', 'Irish Coffee'],
+  ['#5f5f6e', 'Mid Gray'],
+  ['#5f6672', 'Shuttle Gray'],
+  ['#5fa777', 'Aqua Forest'],
+  ['#5fb3ac', 'Tradewind'],
+  ['#604913', 'Horses Neck'],
+  ['#605b73', 'Smoky'],
+  ['#606e68', 'Corduroy'],
+  ['#6093d1', 'Danube'],
+  ['#612718', 'Espresso'],
+  ['#614051', 'Eggplant'],
+  ['#615d30', 'Costa Del Sol'],
+  ['#61845f', 'Glade Green'],
+  ['#622f30', 'Buccaneer'],
+  ['#623f2d', 'Quincy'],
+  ['#624e9a', 'Butterfly Bush'],
+  ['#625119', 'West Coast'],
+  ['#626649', 'Finch'],
+  ['#639a8f', 'Patina'],
+  ['#63b76c', 'Fern'],
+  ['#6456b7', 'Blue Violet'],
+  ['#646077', 'Dolphin'],
+  ['#646463', 'Storm Dust'],
+  ['#646a54', 'Siam'],
+  ['#646e75', 'Nevada'],
+  ['#6495ed', 'Cornflower Blue'],
+  ['#64ccdb', 'Viking'],
+  ['#65000b', 'Rosewood'],
+  ['#651a14', 'Cherrywood'],
+  ['#652dc1', 'Purple Heart'],
+  ['#657220', 'Fern Frond'],
+  ['#65745d', 'Willow Grove'],
+  ['#65869f', 'Hoki'],
+  ['#660045', 'Pompadour'],
+  ['#660099', 'Purple'],
+  ['#66023c', 'Tyrian Purple'],
+  ['#661010', 'Dark Tan'],
+  ['#66b58f', 'Silver Tree'],
+  ['#66ff00', 'Bright Green'],
+  ['#66ff66', 'Screamin Green'],
+  ['#67032d', 'Black Rose'],
+  ['#675fa6', 'Scampi'],
+  ['#676662', 'Ironside Gray'],
+  ['#678975', 'Viridian Green'],
+  ['#67a712', 'Christi'],
+  ['#683600', 'Nutmeg Wood Finish'],
+  ['#685558', 'Zambezi'],
+  ['#685e6e', 'Salt Box'],
+  ['#692545', 'Tawny Port'],
+  ['#692d54', 'Finn'],
+  ['#695f62', 'Scorpion'],
+  ['#697e9a', 'Lynch'],
+  ['#6a442e', 'Spice'],
+  ['#6a5d1b', 'Himalaya'],
+  ['#6a6051', 'Soya Bean'],
+  ['#6b2a14', 'Hairy Heath'],
+  ['#6b3fa0', 'Royal Purple'],
+  ['#6b4e31', 'Shingle Fawn'],
+  ['#6b5755', 'Dorado'],
+  ['#6b8ba2', 'Bermuda Gray'],
+  ['#6b8e23', 'Olive Drab'],
+  ['#6c3082', 'Eminence'],
+  ['#6cdae7', 'Turquoise Blue'],
+  ['#6d0101', 'Lonestar'],
+  ['#6d5e54', 'Pine Cone'],
+  ['#6d6c6c', 'Dove Gray'],
+  ['#6d9292', 'Juniper'],
+  ['#6d92a1', 'Gothic'],
+  ['#6e0902', 'Red Oxide'],
+  ['#6e1d14', 'Moccaccino'],
+  ['#6e4826', 'Pickled Bean'],
+  ['#6e4b26', 'Dallas'],
+  ['#6e6d57', 'Kokoda'],
+  ['#6e7783', 'Pale Sky'],
+  ['#6f440c', 'Cafe Royale'],
+  ['#6f6a61', 'Flint'],
+  ['#6f8e63', 'Highland'],
+  ['#6f9d02', 'Limeade'],
+  ['#6fd0c5', 'Downy'],
+  ['#701c1c', 'Persian Plum'],
+  ['#704214', 'Sepia'],
+  ['#704a07', 'Antique Bronze'],
+  ['#704f50', 'Ferra'],
+  ['#706555', 'Coffee'],
+  ['#708090', 'Slate Gray'],
+  ['#711a00', 'Cedar Wood Finish'],
+  ['#71291d', 'Metallic Copper'],
+  ['#714693', 'Affair'],
+  ['#714ab2', 'Studio'],
+  ['#715d47', 'Tobacco Brown'],
+  ['#716338', 'Yellow Metal'],
+  ['#716b56', 'Peat'],
+  ['#716e10', 'Olivetone'],
+  ['#717486', 'Storm Gray'],
+  ['#718080', 'Sirocco'],
+  ['#71d9e2', 'Aquamarine Blue'],
+  ['#72010f', 'Venetian Red'],
+  ['#724a2f', 'Old Copper'],
+  ['#726d4e', 'Go Ben'],
+  ['#727b89', 'Raven'],
+  ['#731e8f', 'Seance'],
+  ['#734a12', 'Raw Umber'],
+  ['#736c9f', 'Kimberly'],
+  ['#736d58', 'Crocodile'],
+  ['#737829', 'Crete'],
+  ['#738678', 'Xanadu'],
+  ['#74640d', 'Spicy Mustard'],
+  ['#747d63', 'Limed Ash'],
+  ['#747d83', 'Rolling Stone'],
+  ['#748881', 'Blue Smoke'],
+  ['#749378', 'Laurel'],
+  ['#74c365', 'Mantis'],
+  ['#755a57', 'Russett'],
+  ['#7563a8', 'Deluge'],
+  ['#76395d', 'Cosmic'],
+  ['#7666c6', 'Blue Marguerite'],
+  ['#76bd17', 'Lima'],
+  ['#76d7ea', 'Sky Blue'],
+  ['#770f05', 'Dark Burgundy'],
+  ['#771f1f', 'Crown Of Thorns'],
+  ['#773f1a', 'Walnut'],
+  ['#776f61', 'Pablo'],
+  ['#778120', 'Pacifika'],
+  ['#779e86', 'Oxley'],
+  ['#77dd77', 'Pastel Green'],
+  ['#780109', 'Japanese Maple'],
+  ['#782d19', 'Mocha'],
+  ['#782f16', 'Peanut'],
+  ['#78866b', 'Camouflage Green'],
+  ['#788a25', 'Wasabi'],
+  ['#788bba', 'Ship Cove'],
+  ['#78a39c', 'Sea Nymph'],
+  ['#795d4c', 'Roman Coffee'],
+  ['#796878', 'Old Lavender'],
+  ['#796989', 'Rum'],
+  ['#796a78', 'Fedora'],
+  ['#796d62', 'Sandstone'],
+  ['#79deec', 'Spray'],
+  ['#7a013a', 'Siren'],
+  ['#7a58c1', 'Fuchsia Blue'],
+  ['#7a7a7a', 'Boulder'],
+  ['#7a89b8', 'Wild Blue Yonder'],
+  ['#7ac488', 'De York'],
+  ['#7b3801', 'Red Beech'],
+  ['#7b3f00', 'Cinnamon'],
+  ['#7b6608', 'Yukon Gold'],
+  ['#7b7874', 'Tapa'],
+  ['#7b7c94', 'Waterloo '],
+  ['#7b8265', 'Flax Smoke'],
+  ['#7b9f80', 'Amulet'],
+  ['#7ba05b', 'Asparagus'],
+  ['#7c1c05', 'Kenyan Copper'],
+  ['#7c7631', 'Pesto'],
+  ['#7c778a', 'Topaz'],
+  ['#7c7b7a', 'Concord'],
+  ['#7c7b82', 'Jumbo'],
+  ['#7c881a', 'Trendy Green'],
+  ['#7ca1a6', 'Gumbo'],
+  ['#7cb0a1', 'Acapulco'],
+  ['#7cb7bb', 'Neptune'],
+  ['#7d2c14', 'Pueblo'],
+  ['#7da98d', 'Bay Leaf'],
+  ['#7dc8f7', 'Malibu'],
+  ['#7dd8c6', 'Bermuda'],
+  ['#7e3a15', 'Copper Canyon'],
+  ['#7f1734', 'Claret'],
+  ['#7f3a02', 'Peru Tan'],
+  ['#7f626d', 'Falcon'],
+  ['#7f7589', 'Mobster'],
+  ['#7f76d3', 'Moody Blue'],
+  ['#7fff00', 'Chartreuse'],
+  ['#7fffd4', 'Aquamarine'],
+  ['#800000', 'Maroon'],
+  ['#800b47', 'Rose Bud Cherry'],
+  ['#801818', 'Falu Red'],
+  ['#80341f', 'Red Robin'],
+  ['#803790', 'Vivid Violet'],
+  ['#80461b', 'Russet'],
+  ['#807e79', 'Friar Gray'],
+  ['#808000', 'Olive'],
+  ['#808080', 'Gray'],
+  ['#80b3ae', 'Gulf Stream'],
+  ['#80b3c4', 'Glacier'],
+  ['#80ccea', 'Seagull'],
+  ['#81422c', 'Nutmeg'],
+  ['#816e71', 'Spicy Pink'],
+  ['#817377', 'Empress'],
+  ['#819885', 'Spanish Green'],
+  ['#826f65', 'Sand Dune'],
+  ['#828685', 'Gunsmoke'],
+  ['#828f72', 'Battleship Gray'],
+  ['#831923', 'Merlot'],
+  ['#837050', 'Shadow'],
+  ['#83aa5d', 'Chelsea Cucumber'],
+  ['#83d0c6', 'Monte Carlo'],
+  ['#843179', 'Plum'],
+  ['#84a0a0', 'Granny Smith'],
+  ['#8581d9', 'Chetwode Blue'],
+  ['#858470', 'Bandicoot'],
+  ['#859faf', 'Bali Hai'],
+  ['#85c4cc', 'Half Baked'],
+  ['#860111', 'Red Devil'],
+  ['#863c3c', 'Lotus'],
+  ['#86483c', 'Ironstone'],
+  ['#864d1e', 'Bull Shot'],
+  ['#86560a', 'Rusty Nail'],
+  ['#868974', 'Bitter'],
+  ['#86949f', 'Regent Gray'],
+  ['#871550', 'Disco'],
+  ['#87756e', 'Americano'],
+  ['#877c7b', 'Hurricane'],
+  ['#878d91', 'Oslo Gray'],
+  ['#87ab39', 'Sushi'],
+  ['#885342', 'Spicy Mix'],
+  ['#886221', 'Kumera'],
+  ['#888387', 'Suva Gray'],
+  ['#888d65', 'Avocado'],
+  ['#893456', 'Camelot'],
+  ['#893843', 'Solid Pink'],
+  ['#894367', 'Cannon Pink'],
+  ['#897d6d', 'Makara'],
+  ['#8a3324', 'Burnt Umber'],
+  ['#8a73d6', 'True V'],
+  ['#8a8360', 'Clay Creek'],
+  ['#8a8389', 'Monsoon'],
+  ['#8a8f8a', 'Stack'],
+  ['#8ab9f1', 'Jordy Blue'],
+  ['#8b00ff', 'Electric Violet'],
+  ['#8b0723', 'Monarch'],
+  ['#8b6b0b', 'Corn Harvest'],
+  ['#8b8470', 'Olive Haze'],
+  ['#8b847e', 'Schooner'],
+  ['#8b8680', 'Natural Gray'],
+  ['#8b9c90', 'Mantle'],
+  ['#8b9fee', 'Portage'],
+  ['#8ba690', 'Envy'],
+  ['#8ba9a5', 'Cascade'],
+  ['#8be6d8', 'Riptide'],
+  ['#8c055e', 'Cardinal Pink'],
+  ['#8c472f', 'Mule Fawn'],
+  ['#8c5738', 'Potters Clay'],
+  ['#8c6495', 'Trendy Pink'],
+  ['#8d0226', 'Paprika'],
+  ['#8d3d38', 'Sanguine Brown'],
+  ['#8d3f3f', 'Tosca'],
+  ['#8d7662', 'Cement'],
+  ['#8d8974', 'Granite Green'],
+  ['#8d90a1', 'Manatee'],
+  ['#8da8cc', 'Polo Blue'],
+  ['#8e0000', 'Red Berry'],
+  ['#8e4d1e', 'Rope'],
+  ['#8e6f70', 'Opium'],
+  ['#8e775e', 'Domino'],
+  ['#8e8190', 'Mamba'],
+  ['#8eabc1', 'Nepal'],
+  ['#8f021c', 'Pohutukawa'],
+  ['#8f3e33', 'El Salva'],
+  ['#8f4b0e', 'Korma'],
+  ['#8f8176', 'Squirrel'],
+  ['#8fd6b4', 'Vista Blue'],
+  ['#900020', 'Burgundy'],
+  ['#901e1e', 'Old Brick'],
+  ['#907874', 'Hemp'],
+  ['#907b71', 'Almond Frost'],
+  ['#908d39', 'Sycamore'],
+  ['#92000a', 'Sangria'],
+  ['#924321', 'Cumin'],
+  ['#926f5b', 'Beaver'],
+  ['#928573', 'Stonewall'],
+  ['#928590', 'Venus'],
+  ['#9370db', 'Medium Purple'],
+  ['#93ccea', 'Cornflower'],
+  ['#93dfb8', 'Algae Green'],
+  ['#944747', 'Copper Rust'],
+  ['#948771', 'Arrowtown'],
+  ['#950015', 'Scarlett'],
+  ['#956387', 'Strikemaster'],
+  ['#959396', 'Mountain Mist'],
+  ['#960018', 'Carmine'],
+  ['#964b00', 'Brown'],
+  ['#967059', 'Leather'],
+  ['#9678b6', "Purple Mountain's Majesty"],
+  ['#967bb6', 'Lavender Purple'],
+  ['#96a8a1', 'Pewter'],
+  ['#96bbab', 'Summer Green'],
+  ['#97605d', 'Au Chico'],
+  ['#9771b5', 'Wisteria'],
+  ['#97cd2d', 'Atlantis'],
+  ['#983d61', 'Vin Rouge'],
+  ['#9874d3', 'Lilac Bush'],
+  ['#98777b', 'Bazaar'],
+  ['#98811b', 'Hacienda'],
+  ['#988d77', 'Pale Oyster'],
+  ['#98ff98', 'Mint Green'],
+  ['#990066', 'Fresh Eggplant'],
+  ['#991199', 'Violet Eggplant'],
+  ['#991613', 'Tamarillo'],
+  ['#991b07', 'Totem Pole'],
+  ['#996666', 'Copper Rose'],
+  ['#9966cc', 'Amethyst'],
+  ['#997a8d', 'Mountbatten Pink'],
+  ['#9999cc', 'Blue Bell'],
+  ['#9a3820', 'Prairie Sand'],
+  ['#9a6e61', 'Toast'],
+  ['#9a9577', 'Gurkha'],
+  ['#9ab973', 'Olivine'],
+  ['#9ac2b8', 'Shadow Green'],
+  ['#9b4703', 'Oregon'],
+  ['#9b9e8f', 'Lemon Grass'],
+  ['#9c3336', 'Stiletto'],
+  ['#9d5616', 'Hawaiian Tan'],
+  ['#9dacb7', 'Gull Gray'],
+  ['#9dc209', 'Pistachio'],
+  ['#9de093', 'Granny Smith Apple'],
+  ['#9de5ff', 'Anakiwa'],
+  ['#9e5302', 'Chelsea Gem'],
+  ['#9e5b40', 'Sepia Skin'],
+  ['#9ea587', 'Sage'],
+  ['#9ea91f', 'Citron'],
+  ['#9eb1cd', 'Rock Blue'],
+  ['#9edee0', 'Morning Glory'],
+  ['#9f381d', 'Cognac'],
+  ['#9f821c', 'Reef Gold'],
+  ['#9f9f9c', 'Star Dust'],
+  ['#9fa0b1', 'Santas Gray'],
+  ['#9fd7d3', 'Sinbad'],
+  ['#9fdd8c', 'Feijoa'],
+  ['#a02712', 'Tabasco'],
+  ['#a1750d', 'Buttered Rum'],
+  ['#a1adb5', 'Hit Gray'],
+  ['#a1c50a', 'Citrus'],
+  ['#a1dad7', 'Aqua Island'],
+  ['#a1e9de', 'Water Leaf'],
+  ['#a2006d', 'Flirt'],
+  ['#a23b6c', 'Rouge'],
+  ['#a26645', 'Cape Palliser'],
+  ['#a2aab3', 'Gray Chateau'],
+  ['#a2aeab', 'Edward'],
+  ['#a3807b', 'Pharlap'],
+  ['#a397b4', 'Amethyst Smoke'],
+  ['#a3e3ed', 'Blizzard Blue'],
+  ['#a4a49d', 'Delta'],
+  ['#a4a6d3', 'Wistful'],
+  ['#a4af6e', 'Green Smoke'],
+  ['#a50b5e', 'Jazzberry Jam'],
+  ['#a59b91', 'Zorba'],
+  ['#a5cb0c', 'Bahia'],
+  ['#a62f20', 'Roof Terracotta'],
+  ['#a65529', 'Paarl'],
+  ['#a68b5b', 'Barley Corn'],
+  ['#a69279', 'Donkey Brown'],
+  ['#a6a29a', 'Dawn'],
+  ['#a72525', 'Mexican Red'],
+  ['#a7882c', 'Luxor Gold'],
+  ['#a85307', 'Rich Gold'],
+  ['#a86515', 'Reno Sand'],
+  ['#a86b6b', 'Coral Tree'],
+  ['#a8989b', 'Dusty Gray'],
+  ['#a899e6', 'Dull Lavender'],
+  ['#a8a589', 'Tallow'],
+  ['#a8ae9c', 'Bud'],
+  ['#a8af8e', 'Locust'],
+  ['#a8bd9f', 'Norway'],
+  ['#a8e3bd', 'Chinook'],
+  ['#a9a491', 'Gray Olive'],
+  ['#a9acb6', 'Aluminium'],
+  ['#a9b2c3', 'Cadet Blue'],
+  ['#a9b497', 'Schist'],
+  ['#a9bdbf', 'Tower Gray'],
+  ['#a9bef2', 'Perano'],
+  ['#a9c6c2', 'Opal'],
+  ['#aa375a', 'Night Shadz'],
+  ['#aa4203', 'Fire'],
+  ['#aa8b5b', 'Muesli'],
+  ['#aa8d6f', 'Sandal'],
+  ['#aaa5a9', 'Shady Lady'],
+  ['#aaa9cd', 'Logan'],
+  ['#aaabb7', 'Spun Pearl'],
+  ['#aad6e6', 'Regent St Blue'],
+  ['#aaf0d1', 'Magic Mint'],
+  ['#ab0563', 'Lipstick'],
+  ['#ab3472', 'Royal Heath'],
+  ['#ab917a', 'Sandrift'],
+  ['#aba0d9', 'Cold Purple'],
+  ['#aba196', 'Bronco'],
+  ['#ac8a56', 'Limed Oak'],
+  ['#ac91ce', 'East Side'],
+  ['#ac9e22', 'Lemon Ginger'],
+  ['#aca494', 'Napa'],
+  ['#aca586', 'Hillary'],
+  ['#aca59f', 'Cloudy'],
+  ['#acacac', 'Silver Chalice'],
+  ['#acb78e', 'Swamp Green'],
+  ['#accbb1', 'Spring Rain'],
+  ['#acdd4d', 'Conifer'],
+  ['#ace1af', 'Celadon'],
+  ['#ad781b', 'Mandalay'],
+  ['#adbed1', 'Casper'],
+  ['#addfad', 'Moss Green'],
+  ['#ade6c4', 'Padua'],
+  ['#adff2f', 'Green Yellow'],
+  ['#ae4560', 'Hippie Pink'],
+  ['#ae6020', 'Desert'],
+  ['#ae809e', 'Bouquet'],
+  ['#af4035', 'Medium Carmine'],
+  ['#af4d43', 'Apple Blossom'],
+  ['#af593e', 'Brown Rust'],
+  ['#af8751', 'Driftwood'],
+  ['#af8f2c', 'Alpine'],
+  ['#af9f1c', 'Lucky'],
+  ['#afa09e', 'Martini'],
+  ['#afb1b8', 'Bombay'],
+  ['#afbdd9', 'Pigeon Post'],
+  ['#b04c6a', 'Cadillac'],
+  ['#b05d54', 'Matrix'],
+  ['#b05e81', 'Tapestry'],
+  ['#b06608', 'Mai Tai'],
+  ['#b09a95', 'Del Rio'],
+  ['#b0e0e6', 'Powder Blue'],
+  ['#b0e313', 'Inch Worm'],
+  ['#b10000', 'Bright Red'],
+  ['#b14a0b', 'Vesuvius'],
+  ['#b1610b', 'Pumpkin Skin'],
+  ['#b16d52', 'Santa Fe'],
+  ['#b19461', 'Teak'],
+  ['#b1e2c1', 'Fringy Flower'],
+  ['#b1f4e7', 'Ice Cold'],
+  ['#b20931', 'Shiraz'],
+  ['#b2a1ea', 'Biloba Flower'],
+  ['#b32d29', 'Tall Poppy'],
+  ['#b35213', 'Fiery Orange'],
+  ['#b38007', 'Hot Toddy'],
+  ['#b3af95', 'Taupe Gray'],
+  ['#b3c110', 'La Rioja'],
+  ['#b43332', 'Well Read'],
+  ['#b44668', 'Blush'],
+  ['#b4cfd3', 'Jungle Mist'],
+  ['#b57281', 'Turkish Rose'],
+  ['#b57edc', 'Lavender'],
+  ['#b5a27f', 'Mongoose'],
+  ['#b5b35c', 'Olive Green'],
+  ['#b5d2ce', 'Jet Stream'],
+  ['#b5ecdf', 'Cruise'],
+  ['#b6316c', 'Hibiscus'],
+  ['#b69d98', 'Thatch'],
+  ['#b6b095', 'Heathered Gray'],
+  ['#b6baa4', 'Eagle'],
+  ['#b6d1ea', 'Spindle'],
+  ['#b6d3bf', 'Gum Leaf'],
+  ['#b7410e', 'Rust'],
+  ['#b78e5c', 'Muddy Waters'],
+  ['#b7a214', 'Sahara'],
+  ['#b7a458', 'Husk'],
+  ['#b7b1b1', 'Nobel'],
+  ['#b7c3d0', 'Heather'],
+  ['#b7f0be', 'Madang'],
+  ['#b81104', 'Milano Red'],
+  ['#b87333', 'Copper'],
+  ['#b8b56a', 'Gimblet'],
+  ['#b8c1b1', 'Green Spring'],
+  ['#b8c25d', 'Celery'],
+  ['#b8e0f9', 'Sail'],
+  ['#b94e48', 'Chestnut'],
+  ['#b95140', 'Crail'],
+  ['#b98d28', 'Marigold'],
+  ['#b9c46a', 'Wild Willow'],
+  ['#b9c8ac', 'Rainee'],
+  ['#ba0101', 'Guardsman Red'],
+  ['#ba450c', 'Rock Spray'],
+  ['#ba6f1e', 'Bourbon'],
+  ['#ba7f03', 'Pirate Gold'],
+  ['#bab1a2', 'Nomad'],
+  ['#bac7c9', 'Submarine'],
+  ['#baeef9', 'Charlotte'],
+  ['#bb3385', 'Medium Red Violet'],
+  ['#bb8983', 'Brandy Rose'],
+  ['#bbd009', 'Rio Grande'],
+  ['#bbd7c1', 'Surf'],
+  ['#bcc9c2', 'Powder Ash'],
+  ['#bd5e2e', 'Tuscany'],
+  ['#bd978e', 'Quicksand'],
+  ['#bdb1a8', 'Silk'],
+  ['#bdb2a1', 'Malta'],
+  ['#bdb3c7', 'Chatelle'],
+  ['#bdbbd7', 'Lavender Gray'],
+  ['#bdbdc6', 'French Gray'],
+  ['#bdc8b3', 'Clay Ash'],
+  ['#bdc9ce', 'Loblolly'],
+  ['#bdedfd', 'French Pass'],
+  ['#bea6c3', 'London Hue'],
+  ['#beb5b7', 'Pink Swan'],
+  ['#bede0d', 'Fuego'],
+  ['#bf5500', 'Rose Of Sharon'],
+  ['#bfb8b0', 'Tide'],
+  ['#bfbed8', 'Blue Haze'],
+  ['#bfc1c2', 'Silver Sand'],
+  ['#bfc921', 'Key Lime Pie'],
+  ['#bfdbe2', 'Ziggurat'],
+  ['#bfff00', 'Lime'],
+  ['#c02b18', 'Thunderbird'],
+  ['#c04737', 'Mojo'],
+  ['#c08081', 'Old Rose'],
+  ['#c0c0c0', 'Silver'],
+  ['#c0d3b9', 'Pale Leaf'],
+  ['#c0d8b6', 'Pixie Green'],
+  ['#c1440e', 'Tia Maria'],
+  ['#c154c1', 'Fuchsia Pink'],
+  ['#c1a004', 'Buddha Gold'],
+  ['#c1b7a4', 'Bison Hide'],
+  ['#c1bab0', 'Tea'],
+  ['#c1becd', 'Gray Suit'],
+  ['#c1d7b0', 'Sprout'],
+  ['#c1f07c', 'Sulu'],
+  ['#c26b03', 'Indochine'],
+  ['#c2955d', 'Twine'],
+  ['#c2bdb6', 'Cotton Seed'],
+  ['#c2cac4', 'Pumice'],
+  ['#c2e8e5', 'Jagged Ice'],
+  ['#c32148', 'Maroon Flush'],
+  ['#c3b091', 'Indian Khaki'],
+  ['#c3bfc1', 'Pale Slate'],
+  ['#c3c3bd', 'Gray Nickel'],
+  ['#c3cde6', 'Periwinkle Gray'],
+  ['#c3d1d1', 'Tiara'],
+  ['#c3ddf9', 'Tropical Blue'],
+  ['#c41e3a', 'Cardinal'],
+  ['#c45655', 'Fuzzy Wuzzy Brown'],
+  ['#c45719', 'Orange Roughy'],
+  ['#c4c4bc', 'Mist Gray'],
+  ['#c4d0b0', 'Coriander'],
+  ['#c4f4eb', 'Mint Tulip'],
+  ['#c54b8c', 'Mulberry'],
+  ['#c59922', 'Nugget'],
+  ['#c5994b', 'Tussock'],
+  ['#c5dbca', 'Sea Mist'],
+  ['#c5e17a', 'Yellow Green'],
+  ['#c62d42', 'Brick Red'],
+  ['#c6726b', 'Contessa'],
+  ['#c69191', 'Oriental Pink'],
+  ['#c6a84b', 'Roti'],
+  ['#c6c3b5', 'Ash'],
+  ['#c6c8bd', 'Kangaroo'],
+  ['#c6e610', 'Las Palmas'],
+  ['#c7031e', 'Monza'],
+  ['#c71585', 'Red Violet'],
+  ['#c7bca2', 'Coral Reef'],
+  ['#c7c1ff', 'Melrose'],
+  ['#c7c4bf', 'Cloud'],
+  ['#c7c9d5', 'Ghost'],
+  ['#c7cd90', 'Pine Glade'],
+  ['#c7dde5', 'Botticelli'],
+  ['#c88a65', 'Antique Brass'],
+  ['#c8a2c8', 'Lilac'],
+  ['#c8a528', 'Hokey Pokey'],
+  ['#c8aabf', 'Lily'],
+  ['#c8b568', 'Laser'],
+  ['#c8e3d7', 'Edgewater'],
+  ['#c96323', 'Piper'],
+  ['#c99415', 'Pizza'],
+  ['#c9a0dc', 'Light Wisteria'],
+  ['#c9b29b', 'Rodeo Dust'],
+  ['#c9b35b', 'Sundance'],
+  ['#c9b93b', 'Earls Green'],
+  ['#c9c0bb', 'Silver Rust'],
+  ['#c9d9d2', 'Conch'],
+  ['#c9ffa2', 'Reef'],
+  ['#c9ffe5', 'Aero Blue'],
+  ['#ca3435', 'Flush Mahogany'],
+  ['#cabb48', 'Turmeric'],
+  ['#cadcd4', 'Paris White'],
+  ['#cae00d', 'Bitter Lemon'],
+  ['#cae6da', 'Skeptic'],
+  ['#cb8fa9', 'Viola'],
+  ['#cbcab6', 'Foggy Gray'],
+  ['#cbd3b0', 'Green Mist'],
+  ['#cbdbd6', 'Nebula'],
+  ['#cc3333', 'Persian Red'],
+  ['#cc5500', 'Burnt Orange'],
+  ['#cc7722', 'Ochre'],
+  ['#cc8899', 'Puce'],
+  ['#cccaa8', 'Thistle Green'],
+  ['#ccccff', 'Periwinkle'],
+  ['#ccff00', 'Electric Lime'],
+  ['#cd5700', 'Tenn'],
+  ['#cd5c5c', 'Chestnut Rose'],
+  ['#cd8429', 'Brandy Punch'],
+  ['#cdf4ff', 'Onahau'],
+  ['#ceb98f', 'Sorrell Brown'],
+  ['#cebaba', 'Cold Turkey'],
+  ['#cec291', 'Yuma'],
+  ['#cec7a7', 'Chino'],
+  ['#cfa39d', 'Eunry'],
+  ['#cfb53b', 'Old Gold'],
+  ['#cfdccf', 'Tasman'],
+  ['#cfe5d2', 'Surf Crest'],
+  ['#cff9f3', 'Humming Bird'],
+  ['#cffaf4', 'Scandal'],
+  ['#d05f04', 'Red Stage'],
+  ['#d06da1', 'Hopbush'],
+  ['#d07d12', 'Meteor'],
+  ['#d0bef8', 'Perfume'],
+  ['#d0c0e5', 'Prelude'],
+  ['#d0f0c0', 'Tea Green'],
+  ['#d18f1b', 'Geebung'],
+  ['#d1bea8', 'Vanilla'],
+  ['#d1c6b4', 'Soft Amber'],
+  ['#d1d2ca', 'Celeste'],
+  ['#d1d2dd', 'Mischka'],
+  ['#d1e231', 'Pear'],
+  ['#d2691e', 'Hot Cinnamon'],
+  ['#d27d46', 'Raw Sienna'],
+  ['#d29eaa', 'Careys Pink'],
+  ['#d2b48c', 'Tan'],
+  ['#d2da97', 'Deco'],
+  ['#d2f6de', 'Blue Romance'],
+  ['#d2f8b0', 'Gossip'],
+  ['#d3cbba', 'Sisal'],
+  ['#d3cdc5', 'Swirl'],
+  ['#d47494', 'Charm'],
+  ['#d4b6af', 'Clam Shell'],
+  ['#d4bf8d', 'Straw'],
+  ['#d4c4a8', 'Akaroa'],
+  ['#d4cd16', 'Bird Flower'],
+  ['#d4d7d9', 'Iron'],
+  ['#d4dfe2', 'Geyser'],
+  ['#d4e2fc', 'Hawkes Blue'],
+  ['#d54600', 'Grenadier'],
+  ['#d591a4', 'Can Can'],
+  ['#d59a6f', 'Whiskey'],
+  ['#d5d195', 'Winter Hazel'],
+  ['#d5f6e3', 'Granny Apple'],
+  ['#d69188', 'My Pink'],
+  ['#d6c562', 'Tacha'],
+  ['#d6cef6', 'Moon Raker'],
+  ['#d6d6d1', 'Quill Gray'],
+  ['#d6ffdb', 'Snowy Mint'],
+  ['#d7837f', 'New York Pink'],
+  ['#d7c498', 'Pavlova'],
+  ['#d7d0ff', 'Fog'],
+  ['#d84437', 'Valencia'],
+  ['#d87c63', 'Japonica'],
+  ['#d8bfd8', 'Thistle'],
+  ['#d8c2d5', 'Maverick'],
+  ['#d8fcfa', 'Foam'],
+  ['#d94972', 'Cabaret'],
+  ['#d99376', 'Burning Sand'],
+  ['#d9b99b', 'Cameo'],
+  ['#d9d6cf', 'Timberwolf'],
+  ['#d9dcc1', 'Tana'],
+  ['#d9e4f5', 'Link Water'],
+  ['#d9f7ff', 'Mabel'],
+  ['#da3287', 'Cerise'],
+  ['#da5b38', 'Flame Pea'],
+  ['#da6304', 'Bamboo'],
+  ['#da6a41', 'Red Damask'],
+  ['#da70d6', 'Orchid'],
+  ['#da8a67', 'Copperfield'],
+  ['#daa520', 'Golden Grass'],
+  ['#daecd6', 'Zanah'],
+  ['#daf4f0', 'Iceberg'],
+  ['#dafaff', 'Oyster Bay'],
+  ['#db5079', 'Cranberry'],
+  ['#db9690', 'Petite Orchid'],
+  ['#db995e', 'Di Serria'],
+  ['#dbdbdb', 'Alto'],
+  ['#dbfff8', 'Frosted Mint'],
+  ['#dc143c', 'Crimson'],
+  ['#dc4333', 'Punch'],
+  ['#dcb20c', 'Galliano'],
+  ['#dcb4bc', 'Blossom'],
+  ['#dcd747', 'Wattle'],
+  ['#dcd9d2', 'Westar'],
+  ['#dcddcc', 'Moon Mist'],
+  ['#dcedb4', 'Caper'],
+  ['#dcf0ea', 'Swans Down'],
+  ['#ddd6d5', 'Swiss Coffee'],
+  ['#ddf9f1', 'White Ice'],
+  ['#de3163', 'Cerise Red'],
+  ['#de6360', 'Roman'],
+  ['#dea681', 'Tumbleweed'],
+  ['#deba13', 'Gold Tips'],
+  ['#dec196', 'Brandy'],
+  ['#decbc6', 'Wafer'],
+  ['#ded4a4', 'Sapling'],
+  ['#ded717', 'Barberry'],
+  ['#dee5c0', 'Beryl Green'],
+  ['#def5ff', 'Pattens Blue'],
+  ['#df73ff', 'Heliotrope'],
+  ['#dfbe6f', 'Apache'],
+  ['#dfcd6f', 'Chenin'],
+  ['#dfcfdb', 'Lola'],
+  ['#dfecda', 'Willow Brook'],
+  ['#dfff00', 'Chartreuse Yellow'],
+  ['#e0b0ff', 'Mauve'],
+  ['#e0b646', 'Anzac'],
+  ['#e0b974', 'Harvest Gold'],
+  ['#e0c095', 'Calico'],
+  ['#e0ffff', 'Baby Blue'],
+  ['#e16865', 'Sunglo'],
+  ['#e1bc64', 'Equator'],
+  ['#e1c0c8', 'Pink Flare'],
+  ['#e1e6d6', 'Periglacial Blue'],
+  ['#e1ead4', 'Kidnapper'],
+  ['#e1f6e8', 'Tara'],
+  ['#e25465', 'Mandy'],
+  ['#e2725b', 'Terracotta'],
+  ['#e28913', 'Golden Bell'],
+  ['#e292c0', 'Shocking'],
+  ['#e29418', 'Dixie'],
+  ['#e29cd2', 'Light Orchid'],
+  ['#e2d8ed', 'Snuff'],
+  ['#e2ebed', 'Mystic'],
+  ['#e2f3ec', 'Apple Green'],
+  ['#e30b5c', 'Razzmatazz'],
+  ['#e32636', 'Alizarin Crimson'],
+  ['#e34234', 'Cinnabar'],
+  ['#e3bebe', 'Cavern Pink'],
+  ['#e3f5e1', 'Peppermint'],
+  ['#e3f988', 'Mindaro'],
+  ['#e47698', 'Deep Blush'],
+  ['#e49b0f', 'Gamboge'],
+  ['#e4c2d5', 'Melanie'],
+  ['#e4cfde', 'Twilight'],
+  ['#e4d1c0', 'Bone'],
+  ['#e4d422', 'Sunflower'],
+  ['#e4d5b7', 'Grain Brown'],
+  ['#e4d69b', 'Zombie'],
+  ['#e4f6e7', 'Frostee'],
+  ['#e4ffd1', 'Snow Flurry'],
+  ['#e52b50', 'Amaranth'],
+  ['#e5841b', 'Zest'],
+  ['#e5ccc9', 'Dust Storm'],
+  ['#e5d7bd', 'Stark White'],
+  ['#e5d8af', 'Hampton'],
+  ['#e5e0e1', 'Bon Jour'],
+  ['#e5e5e5', 'Mercury'],
+  ['#e5f9f6', 'Polar'],
+  ['#e64e03', 'Trinidad'],
+  ['#e6be8a', 'Gold Sand'],
+  ['#e6bea5', 'Cashmere'],
+  ['#e6d7b9', 'Double Spanish White'],
+  ['#e6e4d4', 'Satin Linen'],
+  ['#e6f2ea', 'Harp'],
+  ['#e6f8f3', 'Off Green'],
+  ['#e6ffe9', 'Hint Of Green'],
+  ['#e6ffff', 'Tranquil'],
+  ['#e77200', 'Mango Tango'],
+  ['#e7730a', 'Christine'],
+  ['#e79f8c', 'Tonys Pink'],
+  ['#e79fc4', 'Kobi'],
+  ['#e7bcb4', 'Rose Fog'],
+  ['#e7bf05', 'Corn'],
+  ['#e7cd8c', 'Putty'],
+  ['#e7ece6', 'Gray Nurse'],
+  ['#e7f8ff', 'Lily White'],
+  ['#e7feff', 'Bubbles'],
+  ['#e89928', 'Fire Bush'],
+  ['#e8b9b3', 'Shilo'],
+  ['#e8e0d5', 'Pearl Bush'],
+  ['#e8ebe0', 'Green White'],
+  ['#e8f1d4', 'Chrome White'],
+  ['#e8f2eb', 'Gin'],
+  ['#e8f5f2', 'Aqua Squeeze'],
+  ['#e96e00', 'Clementine'],
+  ['#e97451', 'Burnt Sienna'],
+  ['#e97c07', 'Tahiti Gold'],
+  ['#e9cecd', 'Oyster Pink'],
+  ['#e9d75a', 'Confetti'],
+  ['#e9e3e3', 'Ebb'],
+  ['#e9f8ed', 'Ottoman'],
+  ['#e9fffd', 'Clear Day'],
+  ['#ea88a8', 'Carissma'],
+  ['#eaae69', 'Porsche'],
+  ['#eab33b', 'Tulip Tree'],
+  ['#eac674', 'Rob Roy'],
+  ['#eadab8', 'Raffia'],
+  ['#eae8d4', 'White Rock'],
+  ['#eaf6ee', 'Panache'],
+  ['#eaf6ff', 'Solitude'],
+  ['#eaf9f5', 'Aqua Spring'],
+  ['#eafffe', 'Dew'],
+  ['#eb9373', 'Apricot'],
+  ['#ebc2af', 'Zinnwaldite'],
+  ['#eca927', 'Fuel Yellow'],
+  ['#ecc54e', 'Ronchi'],
+  ['#ecc7ee', 'French Lilac'],
+  ['#eccdb9', 'Just Right'],
+  ['#ece090', 'Wild Rice'],
+  ['#ecebbd', 'Fall Green'],
+  ['#ecebce', 'Aths Special'],
+  ['#ecf245', 'Starship'],
+  ['#ed0a3f', 'Red Ribbon'],
+  ['#ed7a1c', 'Tango'],
+  ['#ed9121', 'Carrot Orange'],
+  ['#ed989e', 'Sea Pink'],
+  ['#edb381', 'Tacao'],
+  ['#edc9af', 'Desert Sand'],
+  ['#edcdab', 'Pancho'],
+  ['#eddcb1', 'Chamois'],
+  ['#edea99', 'Primrose'],
+  ['#edf5dd', 'Frost'],
+  ['#edf5f5', 'Aqua Haze'],
+  ['#edf6ff', 'Zumthor'],
+  ['#edf9f1', 'Narvik'],
+  ['#edfc84', 'Honeysuckle'],
+  ['#ee82ee', 'Lavender Magenta'],
+  ['#eec1be', 'Beauty Bush'],
+  ['#eed794', 'Chalky'],
+  ['#eed9c4', 'Almond'],
+  ['#eedc82', 'Flax'],
+  ['#eededa', 'Bizarre'],
+  ['#eee3ad', 'Double Colonial White'],
+  ['#eeeee8', 'Cararra'],
+  ['#eeef78', 'Manz'],
+  ['#eef0c8', 'Tahuna Sands'],
+  ['#eef0f3', 'Athens Gray'],
+  ['#eef3c3', 'Tusk'],
+  ['#eef4de', 'Loafer'],
+  ['#eef6f7', 'Catskill White'],
+  ['#eefdff', 'Twilight Blue'],
+  ['#eeff9a', 'Jonquil'],
+  ['#eeffe2', 'Rice Flower'],
+  ['#ef863f', 'Jaffa'],
+  ['#efefef', 'Gallery'],
+  ['#eff2f3', 'Porcelain'],
+  ['#f091a9', 'Mauvelous'],
+  ['#f0d52d', 'Golden Dream'],
+  ['#f0db7d', 'Golden Sand'],
+  ['#f0dc82', 'Buff'],
+  ['#f0e2ec', 'Prim'],
+  ['#f0e68c', 'Khaki'],
+  ['#f0eefd', 'Selago'],
+  ['#f0eeff', 'Titan White'],
+  ['#f0f8ff', 'Alice Blue'],
+  ['#f0fcea', 'Feta'],
+  ['#f18200', 'Gold Drop'],
+  ['#f19bab', 'Wewak'],
+  ['#f1e788', 'Sahara Sand'],
+  ['#f1e9d2', 'Parchment'],
+  ['#f1e9ff', 'Blue Chalk'],
+  ['#f1eec1', 'Mint Julep'],
+  ['#f1f1f1', 'Seashell'],
+  ['#f1f7f2', 'Saltpan'],
+  ['#f1ffad', 'Tidal'],
+  ['#f1ffc8', 'Chiffon'],
+  ['#f2552a', 'Flamingo'],
+  ['#f28500', 'Tangerine'],
+  ['#f2c3b2', 'Mandys Pink'],
+  ['#f2f2f2', 'Concrete'],
+  ['#f2fafa', 'Black Squeeze'],
+  ['#f34723', 'Pomegranate'],
+  ['#f3ad16', 'Buttercup'],
+  ['#f3d69d', 'New Orleans'],
+  ['#f3d9df', 'Vanilla Ice'],
+  ['#f3e7bb', 'Sidecar'],
+  ['#f3e9e5', 'Dawn Pink'],
+  ['#f3edcf', 'Wheatfield'],
+  ['#f3fb62', 'Canary'],
+  ['#f3fbd4', 'Orinoco'],
+  ['#f3ffd8', 'Carla'],
+  ['#f400a1', 'Hollywood Cerise'],
+  ['#f4a460', 'Sandy brown'],
+  ['#f4c430', 'Saffron'],
+  ['#f4d81c', 'Ripe Lemon'],
+  ['#f4ebd3', 'Janna'],
+  ['#f4f2ee', 'Pampas'],
+  ['#f4f4f4', 'Wild Sand'],
+  ['#f4f8ff', 'Zircon'],
+  ['#f57584', 'Froly'],
+  ['#f5c85c', 'Cream Can'],
+  ['#f5c999', 'Manhattan'],
+  ['#f5d5a0', 'Maize'],
+  ['#f5deb3', 'Wheat'],
+  ['#f5e7a2', 'Sandwisp'],
+  ['#f5e7e2', 'Pot Pourri'],
+  ['#f5e9d3', 'Albescent White'],
+  ['#f5edef', 'Soft Peach'],
+  ['#f5f3e5', 'Ecru White'],
+  ['#f5f5dc', 'Beige'],
+  ['#f5fb3d', 'Golden Fizz'],
+  ['#f5ffbe', 'Australian Mint'],
+  ['#f64a8a', 'French Rose'],
+  ['#f653a6', 'Brilliant Rose'],
+  ['#f6a4c9', 'Illusion'],
+  ['#f6f0e6', 'Merino'],
+  ['#f6f7f7', 'Black Haze'],
+  ['#f6ffdc', 'Spring Sun'],
+  ['#f7468a', 'Violet Red'],
+  ['#f77703', 'Chilean Fire'],
+  ['#f77fbe', 'Persian Pink'],
+  ['#f7b668', 'Rajah'],
+  ['#f7c8da', 'Azalea'],
+  ['#f7dbe6', 'We Peep'],
+  ['#f7f2e1', 'Quarter Spanish White'],
+  ['#f7f5fa', 'Whisper'],
+  ['#f7faf7', 'Snow Drift'],
+  ['#f8b853', 'Casablanca'],
+  ['#f8c3df', 'Chantilly'],
+  ['#f8d9e9', 'Cherub'],
+  ['#f8db9d', 'Marzipan'],
+  ['#f8dd5c', 'Energy Yellow'],
+  ['#f8e4bf', 'Givry'],
+  ['#f8f0e8', 'White Linen'],
+  ['#f8f4ff', 'Magnolia'],
+  ['#f8f6f1', 'Spring Wood'],
+  ['#f8f7dc', 'Coconut Cream'],
+  ['#f8f7fc', 'White Lilac'],
+  ['#f8f8f7', 'Desert Storm'],
+  ['#f8f99c', 'Texas'],
+  ['#f8facd', 'Corn Field'],
+  ['#f8fdd3', 'Mimosa'],
+  ['#f95a61', 'Carnation'],
+  ['#f9bf58', 'Saffron Mango'],
+  ['#f9e0ed', 'Carousel Pink'],
+  ['#f9e4bc', 'Dairy Cream'],
+  ['#f9e663', 'Portica'],
+  ['#f9eaf3', 'Amour'],
+  ['#f9f8e4', 'Rum Swizzle'],
+  ['#f9ff8b', 'Dolly'],
+  ['#f9fff6', 'Sugar Cane'],
+  ['#fa7814', 'Ecstasy'],
+  ['#fa9d5a', 'Tan Hide'],
+  ['#fad3a2', 'Corvette'],
+  ['#fadfad', 'Peach Yellow'],
+  ['#fae600', 'Turbo'],
+  ['#faeab9', 'Astra'],
+  ['#faeccc', 'Champagne'],
+  ['#faf0e6', 'Linen'],
+  ['#faf3f0', 'Fantasy'],
+  ['#faf7d6', 'Citrine White'],
+  ['#fafafa', 'Alabaster'],
+  ['#fafde4', 'Hint Of Yellow'],
+  ['#faffa4', 'Milan'],
+  ['#fb607f', 'Brink Pink'],
+  ['#fb8989', 'Geraldine'],
+  ['#fba0e3', 'Lavender Rose'],
+  ['#fba129', 'Sea Buckthorn'],
+  ['#fbac13', 'Sun'],
+  ['#fbaed2', 'Lavender Pink'],
+  ['#fbb2a3', 'Rose Bud'],
+  ['#fbbeda', 'Cupid'],
+  ['#fbcce7', 'Classic Rose'],
+  ['#fbceb1', 'Apricot Peach'],
+  ['#fbe7b2', 'Banana Mania'],
+  ['#fbe870', 'Marigold Yellow'],
+  ['#fbe96c', 'Festival'],
+  ['#fbea8c', 'Sweet Corn'],
+  ['#fbec5d', 'Candy Corn'],
+  ['#fbf9f9', 'Hint Of Red'],
+  ['#fbffba', 'Shalimar'],
+  ['#fc0fc0', 'Shocking Pink'],
+  ['#fc80a5', 'Tickle Me Pink'],
+  ['#fc9c1d', 'Tree Poppy'],
+  ['#fcc01e', 'Lightning Yellow'],
+  ['#fcd667', 'Goldenrod'],
+  ['#fcd917', 'Candlelight'],
+  ['#fcda98', 'Cherokee'],
+  ['#fcf4d0', 'Double Pearl Lusta'],
+  ['#fcf4dc', 'Pearl Lusta'],
+  ['#fcf8f7', 'Vista White'],
+  ['#fcfbf3', 'Bianca'],
+  ['#fcfeda', 'Moon Glow'],
+  ['#fcffe7', 'China Ivory'],
+  ['#fcfff9', 'Ceramic'],
+  ['#fd0e35', 'Torch Red'],
+  ['#fd5b78', 'Wild Watermelon'],
+  ['#fd7b33', 'Crusta'],
+  ['#fd7c07', 'Sorbus'],
+  ['#fd9fa2', 'Sweet Pink'],
+  ['#fdd5b1', 'Light Apricot'],
+  ['#fdd7e4', 'Pig Pink'],
+  ['#fde1dc', 'Cinderella'],
+  ['#fde295', 'Golden Glow'],
+  ['#fde910', 'Lemon'],
+  ['#fdf5e6', 'Old Lace'],
+  ['#fdf6d3', 'Half Colonial White'],
+  ['#fdf7ad', 'Drover'],
+  ['#fdfeb8', 'Pale Prim'],
+  ['#fdffd5', 'Cumulus'],
+  ['#fe28a2', 'Persian Rose'],
+  ['#fe4c40', 'Sunset Orange'],
+  ['#fe6f5e', 'Bittersweet'],
+  ['#fe9d04', 'California'],
+  ['#fea904', 'Yellow Sea'],
+  ['#febaad', 'Melon'],
+  ['#fed33c', 'Bright Sun'],
+  ['#fed85d', 'Dandelion'],
+  ['#fedb8d', 'Salomie'],
+  ['#fee5ac', 'Cape Honey'],
+  ['#feebf3', 'Remy'],
+  ['#feefce', 'Oasis'],
+  ['#fef0ec', 'Bridesmaid'],
+  ['#fef2c7', 'Beeswax'],
+  ['#fef3d8', 'Bleach White'],
+  ['#fef4cc', 'Pipi'],
+  ['#fef4db', 'Half Spanish White'],
+  ['#fef4f8', 'Wisp Pink'],
+  ['#fef5f1', 'Provincial Pink'],
+  ['#fef7de', 'Half Dutch White'],
+  ['#fef8e2', 'Solitaire'],
+  ['#fef8ff', 'White Pointer'],
+  ['#fef9e3', 'Off Yellow'],
+  ['#fefced', 'Orange White'],
+  ['#ff0000', 'Red'],
+  ['#ff007f', 'Rose'],
+  ['#ff00cc', 'Purple Pizzazz'],
+  ['#ff00ff', 'Magenta Fuchsia'],
+  ['#ff2400', 'Scarlet'],
+  ['#ff3399', 'Wild Strawberry'],
+  ['#ff33cc', 'Razzle Dazzle Rose'],
+  ['#ff355e', 'Radical Red'],
+  ['#ff3f34', 'Red Orange'],
+  ['#ff4040', 'Coral Red'],
+  ['#ff4d00', 'Vermilion'],
+  ['#ff4f00', 'International Orange'],
+  ['#ff6037', 'Outrageous Orange'],
+  ['#ff6600', 'Blaze Orange'],
+  ['#ff66ff', 'Pink Flamingo'],
+  ['#ff681f', 'Orange'],
+  ['#ff69b4', 'Hot Pink'],
+  ['#ff6b53', 'Persimmon'],
+  ['#ff6fff', 'Blush Pink'],
+  ['#ff7034', 'Burning Orange'],
+  ['#ff7518', 'Pumpkin'],
+  ['#ff7d07', 'Flamenco'],
+  ['#ff7f00', 'Flush Orange'],
+  ['#ff7f50', 'Coral'],
+  ['#ff8c69', 'Salmon'],
+  ['#ff9000', 'Pizazz'],
+  ['#ff910f', 'West Side'],
+  ['#ff91a4', 'Pink Salmon'],
+  ['#ff9933', 'Neon Carrot'],
+  ['#ff9966', 'Atomic Tangerine'],
+  ['#ff9980', 'Vivid Tangerine'],
+  ['#ff9e2c', 'Sunshade'],
+  ['#ffa000', 'Orange Peel'],
+  ['#ffa194', 'Mona Lisa'],
+  ['#ffa500', 'Web Orange'],
+  ['#ffa6c9', 'Carnation Pink'],
+  ['#ffab81', 'Hit Pink'],
+  ['#ffae42', 'Yellow Orange'],
+  ['#ffb0ac', 'Cornflower Lilac'],
+  ['#ffb1b3', 'Sundown'],
+  ['#ffb31f', 'My Sin'],
+  ['#ffb555', 'Texas Rose'],
+  ['#ffb7d5', 'Cotton Candy'],
+  ['#ffb97b', 'Macaroni And Cheese'],
+  ['#ffba00', 'Selective Yellow'],
+  ['#ffbd5f', 'Koromiko'],
+  ['#ffbf00', 'Amber'],
+  ['#ffc0a8', 'Wax Flower'],
+  ['#ffc0cb', 'Pink'],
+  ['#ffc3c0', 'Your Pink'],
+  ['#ffc901', 'Supernova'],
+  ['#ffcba4', 'Flesh'],
+  ['#ffcc33', 'Sunglow'],
+  ['#ffcc5c', 'Golden Tainoi'],
+  ['#ffcc99', 'Peach Orange'],
+  ['#ffcd8c', 'Chardonnay'],
+  ['#ffd1dc', 'Pastel Pink'],
+  ['#ffd2b7', 'Romantic'],
+  ['#ffd38c', 'Grandis'],
+  ['#ffd700', 'Gold'],
+  ['#ffd800', 'School Bus Yellow'],
+  ['#ffd8d9', 'Cosmos'],
+  ['#ffdb58', 'Mustard'],
+  ['#ffdcd6', 'Peach Schnapps'],
+  ['#ffddaf', 'Caramel'],
+  ['#ffddcd', 'Tuft Bush'],
+  ['#ffddcf', 'Watusi'],
+  ['#ffddf4', 'Pink Lace'],
+  ['#ffdead', 'Navajo White'],
+  ['#ffdeb3', 'Frangipani'],
+  ['#ffe1df', 'Pippin'],
+  ['#ffe1f2', 'Pale Rose'],
+  ['#ffe2c5', 'Negroni'],
+  ['#ffe5a0', 'Cream Brulee'],
+  ['#ffe5b4', 'Peach'],
+  ['#ffe6c7', 'Tequila'],
+  ['#ffe772', 'Kournikova'],
+  ['#ffeac8', 'Sandy Beach'],
+  ['#ffead4', 'Karry'],
+  ['#ffec13', 'Broom'],
+  ['#ffedbc', 'Colonial White'],
+  ['#ffeed8', 'Derby'],
+  ['#ffefa1', 'Vis Vis'],
+  ['#ffefc1', 'Egg White'],
+  ['#ffefd5', 'Papaya Whip'],
+  ['#ffefec', 'Fair Pink'],
+  ['#fff0db', 'Peach Cream'],
+  ['#fff0f5', 'Lavender Blush'],
+  ['#fff14f', 'Gorse'],
+  ['#fff1b5', 'Buttermilk'],
+  ['#fff1d8', 'Pink Lady'],
+  ['#fff1ee', 'Forget Me Not'],
+  ['#fff1f9', 'Tutu'],
+  ['#fff39d', 'Picasso'],
+  ['#fff3f1', 'Chardon'],
+  ['#fff46e', 'Paris Daisy'],
+  ['#fff4ce', 'Barley White'],
+  ['#fff4dd', 'Egg Sour'],
+  ['#fff4e0', 'Sazerac'],
+  ['#fff4e8', 'Serenade'],
+  ['#fff4f3', 'Chablis'],
+  ['#fff5ee', 'Seashell Peach'],
+  ['#fff5f3', 'Sauvignon'],
+  ['#fff6d4', 'Milk Punch'],
+  ['#fff6df', 'Varden'],
+  ['#fff6f5', 'Rose White'],
+  ['#fff8d1', 'Baja White'],
+  ['#fff9e2', 'Gin Fizz'],
+  ['#fff9e6', 'Early Dawn'],
+  ['#fffacd', 'Lemon Chiffon'],
+  ['#fffaf4', 'Bridal Heath'],
+  ['#fffbdc', 'Scotch Mist'],
+  ['#fffbf9', 'Soapstone'],
+  ['#fffc99', 'Witch Haze'],
+  ['#fffcea', 'Buttery White'],
+  ['#fffcee', 'Island Spice'],
+  ['#fffdd0', 'Cream'],
+  ['#fffde6', 'Chilean Heath'],
+  ['#fffde8', 'Travertine'],
+  ['#fffdf3', 'Orchid White'],
+  ['#fffdf4', 'Quarter Pearl Lusta'],
+  ['#fffee1', 'Half And Half'],
+  ['#fffeec', 'Apricot White'],
+  ['#fffef0', 'Rice Cake'],
+  ['#fffef6', 'Black White'],
+  ['#fffefd', 'Romance'],
+  ['#ffff00', 'Yellow'],
+  ['#ffff66', 'Laser Lemon'],
+  ['#ffff99', 'Pale Canary'],
+  ['#ffffb4', 'Portafino'],
+  ['#fffff0', 'Ivory'],
+  ['#ffffff', 'White']
+];
+
+/**
+ * Map Of hex color values to color names
+ *
+ * - key: hex value
+ * - value: color name
+ */
+export const colorNameMap = colorNames.reduce<Record<string, string>>((acc, [hex, name]) => {
+  acc[hex] = name;
+  return acc;
+}, {});

+ 356 - 0
packages/color/src/constant/palette.ts

@@ -0,0 +1,356 @@
+import type { ColorPaletteFamily } from '../types';
+
+export const colorPalettes: ColorPaletteFamily[] = [
+  {
+    name: 'Slate',
+    palettes: [
+      { hex: '#f8fafc', number: 50 },
+      { hex: '#f1f5f9', number: 100 },
+      { hex: '#e2e8f0', number: 200 },
+      { hex: '#cbd5e1', number: 300 },
+      { hex: '#94a3b8', number: 400 },
+      { hex: '#64748b', number: 500 },
+      { hex: '#475569', number: 600 },
+      { hex: '#334155', number: 700 },
+      { hex: '#1e293b', number: 800 },
+      { hex: '#0f172a', number: 900 },
+      { hex: '#020617', number: 950 }
+    ]
+  },
+  {
+    name: 'Gray',
+    palettes: [
+      { hex: '#f9fafb', number: 50 },
+      { hex: '#f3f4f6', number: 100 },
+      { hex: '#e5e7eb', number: 200 },
+      { hex: '#d1d5db', number: 300 },
+      { hex: '#9ca3af', number: 400 },
+      { hex: '#6b7280', number: 500 },
+      { hex: '#4b5563', number: 600 },
+      { hex: '#374151', number: 700 },
+      { hex: '#1f2937', number: 800 },
+      { hex: '#111827', number: 900 },
+      { hex: '#030712', number: 950 }
+    ]
+  },
+  {
+    name: 'Zinc',
+    palettes: [
+      { hex: '#fafafa', number: 50 },
+      { hex: '#f4f4f5', number: 100 },
+      { hex: '#e4e4e7', number: 200 },
+      { hex: '#d4d4d8', number: 300 },
+      { hex: '#a1a1aa', number: 400 },
+      { hex: '#71717a', number: 500 },
+      { hex: '#52525b', number: 600 },
+      { hex: '#3f3f46', number: 700 },
+      { hex: '#27272a', number: 800 },
+      { hex: '#18181b', number: 900 },
+      { hex: '#09090b', number: 950 }
+    ]
+  },
+  {
+    name: 'Neutral',
+    palettes: [
+      { hex: '#fafafa', number: 50 },
+      { hex: '#f5f5f5', number: 100 },
+      { hex: '#e5e5e5', number: 200 },
+      { hex: '#d4d4d4', number: 300 },
+      { hex: '#a3a3a3', number: 400 },
+      { hex: '#737373', number: 500 },
+      { hex: '#525252', number: 600 },
+      { hex: '#404040', number: 700 },
+      { hex: '#262626', number: 800 },
+      { hex: '#171717', number: 900 },
+      { hex: '#0a0a0a', number: 950 }
+    ]
+  },
+  {
+    name: 'Stone',
+    palettes: [
+      { hex: '#fafaf9', number: 50 },
+      { hex: '#f5f5f4', number: 100 },
+      { hex: '#e7e5e4', number: 200 },
+      { hex: '#d6d3d1', number: 300 },
+      { hex: '#a8a29e', number: 400 },
+      { hex: '#78716c', number: 500 },
+      { hex: '#57534e', number: 600 },
+      { hex: '#44403c', number: 700 },
+      { hex: '#292524', number: 800 },
+      { hex: '#1c1917', number: 900 },
+      { hex: '#0c0a09', number: 950 }
+    ]
+  },
+  {
+    name: 'Red',
+    palettes: [
+      { hex: '#fef2f2', number: 50 },
+      { hex: '#fee2e2', number: 100 },
+      { hex: '#fecaca', number: 200 },
+      { hex: '#fca5a5', number: 300 },
+      { hex: '#f87171', number: 400 },
+      { hex: '#ef4444', number: 500 },
+      { hex: '#dc2626', number: 600 },
+      { hex: '#b91c1c', number: 700 },
+      { hex: '#991b1b', number: 800 },
+      { hex: '#7f1d1d', number: 900 },
+      { hex: '#450a0a', number: 950 }
+    ]
+  },
+  {
+    name: 'Orange',
+    palettes: [
+      { hex: '#fff7ed', number: 50 },
+      { hex: '#ffedd5', number: 100 },
+      { hex: '#fed7aa', number: 200 },
+      { hex: '#fdba74', number: 300 },
+      { hex: '#fb923c', number: 400 },
+      { hex: '#f97316', number: 500 },
+      { hex: '#ea580c', number: 600 },
+      { hex: '#c2410c', number: 700 },
+      { hex: '#9a3412', number: 800 },
+      { hex: '#7c2d12', number: 900 },
+      { hex: '#431407', number: 950 }
+    ]
+  },
+  {
+    name: 'Amber',
+    palettes: [
+      { hex: '#fffbeb', number: 50 },
+      { hex: '#fef3c7', number: 100 },
+      { hex: '#fde68a', number: 200 },
+      { hex: '#fcd34d', number: 300 },
+      { hex: '#fbbf24', number: 400 },
+      { hex: '#f59e0b', number: 500 },
+      { hex: '#d97706', number: 600 },
+      { hex: '#b45309', number: 700 },
+      { hex: '#92400e', number: 800 },
+      { hex: '#78350f', number: 900 },
+      { hex: '#451a03', number: 950 }
+    ]
+  },
+  {
+    name: 'Yellow',
+    palettes: [
+      { hex: '#fefce8', number: 50 },
+      { hex: '#fef9c3', number: 100 },
+      { hex: '#fef08a', number: 200 },
+      { hex: '#fde047', number: 300 },
+      { hex: '#facc15', number: 400 },
+      { hex: '#eab308', number: 500 },
+      { hex: '#ca8a04', number: 600 },
+      { hex: '#a16207', number: 700 },
+      { hex: '#854d0e', number: 800 },
+      { hex: '#713f12', number: 900 },
+      { hex: '#422006', number: 950 }
+    ]
+  },
+  {
+    name: 'Lime',
+    palettes: [
+      { hex: '#f7fee7', number: 50 },
+      { hex: '#ecfccb', number: 100 },
+      { hex: '#d9f99d', number: 200 },
+      { hex: '#bef264', number: 300 },
+      { hex: '#a3e635', number: 400 },
+      { hex: '#84cc16', number: 500 },
+      { hex: '#65a30d', number: 600 },
+      { hex: '#4d7c0f', number: 700 },
+      { hex: '#3f6212', number: 800 },
+      { hex: '#365314', number: 900 },
+      { hex: '#1a2e05', number: 950 }
+    ]
+  },
+  {
+    name: 'Green',
+    palettes: [
+      { hex: '#f0fdf4', number: 50 },
+      { hex: '#dcfce7', number: 100 },
+      { hex: '#bbf7d0', number: 200 },
+      { hex: '#86efac', number: 300 },
+      { hex: '#4ade80', number: 400 },
+      { hex: '#22c55e', number: 500 },
+      { hex: '#16a34a', number: 600 },
+      { hex: '#15803d', number: 700 },
+      { hex: '#166534', number: 800 },
+      { hex: '#14532d', number: 900 },
+      { hex: '#052e16', number: 950 }
+    ]
+  },
+  {
+    name: 'Emerald',
+    palettes: [
+      { hex: '#ecfdf5', number: 50 },
+      { hex: '#d1fae5', number: 100 },
+      { hex: '#a7f3d0', number: 200 },
+      { hex: '#6ee7b7', number: 300 },
+      { hex: '#34d399', number: 400 },
+      { hex: '#10b981', number: 500 },
+      { hex: '#059669', number: 600 },
+      { hex: '#047857', number: 700 },
+      { hex: '#065f46', number: 800 },
+      { hex: '#064e3b', number: 900 },
+      { hex: '#022c22', number: 950 }
+    ]
+  },
+  {
+    name: 'Teal',
+    palettes: [
+      { hex: '#f0fdfa', number: 50 },
+      { hex: '#ccfbf1', number: 100 },
+      { hex: '#99f6e4', number: 200 },
+      { hex: '#5eead4', number: 300 },
+      { hex: '#2dd4bf', number: 400 },
+      { hex: '#14b8a6', number: 500 },
+      { hex: '#0d9488', number: 600 },
+      { hex: '#0f766e', number: 700 },
+      { hex: '#115e59', number: 800 },
+      { hex: '#134e4a', number: 900 },
+      { hex: '#042f2e', number: 950 }
+    ]
+  },
+  {
+    name: 'Cyan',
+    palettes: [
+      { hex: '#ecfeff', number: 50 },
+      { hex: '#cffafe', number: 100 },
+      { hex: '#a5f3fc', number: 200 },
+      { hex: '#67e8f9', number: 300 },
+      { hex: '#22d3ee', number: 400 },
+      { hex: '#06b6d4', number: 500 },
+      { hex: '#0891b2', number: 600 },
+      { hex: '#0e7490', number: 700 },
+      { hex: '#155e75', number: 800 },
+      { hex: '#164e63', number: 900 },
+      { hex: '#083344', number: 950 }
+    ]
+  },
+  {
+    name: 'Sky',
+    palettes: [
+      { hex: '#f0f9ff', number: 50 },
+      { hex: '#e0f2fe', number: 100 },
+      { hex: '#bae6fd', number: 200 },
+      { hex: '#7dd3fc', number: 300 },
+      { hex: '#38bdf8', number: 400 },
+      { hex: '#0ea5e9', number: 500 },
+      { hex: '#0284c7', number: 600 },
+      { hex: '#0369a1', number: 700 },
+      { hex: '#075985', number: 800 },
+      { hex: '#0c4a6e', number: 900 },
+      { hex: '#082f49', number: 950 }
+    ]
+  },
+  {
+    name: 'Blue',
+    palettes: [
+      { hex: '#eff6ff', number: 50 },
+      { hex: '#dbeafe', number: 100 },
+      { hex: '#bfdbfe', number: 200 },
+      { hex: '#93c5fd', number: 300 },
+      { hex: '#60a5fa', number: 400 },
+      { hex: '#3b82f6', number: 500 },
+      { hex: '#2563eb', number: 600 },
+      { hex: '#1d4ed8', number: 700 },
+      { hex: '#1e40af', number: 800 },
+      { hex: '#1e3a8a', number: 900 },
+      { hex: '#172554', number: 950 }
+    ]
+  },
+  {
+    name: 'Indigo',
+    palettes: [
+      { hex: '#eef2ff', number: 50 },
+      { hex: '#e0e7ff', number: 100 },
+      { hex: '#c7d2fe', number: 200 },
+      { hex: '#a5b4fc', number: 300 },
+      { hex: '#818cf8', number: 400 },
+      { hex: '#6366f1', number: 500 },
+      { hex: '#4f46e5', number: 600 },
+      { hex: '#4338ca', number: 700 },
+      { hex: '#3730a3', number: 800 },
+      { hex: '#312e81', number: 900 },
+      { hex: '#1e1b4b', number: 950 }
+    ]
+  },
+  {
+    name: 'Violet',
+    palettes: [
+      { hex: '#f5f3ff', number: 50 },
+      { hex: '#ede9fe', number: 100 },
+      { hex: '#ddd6fe', number: 200 },
+      { hex: '#c4b5fd', number: 300 },
+      { hex: '#a78bfa', number: 400 },
+      { hex: '#8b5cf6', number: 500 },
+      { hex: '#7c3aed', number: 600 },
+      { hex: '#6d28d9', number: 700 },
+      { hex: '#5b21b6', number: 800 },
+      { hex: '#4c1d95', number: 900 },
+      { hex: '#2e1065', number: 950 }
+    ]
+  },
+  {
+    name: 'Purple',
+    palettes: [
+      { hex: '#faf5ff', number: 50 },
+      { hex: '#f3e8ff', number: 100 },
+      { hex: '#e9d5ff', number: 200 },
+      { hex: '#d8b4fe', number: 300 },
+      { hex: '#c084fc', number: 400 },
+      { hex: '#a855f7', number: 500 },
+      { hex: '#9333ea', number: 600 },
+      { hex: '#7e22ce', number: 700 },
+      { hex: '#6b21a8', number: 800 },
+      { hex: '#581c87', number: 900 },
+      { hex: '#3b0764', number: 950 }
+    ]
+  },
+  {
+    name: 'Fuchsia',
+    palettes: [
+      { hex: '#fdf4ff', number: 50 },
+      { hex: '#fae8ff', number: 100 },
+      { hex: '#f5d0fe', number: 200 },
+      { hex: '#f0abfc', number: 300 },
+      { hex: '#e879f9', number: 400 },
+      { hex: '#d946ef', number: 500 },
+      { hex: '#c026d3', number: 600 },
+      { hex: '#a21caf', number: 700 },
+      { hex: '#86198f', number: 800 },
+      { hex: '#701a75', number: 900 },
+      { hex: '#4a044e', number: 950 }
+    ]
+  },
+  {
+    name: 'Pink',
+    palettes: [
+      { hex: '#fdf2f8', number: 50 },
+      { hex: '#fce7f3', number: 100 },
+      { hex: '#fbcfe8', number: 200 },
+      { hex: '#f9a8d4', number: 300 },
+      { hex: '#f472b6', number: 400 },
+      { hex: '#ec4899', number: 500 },
+      { hex: '#db2777', number: 600 },
+      { hex: '#be185d', number: 700 },
+      { hex: '#9d174d', number: 800 },
+      { hex: '#831843', number: 900 },
+      { hex: '#500724', number: 950 }
+    ]
+  },
+  {
+    name: 'Rose',
+    palettes: [
+      { hex: '#fff1f2', number: 50 },
+      { hex: '#ffe4e6', number: 100 },
+      { hex: '#fecdd3', number: 200 },
+      { hex: '#fda4af', number: 300 },
+      { hex: '#fb7185', number: 400 },
+      { hex: '#f43f5e', number: 500 },
+      { hex: '#e11d48', number: 600 },
+      { hex: '#be123c', number: 700 },
+      { hex: '#9f1239', number: 800 },
+      { hex: '#881337', number: 900 },
+      { hex: '#4c0519', number: 950 }
+    ]
+  }
+];

+ 7 - 0
packages/color/src/index.ts

@@ -0,0 +1,7 @@
+import { colorPalettes } from './constant';
+
+export * from './palette';
+export * from './shared';
+export { colorPalettes };
+
+export * from './types';

+ 176 - 0
packages/color/src/palette/antd.ts

@@ -0,0 +1,176 @@
+import type { AnyColor, HsvColor } from 'colord';
+import { getHex, getHsv, isValidColor, mixColor } from '../shared';
+import type { ColorIndex } from '../types';
+
+/** Hue step */
+const hueStep = 2;
+/** Saturation step, light color part */
+const saturationStep = 16;
+/** Saturation step, dark color part */
+const saturationStep2 = 5;
+/** Brightness step, light color part */
+const brightnessStep1 = 5;
+/** Brightness step, dark color part */
+const brightnessStep2 = 15;
+/** Light color count, main color up */
+const lightColorCount = 5;
+/** Dark color count, main color down */
+const darkColorCount = 4;
+
+/**
+ * Get AntD palette color by index
+ *
+ * @param color - Color
+ * @param index - The color index of color palette (the main color index is 6)
+ * @returns Hex color
+ */
+export function getAntDPaletteColorByIndex(color: AnyColor, index: ColorIndex): string {
+  if (!isValidColor(color)) {
+    throw new Error('invalid input color value');
+  }
+
+  if (index === 6) {
+    return getHex(color);
+  }
+
+  const isLight = index < 6;
+  const hsv = getHsv(color);
+  const i = isLight ? lightColorCount + 1 - index : index - lightColorCount - 1;
+
+  const newHsv: HsvColor = {
+    h: getHue(hsv, i, isLight),
+    s: getSaturation(hsv, i, isLight),
+    v: getValue(hsv, i, isLight)
+  };
+
+  return getHex(newHsv);
+}
+
+/** Map of dark color index and opacity */
+const darkColorMap = [
+  { index: 7, opacity: 0.15 },
+  { index: 6, opacity: 0.25 },
+  { index: 5, opacity: 0.3 },
+  { index: 5, opacity: 0.45 },
+  { index: 5, opacity: 0.65 },
+  { index: 5, opacity: 0.85 },
+  { index: 5, opacity: 0.9 },
+  { index: 4, opacity: 0.93 },
+  { index: 3, opacity: 0.95 },
+  { index: 2, opacity: 0.97 },
+  { index: 1, opacity: 0.98 }
+];
+
+/**
+ * Get AntD color palette
+ *
+ * @param color - Color
+ * @param darkTheme - Dark theme
+ * @param darkThemeMixColor - Dark theme mix color (default: #141414)
+ */
+export function getAntDColorPalette(color: AnyColor, darkTheme = false, darkThemeMixColor = '#141414'): string[] {
+  const indexes: ColorIndex[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
+
+  const patterns = indexes.map(index => getAntDPaletteColorByIndex(color, index));
+
+  if (darkTheme) {
+    const darkPatterns = darkColorMap.map(({ index, opacity }) => {
+      const darkColor = mixColor(darkThemeMixColor, patterns[index], opacity);
+
+      return darkColor;
+    });
+
+    return darkPatterns.map(item => getHex(item));
+  }
+
+  return patterns;
+}
+
+/**
+ * Get hue
+ *
+ * @param hsv - Hsv format color
+ * @param i - The relative distance from 6
+ * @param isLight - Is light color
+ */
+function getHue(hsv: HsvColor, i: number, isLight: boolean) {
+  let hue: number;
+
+  const hsvH = Math.round(hsv.h);
+
+  if (hsvH >= 60 && hsvH <= 240) {
+    hue = isLight ? hsvH - hueStep * i : hsvH + hueStep * i;
+  } else {
+    hue = isLight ? hsvH + hueStep * i : hsvH - hueStep * i;
+  }
+
+  if (hue < 0) {
+    hue += 360;
+  }
+
+  if (hue >= 360) {
+    hue -= 360;
+  }
+
+  return hue;
+}
+
+/**
+ * Get saturation
+ *
+ * @param hsv - Hsv format color
+ * @param i - The relative distance from 6
+ * @param isLight - Is light color
+ */
+function getSaturation(hsv: HsvColor, i: number, isLight: boolean) {
+  if (hsv.h === 0 && hsv.s === 0) {
+    return hsv.s;
+  }
+
+  let saturation: number;
+
+  if (isLight) {
+    saturation = hsv.s - saturationStep * i;
+  } else if (i === darkColorCount) {
+    saturation = hsv.s + saturationStep;
+  } else {
+    saturation = hsv.s + saturationStep2 * i;
+  }
+
+  if (saturation > 100) {
+    saturation = 100;
+  }
+
+  if (isLight && i === lightColorCount && saturation > 10) {
+    saturation = 10;
+  }
+
+  if (saturation < 6) {
+    saturation = 6;
+  }
+
+  return saturation;
+}
+
+/**
+ * Get value of hsv
+ *
+ * @param hsv - Hsv format color
+ * @param i - The relative distance from 6
+ * @param isLight - Is light color
+ */
+function getValue(hsv: HsvColor, i: number, isLight: boolean) {
+  let value: number;
+
+  if (isLight) {
+    value = hsv.v + brightnessStep1 * i;
+  } else {
+    value = hsv.v - brightnessStep2 * i;
+  }
+
+  if (value > 100) {
+    value = 100;
+  }
+
+  return value;
+}

+ 45 - 0
packages/color/src/palette/index.ts

@@ -0,0 +1,45 @@
+import type { AnyColor } from 'colord';
+import { getHex } from '../shared';
+import type { ColorPaletteNumber } from '../types';
+import { getRecommendedColorPalette } from './recommend';
+import { getAntDColorPalette } from './antd';
+
+/**
+ * get color palette by provided color
+ *
+ * @param color
+ * @param recommended whether to get recommended color palette (the provided color may not be the main color)
+ */
+export function getColorPalette(color: AnyColor, recommended = false) {
+  const colorMap = new Map<ColorPaletteNumber, string>();
+
+  if (recommended) {
+    const colorPalette = getRecommendedColorPalette(getHex(color));
+    colorPalette.palettes.forEach(palette => {
+      colorMap.set(palette.number, palette.hex);
+    });
+  } else {
+    const colors = getAntDColorPalette(color);
+
+    const colorNumbers: ColorPaletteNumber[] = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950];
+
+    colorNumbers.forEach((number, index) => {
+      colorMap.set(number, colors[index]);
+    });
+  }
+
+  return colorMap;
+}
+
+/**
+ * get color palette color by number
+ *
+ * @param color the provided color
+ * @param number the color palette number
+ * @param recommended whether to get recommended color palette (the provided color may not be the main color)
+ */
+export function getPaletteColorByNumber(color: AnyColor, number: ColorPaletteNumber, recommended = false) {
+  const colorMap = getColorPalette(color, recommended);
+
+  return colorMap.get(number as ColorPaletteNumber)!;
+}

+ 152 - 0
packages/color/src/palette/recommend.ts

@@ -0,0 +1,152 @@
+import { getColorName, getDeltaE, getHsl, isValidColor, transformHslToHex } from '../shared';
+import { colorPalettes } from '../constant';
+import type {
+  ColorPalette,
+  ColorPaletteFamily,
+  ColorPaletteFamilyWithNearestPalette,
+  ColorPaletteMatch,
+  ColorPaletteNumber
+} from '../types';
+
+/**
+ * get recommended color palette by provided color
+ *
+ * @param color the provided color
+ */
+export function getRecommendedColorPalette(color: string) {
+  const colorPaletteFamily = getRecommendedColorPaletteFamily(color);
+
+  const colorMap = new Map<ColorPaletteNumber, ColorPalette>();
+
+  colorPaletteFamily.palettes.forEach(palette => {
+    colorMap.set(palette.number, palette);
+  });
+
+  const mainColor = colorMap.get(500)!;
+  const matchColor = colorPaletteFamily.palettes.find(palette => palette.hex === color)!;
+
+  const colorPalette: ColorPaletteMatch = {
+    ...colorPaletteFamily,
+    colorMap,
+    main: mainColor,
+    match: matchColor
+  };
+
+  return colorPalette;
+}
+
+/**
+ * get recommended palette color by provided color
+ *
+ * @param color the provided color
+ * @param number the color palette number
+ */
+export function getRecommendedPaletteColorByNumber(color: string, number: ColorPaletteNumber) {
+  const colorPalette = getRecommendedColorPalette(color);
+
+  const { hex } = colorPalette.colorMap.get(number)!;
+
+  return hex;
+}
+
+/**
+ * get color palette family by provided color and color name
+ *
+ * @param color the provided color
+ */
+export function getRecommendedColorPaletteFamily(color: string) {
+  if (!isValidColor(color)) {
+    throw new Error('Invalid color, please check color value!');
+  }
+
+  let colorName = getColorName(color);
+
+  colorName = colorName.toLowerCase().replace(/\s/g, '-');
+
+  const { h: h1, s: s1 } = getHsl(color);
+
+  const { nearestLightnessPalette, palettes } = getNearestColorPaletteFamily(color, colorPalettes);
+
+  const { number, hex } = nearestLightnessPalette;
+
+  const { h: h2, s: s2 } = getHsl(hex);
+
+  const deltaH = h1 - h2;
+
+  const sRatio = s1 / s2;
+
+  const colorPaletteFamily: ColorPaletteFamily = {
+    name: colorName,
+    palettes: palettes.map(palette => {
+      let hexValue = color;
+
+      const isSame = number === palette.number;
+
+      if (!isSame) {
+        const { h: h3, s: s3, l } = getHsl(palette.hex);
+
+        const newH = deltaH < 0 ? h3 + deltaH : h3 - deltaH;
+        const newS = s3 * sRatio;
+
+        hexValue = transformHslToHex({
+          h: newH,
+          s: newS,
+          l
+        });
+      }
+
+      return {
+        hex: hexValue,
+        number: palette.number
+      };
+    })
+  };
+
+  return colorPaletteFamily;
+}
+
+/**
+ * get nearest color palette family
+ *
+ * @param color color
+ * @param families color palette families
+ */
+function getNearestColorPaletteFamily(color: string, families: ColorPaletteFamily[]) {
+  const familyWithConfig = families.map(family => {
+    const palettes = family.palettes.map(palette => {
+      return {
+        ...palette,
+        delta: getDeltaE(color, palette.hex)
+      };
+    });
+
+    const nearestPalette = palettes.reduce((prev, curr) => (prev.delta < curr.delta ? prev : curr));
+
+    return {
+      ...family,
+      palettes,
+      nearestPalette
+    };
+  });
+
+  const nearestPaletteFamily = familyWithConfig.reduce((prev, curr) =>
+    prev.nearestPalette.delta < curr.nearestPalette.delta ? prev : curr
+  );
+
+  const { l } = getHsl(color);
+
+  const paletteFamily: ColorPaletteFamilyWithNearestPalette = {
+    ...nearestPaletteFamily,
+    nearestLightnessPalette: nearestPaletteFamily.palettes.reduce((prev, curr) => {
+      const { l: prevLightness } = getHsl(prev.hex);
+      const { l: currLightness } = getHsl(curr.hex);
+
+      const deltaPrev = Math.abs(prevLightness - l);
+      const deltaCurr = Math.abs(currLightness - l);
+
+      return deltaPrev < deltaCurr ? prev : curr;
+    })
+  };
+
+  return paletteFamily;
+}

+ 93 - 0
packages/color/src/shared/colord.ts

@@ -0,0 +1,93 @@
+import { colord, extend } from 'colord';
+import namesPlugin from 'colord/plugins/names';
+import mixPlugin from 'colord/plugins/mix';
+import labPlugin from 'colord/plugins/lab';
+import type { AnyColor, HslColor, RgbColor } from 'colord';
+
+extend([namesPlugin, mixPlugin, labPlugin]);
+
+export function isValidColor(color: AnyColor) {
+  return colord(color).isValid();
+}
+
+export function getHex(color: AnyColor) {
+  return colord(color).toHex();
+}
+
+export function getRgb(color: AnyColor) {
+  return colord(color).toRgb();
+}
+
+export function getHsl(color: AnyColor) {
+  return colord(color).toHsl();
+}
+
+export function getHsv(color: AnyColor) {
+  return colord(color).toHsv();
+}
+
+export function getDeltaE(color1: AnyColor, color2: AnyColor) {
+  return colord(color1).delta(color2);
+}
+
+export function transformHslToHex(color: HslColor) {
+  return colord(color).toHex();
+}
+
+/**
+ * Add color alpha
+ *
+ * @param color - Color
+ * @param alpha - Alpha (0 - 1)
+ */
+export function addColorAlpha(color: AnyColor, alpha: number) {
+  return colord(color).alpha(alpha).toHex();
+}
+
+/**
+ * Mix color
+ *
+ * @param firstColor - First color
+ * @param secondColor - Second color
+ * @param ratio - The ratio of the second color (0 - 1)
+ */
+export function mixColor(firstColor: AnyColor, secondColor: AnyColor, ratio: number) {
+  return colord(firstColor).mix(secondColor, ratio).toHex();
+}
+
+/**
+ * Transform color with opacity to similar color without opacity
+ *
+ * @param color - Color
+ * @param alpha - Alpha (0 - 1)
+ * @param bgColor Background color (usually white or black)
+ */
+export function transformColorWithOpacity(color: AnyColor, alpha: number, bgColor = '#ffffff') {
+  const originColor = addColorAlpha(color, alpha);
+  const { r: oR, g: oG, b: oB } = colord(originColor).toRgb();
+
+  const { r: bgR, g: bgG, b: bgB } = colord(bgColor).toRgb();
+
+  function calRgb(or: number, bg: number, al: number) {
+    return bg + (or - bg) * al;
+  }
+
+  const resultRgb: RgbColor = {
+    r: calRgb(oR, bgR, alpha),
+    g: calRgb(oG, bgG, alpha),
+    b: calRgb(oB, bgB, alpha)
+  };
+
+  return colord(resultRgb).toHex();
+}
+
+/**
+ * Is white color
+ *
+ * @param color - Color
+ */
+export function isWhiteColor(color: AnyColor) {
+  return colord(color).isEqual('#ffffff');
+}
+
+export { colord };

+ 2 - 0
packages/color/src/shared/index.ts

@@ -0,0 +1,2 @@
+export * from './colord';
+export * from './name';

+ 49 - 0
packages/color/src/shared/name.ts

@@ -0,0 +1,49 @@
+import { colorNames } from '../constant';
+import { getHex, getHsl, getRgb } from './colord';
+
+/**
+ * Get color name
+ *
+ * @param color
+ */
+export function getColorName(color: string) {
+  const hex = getHex(color);
+  const rgb = getRgb(color);
+  const hsl = getHsl(color);
+
+  let ndf = 0;
+  let ndf1 = 0;
+  let ndf2 = 0;
+  let cl = -1;
+  let df = -1;
+
+  let name = '';
+
+  colorNames.some((item, index) => {
+    const [hexValue, colorName] = item;
+
+    const match = hex === hexValue;
+
+    if (match) {
+      name = colorName;
+    } else {
+      const { r, g, b } = getRgb(hexValue);
+      const { h, s, l } = getHsl(hexValue);
+
+      ndf1 = (rgb.r - r) ** 2 + (rgb.g - g) ** 2 + (rgb.b - b) ** 2;
+      ndf2 = (hsl.h - h) ** 2 + (hsl.s - s) ** 2 + (hsl.l - l) ** 2;
+
+      ndf = ndf1 + ndf2 * 2;
+      if (df < 0 || df > ndf) {
+        df = ndf;
+        cl = index;
+      }
+    }
+
+    return match;
+  });
+
+  name = colorNames[cl][1];
+
+  return name;
+}

+ 58 - 0
packages/color/src/types/index.ts

@@ -0,0 +1,58 @@
+/**
+ * the color palette number
+ *
+ * the main color number is 500
+ */
+export type ColorPaletteNumber = 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950;
+
+/** the color palette */
+export type ColorPalette = {
+  /** the color hex value */
+  hex: string;
+  /**
+   * the color number
+   *
+   * - 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950
+   */
+  number: ColorPaletteNumber;
+};
+
+/** the color palette family */
+export type ColorPaletteFamily = {
+  /** the color palette family name */
+  name: string;
+  /** the color palettes */
+  palettes: ColorPalette[];
+};
+
+/** the color palette with delta */
+export type ColorPaletteWithDelta = ColorPalette & {
+  delta: number;
+};
+
+/** the color palette family with nearest palette */
+export type ColorPaletteFamilyWithNearestPalette = ColorPaletteFamily & {
+  nearestPalette: ColorPaletteWithDelta;
+  nearestLightnessPalette: ColorPaletteWithDelta;
+};
+
+/** the color palette match */
+export type ColorPaletteMatch = ColorPaletteFamily & {
+  /** the color map of the palette */
+  colorMap: Map<ColorPaletteNumber, ColorPalette>;
+  /**
+   * the main color of the palette
+   *
+   * which number is 500
+   */
+  main: ColorPalette;
+  /** the match color of the palette */
+  match: ColorPalette;
+};
+
+/**
+ * The color index of color palette
+ *
+ * From left to right, the color is from light to dark, 6 is main color
+ */
+export type ColorIndex = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11;

+ 20 - 0
packages/color/tsconfig.json

@@ -0,0 +1,20 @@
+{
+  "compilerOptions": {
+    "target": "ESNext",
+    "jsx": "preserve",
+    "lib": ["DOM", "ESNext"],
+    "baseUrl": ".",
+    "module": "ESNext",
+    "moduleResolution": "node",
+    "resolveJsonModule": true,
+    "types": ["node"],
+    "strict": true,
+    "strictNullChecks": true,
+    "noUnusedLocals": true,
+    "allowSyntheticDefaultImports": true,
+    "esModuleInterop": true,
+    "forceConsistentCasingInFileNames": true
+  },
+  "include": ["src/**/*"],
+  "exclude": ["node_modules", "dist"]
+}

+ 15 - 0
packages/hooks/package.json

@@ -0,0 +1,15 @@
+{
+  "name": "@sa/hooks",
+  "version": "1.2.6",
+  "exports": {
+    ".": "./src/index.ts"
+  },
+  "typesVersions": {
+    "*": {
+      "*": ["./src/*"]
+    }
+  },
+  "dependencies": {
+    "@sa/axios": "workspace:*"
+  }
+}

+ 10 - 0
packages/hooks/src/index.ts

@@ -0,0 +1,10 @@
+import useBoolean from './use-boolean';
+import useLoading from './use-loading';
+import useCountDown from './use-count-down';
+import useContext from './use-context';
+import useSvgIconRender from './use-svg-icon-render';
+import useHookTable from './use-table';
+
+export { useBoolean, useLoading, useCountDown, useContext, useSvgIconRender, useHookTable };
+
+export * from './use-table';

+ 31 - 0
packages/hooks/src/use-boolean.ts

@@ -0,0 +1,31 @@
+import { ref } from 'vue';
+
+/**
+ * Boolean
+ *
+ * @param initValue Init value
+ */
+export default function useBoolean(initValue = false) {
+  const bool = ref(initValue);
+
+  function setBool(value: boolean) {
+    bool.value = value;
+  }
+  function setTrue() {
+    setBool(true);
+  }
+  function setFalse() {
+    setBool(false);
+  }
+  function toggle() {
+    setBool(!bool.value);
+  }
+
+  return {
+    bool,
+    setBool,
+    setTrue,
+    setFalse,
+    toggle
+  };
+}

+ 96 - 0
packages/hooks/src/use-context.ts

@@ -0,0 +1,96 @@
+import { inject, provide } from 'vue';
+import type { InjectionKey } from 'vue';
+
+/**
+ * Use context
+ *
+ * @example
+ *   ```ts
+ *   // there are three vue files: A.vue, B.vue, C.vue, and A.vue is the parent component of B.vue and C.vue
+ *
+ *   // context.ts
+ *   import { ref } from 'vue';
+ *   import { useContext } from '@sa/hooks';
+ *
+ *   export const { setupStore, useStore } = useContext('demo', () => {
+ *     const count = ref(0);
+ *
+ *     function increment() {
+ *       count.value++;
+ *     }
+ *
+ *     function decrement() {
+ *       count.value--;
+ *     }
+ *
+ *     return {
+ *       count,
+ *       increment,
+ *       decrement
+ *     };
+ *   })
+ *   ``` // A.vue
+ *   ```vue
+ *   <template>
+ *     <div>A</div>
+ *   </template>
+ *   <script setup lang="ts">
+ *   import { setupStore } from './context';
+ *
+ *   setupStore();
+ *   // const { increment } = setupStore(); // also can control the store in the parent component
+ *   </script>
+ *   ``` // B.vue
+ *   ```vue
+ *   <template>
+ *    <div>B</div>
+ *   </template>
+ *   <script setup lang="ts">
+ *   import { useStore } from './context';
+ *
+ *   const { count, increment } = useStore();
+ *   </script>
+ *   ```;
+ *
+ *   // C.vue is same as B.vue
+ *
+ * @param contextName Context name
+ * @param fn Context function
+ */
+export default function useContext<T extends (...args: any[]) => any>(contextName: string, fn: T) {
+  type Context = ReturnType<T>;
+
+  const { useProvide, useInject: useStore } = createContext<Context>(contextName);
+
+  function setupStore(...args: Parameters<T>) {
+    const context: Context = fn(...args);
+    return useProvide(context);
+  }
+
+  return {
+    /** Setup store in the parent component */
+    setupStore,
+    /** Use store in the child component */
+    useStore
+  };
+}
+
+/** Create context */
+function createContext<T>(contextName: string) {
+  const injectKey: InjectionKey<T> = Symbol(contextName);
+
+  function useProvide(context: T) {
+    provide(injectKey, context);
+
+    return context;
+  }
+
+  function useInject() {
+    return inject(injectKey) as T;
+  }
+
+  return {
+    useProvide,
+    useInject
+  };
+}

+ 49 - 0
packages/hooks/src/use-count-down.ts

@@ -0,0 +1,49 @@
+import { computed, onScopeDispose, ref } from 'vue';
+import { useRafFn } from '@vueuse/core';
+
+/**
+ * count down
+ *
+ * @param seconds - count down seconds
+ */
+export default function useCountDown(seconds: number) {
+  const FPS_PER_SECOND = 60;
+
+  const fps = ref(0);
+
+  const count = computed(() => Math.ceil(fps.value / FPS_PER_SECOND));
+
+  const isCounting = computed(() => fps.value > 0);
+
+  const { pause, resume } = useRafFn(
+    () => {
+      if (fps.value > 0) {
+        fps.value -= 1;
+      } else {
+        pause();
+      }
+    },
+    { immediate: false }
+  );
+
+  function start(updateSeconds: number = seconds) {
+    fps.value = FPS_PER_SECOND * updateSeconds;
+    resume();
+  }
+
+  function stop() {
+    fps.value = 0;
+    pause();
+  }
+
+  onScopeDispose(() => {
+    pause();
+  });
+
+  return {
+    count,
+    isCounting,
+    start,
+    stop
+  };
+}

+ 16 - 0
packages/hooks/src/use-loading.ts

@@ -0,0 +1,16 @@
+import useBoolean from './use-boolean';
+
+/**
+ * Loading
+ *
+ * @param initValue Init value
+ */
+export default function useLoading(initValue = false) {
+  const { bool: loading, setTrue: startLoading, setFalse: endLoading } = useBoolean(initValue);
+
+  return {
+    loading,
+    startLoading,
+    endLoading
+  };
+}

+ 79 - 0
packages/hooks/src/use-request.ts

@@ -0,0 +1,79 @@
+import { ref } from 'vue';
+import type { Ref } from 'vue';
+import { createFlatRequest } from '@sa/axios';
+import type {
+  AxiosError,
+  CreateAxiosDefaults,
+  CustomAxiosRequestConfig,
+  MappedType,
+  RequestOption,
+  ResponseType
+} from '@sa/axios';
+import useLoading from './use-loading';
+
+export type HookRequestInstanceResponseSuccessData<T = any> = {
+  data: Ref<T>;
+  error: Ref<null>;
+};
+
+export type HookRequestInstanceResponseFailData<ResponseData = any> = {
+  data: Ref<null>;
+  error: Ref<AxiosError<ResponseData>>;
+};
+
+export type HookRequestInstanceResponseData<T = any, ResponseData = any> = {
+  loading: Ref<boolean>;
+} & (HookRequestInstanceResponseSuccessData<T> | HookRequestInstanceResponseFailData<ResponseData>);
+
+export interface HookRequestInstance<ResponseData = any> {
+  <T = any, R extends ResponseType = 'json'>(
+    config: CustomAxiosRequestConfig
+  ): HookRequestInstanceResponseData<MappedType<R, T>, ResponseData>;
+  cancelRequest: (requestId: string) => void;
+  cancelAllRequest: () => void;
+}
+
+/**
+ * create a hook request instance
+ *
+ * @param axiosConfig
+ * @param options
+ */
+export default function createHookRequest<ResponseData = any>(
+  axiosConfig?: CreateAxiosDefaults,
+  options?: Partial<RequestOption<ResponseData>>
+) {
+  const request = createFlatRequest<ResponseData>(axiosConfig, options);
+
+  const hookRequest: HookRequestInstance<ResponseData> = function hookRequest<T = any, R extends ResponseType = 'json'>(
+    config: CustomAxiosRequestConfig
+  ) {
+    const { loading, startLoading, endLoading } = useLoading();
+
+    const data = ref<MappedType<R, T> | null>(null) as Ref<MappedType<R, T>>;
+    const error = ref<AxiosError<ResponseData> | null>(null) as Ref<AxiosError<ResponseData> | null>;
+
+    startLoading();
+
+    request(config).then(res => {
+      if (res.data) {
+        data.value = res.data;
+      } else {
+        error.value = res.error;
+      }
+
+      endLoading();
+    });
+
+    return {
+      loading,
+      data,
+      error
+    };
+  } as HookRequestInstance<ResponseData>;
+
+  hookRequest.cancelRequest = request.cancelRequest;
+  hookRequest.cancelAllRequest = request.cancelAllRequest;
+
+  return hookRequest;
+}

+ 50 - 0
packages/hooks/src/use-svg-icon-render.ts

@@ -0,0 +1,50 @@
+import { h } from 'vue';
+import type { Component } from 'vue';
+
+/**
+ * Svg icon render hook
+ *
+ * @param SvgIcon Svg icon component
+ */
+export default function useSvgIconRender(SvgIcon: Component) {
+  interface IconConfig {
+    /** Iconify icon name */
+    icon?: string;
+    /** Local icon name */
+    localIcon?: string;
+    /** Icon color */
+    color?: string;
+    /** Icon size */
+    fontSize?: number;
+  }
+
+  type IconStyle = Partial<Pick<CSSStyleDeclaration, 'color' | 'fontSize'>>;
+
+  /**
+   * Svg icon VNode
+   *
+   * @param config
+   */
+  const SvgIconVNode = (config: IconConfig) => {
+    const { color, fontSize, icon, localIcon } = config;
+
+    const style: IconStyle = {};
+
+    if (color) {
+      style.color = color;
+    }
+    if (fontSize) {
+      style.fontSize = `${fontSize}px`;
+    }
+
+    if (!icon && !localIcon) {
+      return undefined;
+    }
+
+    return () => h(SvgIcon, { icon, localIcon, style });
+  };
+
+  return {
+    SvgIconVNode
+  };
+}

+ 151 - 0
packages/hooks/src/use-table.ts

@@ -0,0 +1,151 @@
+import { computed, reactive, ref } from 'vue';
+import type { Ref } from 'vue';
+import useBoolean from './use-boolean';
+import useLoading from './use-loading';
+
+export type MaybePromise<T> = T | Promise<T>;
+
+export type ApiFn = (args: any) => Promise<unknown>;
+
+export type TableColumnCheck = {
+  key: string;
+  title: string;
+  checked: boolean;
+};
+
+export type TableDataWithIndex<T> = T & { index: number };
+
+export type TransformedData<T> = {
+  data: TableDataWithIndex<T>[];
+  pageNum: number;
+  pageSize: number;
+  total: number;
+};
+
+export type Transformer<T, Response> = (response: Response) => TransformedData<T>;
+
+export type TableConfig<A extends ApiFn, T, C> = {
+  /** api function to get table data */
+  apiFn: A;
+  /** api params */
+  apiParams?: Parameters<A>[0];
+  /** transform api response to table data */
+  transformer: Transformer<T, Awaited<ReturnType<A>>>;
+  /** columns factory */
+  columns: () => C[];
+  /**
+   * get column checks
+   *
+   * @param columns
+   */
+  getColumnChecks: (columns: C[]) => TableColumnCheck[];
+  /**
+   * get columns
+   *
+   * @param columns
+   */
+  getColumns: (columns: C[], checks: TableColumnCheck[]) => C[];
+  /**
+   * callback when response fetched
+   *
+   * @param transformed transformed data
+   */
+  onFetched?: (transformed: TransformedData<T>) => MaybePromise<void>;
+  /**
+   * whether to get data immediately
+   *
+   * @default true
+   */
+  immediate?: boolean;
+};
+
+export default function useHookTable<A extends ApiFn, T, C>(config: TableConfig<A, T, C>) {
+  const { loading, startLoading, endLoading } = useLoading();
+  const { bool: empty, setBool: setEmpty } = useBoolean();
+
+  const { apiFn, apiParams, transformer, immediate = true, getColumnChecks, getColumns } = config;
+
+  const searchParams: NonNullable<Parameters<A>[0]> = reactive({ ...apiParams });
+
+  const allColumns = ref(config.columns()) as Ref<C[]>;
+
+  const data: Ref<T[]> = ref([]);
+
+  const columnChecks: Ref<TableColumnCheck[]> = ref(getColumnChecks(config.columns()));
+
+  const columns = computed(() => getColumns(allColumns.value, columnChecks.value));
+
+  function reloadColumns() {
+    allColumns.value = config.columns();
+
+    const checkMap = new Map(columnChecks.value.map(col => [col.key, col.checked]));
+
+    const defaultChecks = getColumnChecks(allColumns.value);
+
+    columnChecks.value = defaultChecks.map(col => ({
+      ...col,
+      checked: checkMap.get(col.key) ?? col.checked
+    }));
+  }
+
+  async function getData() {
+    startLoading();
+
+    const formattedParams = formatSearchParams(searchParams);
+
+    const response = await apiFn(formattedParams);
+
+    const transformed = transformer(response as Awaited<ReturnType<A>>);
+
+    data.value = transformed.data;
+
+    setEmpty(transformed.data.length === 0);
+
+    await config.onFetched?.(transformed);
+
+    endLoading();
+  }
+
+  function formatSearchParams(params: Record<string, unknown>) {
+    const formattedParams: Record<string, unknown> = {};
+
+    Object.entries(params).forEach(([key, value]) => {
+      if (value !== null && value !== undefined) {
+        formattedParams[key] = value;
+      }
+    });
+
+    return formattedParams;
+  }
+
+  /**
+   * update search params
+   *
+   * @param params
+   */
+  function updateSearchParams(params: Partial<Parameters<A>[0]>) {
+    Object.assign(searchParams, params);
+  }
+
+  /** reset search params */
+  function resetSearchParams() {
+    Object.assign(searchParams, apiParams);
+  }
+
+  if (immediate) {
+    getData();
+  }
+
+  return {
+    loading,
+    empty,
+    data,
+    columns,
+    columnChecks,
+    reloadColumns,
+    getData,
+    searchParams,
+    updateSearchParams,
+    resetSearchParams
+  };
+}

+ 20 - 0
packages/hooks/tsconfig.json

@@ -0,0 +1,20 @@
+{
+  "compilerOptions": {
+    "target": "ESNext",
+    "jsx": "preserve",
+    "lib": ["DOM", "ESNext"],
+    "baseUrl": ".",
+    "module": "ESNext",
+    "moduleResolution": "node",
+    "resolveJsonModule": true,
+    "types": ["node"],
+    "strict": true,
+    "strictNullChecks": true,
+    "noUnusedLocals": true,
+    "allowSyntheticDefaultImports": true,
+    "esModuleInterop": true,
+    "forceConsistentCasingInFileNames": true
+  },
+  "include": ["src/**/*"],
+  "exclude": ["node_modules", "dist"]
+}

+ 20 - 0
packages/materials/package.json

@@ -0,0 +1,20 @@
+{
+  "name": "@sa/materials",
+  "version": "1.2.6",
+  "exports": {
+    ".": "./src/index.ts"
+  },
+  "typesVersions": {
+    "*": {
+      "*": ["./src/*"]
+    }
+  },
+  "dependencies": {
+    "@sa/utils": "workspace:*",
+    "@simonwep/pickr": "1.9.1",
+    "simplebar-vue": "2.3.5"
+  },
+  "devDependencies": {
+    "typed-css-modules": "0.9.1"
+  }
+}

+ 7 - 0
packages/materials/src/index.ts

@@ -0,0 +1,7 @@
+import AdminLayout, { LAYOUT_MAX_Z_INDEX, LAYOUT_SCROLL_EL_ID } from './libs/admin-layout';
+import PageTab from './libs/page-tab';
+import SimpleScrollbar from './libs/simple-scrollbar';
+import ColorPicker from './libs/color-picker';
+
+export { AdminLayout, LAYOUT_SCROLL_EL_ID, LAYOUT_MAX_Z_INDEX, PageTab, SimpleScrollbar, ColorPicker };
+export * from './types';

+ 63 - 0
packages/materials/src/libs/admin-layout/index.module.css

@@ -0,0 +1,63 @@
+/* @type */
+
+.layout-header,
+.layout-header-placement {
+  height: var(--soy-header-height);
+}
+
+.layout-header {
+  z-index: var(--soy-header-z-index);
+}
+
+.layout-tab {
+  top: var(--soy-header-height);
+  height: var(--soy-tab-height);
+  z-index: var(--soy-tab-z-index);
+}
+
+.layout-tab-placement {
+  height: var(--soy-tab-height);
+}
+
+.layout-sider {
+  width: var(--soy-sider-width);
+  z-index: var(--soy-sider-z-index);
+}
+
+.layout-mobile-sider {
+  z-index: var(--soy-sider-z-index);
+}
+
+.layout-mobile-sider-mask {
+  z-index: var(--soy-mobile-sider-z-index);
+}
+
+.layout-sider_collapsed {
+  width: var(--soy-sider-collapsed-width);
+  z-index: var(--soy-sider-z-index);
+}
+
+.layout-footer,
+.layout-footer-placement {
+  height: var(--soy-footer-height);
+}
+
+.layout-footer {
+  z-index: var(--soy-footer-z-index);
+}
+
+.left-gap {
+  padding-left: var(--soy-sider-width);
+}
+
+.left-gap_collapsed {
+  padding-left: var(--soy-sider-collapsed-width);
+}
+
+.sider-padding-top {
+  padding-top: var(--soy-header-height);
+}
+
+.sider-padding-bottom {
+  padding-bottom: var(--soy-footer-height);
+}

+ 17 - 0
packages/materials/src/libs/admin-layout/index.module.css.d.ts

@@ -0,0 +1,17 @@
+declare const styles: {
+  readonly 'layout-header': string;
+  readonly 'layout-header-placement': string;
+  readonly 'layout-tab': string;
+  readonly 'layout-tab-placement': string;
+  readonly 'layout-sider': string;
+  readonly 'layout-mobile-sider': string;
+  readonly 'layout-mobile-sider-mask': string;
+  readonly 'layout-sider_collapsed': string;
+  readonly 'layout-footer': string;
+  readonly 'layout-footer-placement': string;
+  readonly 'left-gap': string;
+  readonly 'left-gap_collapsed': string;
+  readonly 'sider-padding-top': string;
+  readonly 'sider-padding-bottom': string;
+};
+export default styles;

+ 5 - 0
packages/materials/src/libs/admin-layout/index.ts

@@ -0,0 +1,5 @@
+import AdminLayout from './index.vue';
+import { LAYOUT_MAX_Z_INDEX, LAYOUT_SCROLL_EL_ID } from './shared';
+
+export default AdminLayout;
+export { LAYOUT_SCROLL_EL_ID, LAYOUT_MAX_Z_INDEX };

+ 237 - 0
packages/materials/src/libs/admin-layout/index.vue

@@ -0,0 +1,237 @@
+<script setup lang="ts">
+import { computed } from 'vue';
+import type { AdminLayoutProps } from '../../types';
+import { LAYOUT_MAX_Z_INDEX, LAYOUT_SCROLL_EL_ID, createLayoutCssVars } from './shared';
+import style from './index.module.css';
+
+defineOptions({
+  name: 'AdminLayout'
+});
+
+const props = withDefaults(defineProps<AdminLayoutProps>(), {
+  mode: 'vertical',
+  scrollMode: 'content',
+  scrollElId: LAYOUT_SCROLL_EL_ID,
+  commonClass: 'transition-all-300',
+  fixedTop: true,
+  maxZIndex: LAYOUT_MAX_Z_INDEX,
+  headerVisible: true,
+  headerHeight: 56,
+  tabVisible: true,
+  tabHeight: 48,
+  siderVisible: true,
+  siderCollapse: false,
+  siderWidth: 220,
+  siderCollapsedWidth: 64,
+  footerVisible: true,
+  footerHeight: 48,
+  rightFooter: false
+});
+
+interface Emits {
+  /** Update siderCollapse */
+  (e: 'update:siderCollapse', collapse: boolean): void;
+}
+
+const emit = defineEmits<Emits>();
+
+type SlotFn = (props?: Record<string, unknown>) => any;
+
+type Slots = {
+  /** Main */
+  default?: SlotFn;
+  /** Header */
+  header?: SlotFn;
+  /** Tab */
+  tab?: SlotFn;
+  /** Sider */
+  sider?: SlotFn;
+  /** Footer */
+  footer?: SlotFn;
+};
+
+const slots = defineSlots<Slots>();
+
+const cssVars = computed(() => createLayoutCssVars(props));
+
+// config visible
+const showHeader = computed(() => Boolean(slots.header) && props.headerVisible);
+const showTab = computed(() => Boolean(slots.tab) && props.tabVisible);
+const showSider = computed(() => !props.isMobile && Boolean(slots.sider) && props.siderVisible);
+const showMobileSider = computed(() => props.isMobile && Boolean(slots.sider) && props.siderVisible);
+const showFooter = computed(() => Boolean(slots.footer) && props.footerVisible);
+
+// scroll mode
+const isWrapperScroll = computed(() => props.scrollMode === 'wrapper');
+const isContentScroll = computed(() => props.scrollMode === 'content');
+
+// layout direction
+const isVertical = computed(() => props.mode === 'vertical');
+const isHorizontal = computed(() => props.mode === 'horizontal');
+
+const fixedHeaderAndTab = computed(() => props.fixedTop || (isHorizontal.value && isWrapperScroll.value));
+
+// css
+const leftGapClass = computed(() => {
+  if (!props.fullContent && showSider.value) {
+    return props.siderCollapse ? style['left-gap_collapsed'] : style['left-gap'];
+  }
+
+  return '';
+});
+
+const headerLeftGapClass = computed(() => (isVertical.value ? leftGapClass.value : ''));
+
+const footerLeftGapClass = computed(() => {
+  const condition1 = isVertical.value;
+  const condition2 = isHorizontal.value && isWrapperScroll.value && !props.fixedFooter;
+  const condition3 = Boolean(isHorizontal.value && props.rightFooter);
+
+  if (condition1 || condition2 || condition3) {
+    return leftGapClass.value;
+  }
+
+  return '';
+});
+
+const siderPaddingClass = computed(() => {
+  let cls = '';
+
+  if (showHeader.value && !headerLeftGapClass.value) {
+    cls += style['sider-padding-top'];
+  }
+  if (showFooter.value && !footerLeftGapClass.value) {
+    cls += ` ${style['sider-padding-bottom']}`;
+  }
+
+  return cls;
+});
+
+function handleClickMask() {
+  emit('update:siderCollapse', true);
+}
+</script>
+
+<template>
+  <div class="relative h-full" :class="[commonClass]" :style="cssVars">
+    <div
+      :id="isWrapperScroll ? scrollElId : undefined"
+      class="h-full flex flex-col"
+      :class="[commonClass, scrollWrapperClass, { 'overflow-y-auto': isWrapperScroll }]"
+    >
+      <!-- Header -->
+      <template v-if="showHeader">
+        <header
+          v-show="!fullContent"
+          class="flex-shrink-0"
+          :class="[
+            style['layout-header'],
+            commonClass,
+            headerClass,
+            headerLeftGapClass,
+            { 'absolute top-0 left-0 w-full': fixedHeaderAndTab }
+          ]"
+        >
+          <slot name="header"></slot>
+        </header>
+        <div
+          v-show="!fullContent && fixedHeaderAndTab"
+          class="flex-shrink-0 overflow-hidden"
+          :class="[style['layout-header-placement']]"
+        ></div>
+      </template>
+
+      <!-- Tab -->
+      <template v-if="showTab">
+        <div
+          class="flex-shrink-0"
+          :class="[
+            style['layout-tab'],
+            commonClass,
+            tabClass,
+            { 'top-0!': fullContent || !showHeader },
+            leftGapClass,
+            { 'absolute left-0 w-full': fixedHeaderAndTab }
+          ]"
+        >
+          <slot name="tab"></slot>
+        </div>
+        <div
+          v-show="fullContent || fixedHeaderAndTab"
+          class="flex-shrink-0 overflow-hidden"
+          :class="[style['layout-tab-placement']]"
+        ></div>
+      </template>
+
+      <!-- Sider -->
+      <template v-if="showSider">
+        <aside
+          v-show="!fullContent"
+          class="absolute left-0 top-0 h-full"
+          :class="[
+            commonClass,
+            siderClass,
+            siderPaddingClass,
+            siderCollapse ? style['layout-sider_collapsed'] : style['layout-sider']
+          ]"
+        >
+          <slot name="sider"></slot>
+        </aside>
+      </template>
+
+      <!-- Mobile Sider -->
+      <template v-if="showMobileSider">
+        <aside
+          class="absolute left-0 top-0 h-full w-0 bg-white"
+          :class="[
+            commonClass,
+            mobileSiderClass,
+            style['layout-mobile-sider'],
+            siderCollapse ? 'overflow-hidden' : style['layout-sider']
+          ]"
+        >
+          <slot name="sider"></slot>
+        </aside>
+        <div
+          v-show="!siderCollapse"
+          class="absolute left-0 top-0 h-full w-full bg-[rgba(0,0,0,0.2)]"
+          :class="[style['layout-mobile-sider-mask']]"
+          @click="handleClickMask"
+        ></div>
+      </template>
+
+      <!-- Main Content -->
+      <main
+        :id="isContentScroll ? scrollElId : undefined"
+        class="flex flex-col flex-grow"
+        :class="[commonClass, contentClass, leftGapClass, { 'overflow-y-auto': isContentScroll }]"
+      >
+        <slot></slot>
+      </main>
+
+      <!-- Footer -->
+      <template v-if="showFooter">
+        <footer
+          v-show="!fullContent"
+          class="flex-shrink-0"
+          :class="[
+            style['layout-footer'],
+            commonClass,
+            footerClass,
+            footerLeftGapClass,
+            { 'absolute left-0 bottom-0 w-full': fixedFooter }
+          ]"
+        >
+          <slot name="footer"></slot>
+        </footer>
+        <div
+          v-show="!fullContent && fixedFooter"
+          class="flex-shrink-0 overflow-hidden"
+          :class="[style['layout-footer-placement']]"
+        ></div>
+      </template>
+    </div>
+  </div>
+</template>
+
+<style scoped></style>

+ 68 - 0
packages/materials/src/libs/admin-layout/shared.ts

@@ -0,0 +1,68 @@
+import type { AdminLayoutProps, LayoutCssVars, LayoutCssVarsProps } from '../../types';
+
+/** The id of the scroll element of the layout */
+export const LAYOUT_SCROLL_EL_ID = '__SCROLL_EL_ID__';
+
+/** The max z-index of the layout */
+export const LAYOUT_MAX_Z_INDEX = 100;
+
+/**
+ * Create layout css vars by css vars props
+ *
+ * @param props Css vars props
+ */
+function createLayoutCssVarsByCssVarsProps(props: LayoutCssVarsProps) {
+  const cssVars: LayoutCssVars = {
+    '--soy-header-height': `${props.headerHeight}px`,
+    '--soy-header-z-index': props.headerZIndex,
+    '--soy-tab-height': `${props.tabHeight}px`,
+    '--soy-tab-z-index': props.tabZIndex,
+    '--soy-sider-width': `${props.siderWidth}px`,
+    '--soy-sider-collapsed-width': `${props.siderCollapsedWidth}px`,
+    '--soy-sider-z-index': props.siderZIndex,
+    '--soy-mobile-sider-z-index': props.mobileSiderZIndex,
+    '--soy-footer-height': `${props.footerHeight}px`,
+    '--soy-footer-z-index': props.footerZIndex
+  };
+
+  return cssVars;
+}
+
+/**
+ * Create layout css vars
+ *
+ * @param props
+ */
+export function createLayoutCssVars(props: AdminLayoutProps) {
+  const {
+    mode,
+    isMobile,
+    maxZIndex = LAYOUT_MAX_Z_INDEX,
+    headerHeight,
+    tabHeight,
+    siderWidth,
+    siderCollapsedWidth,
+    footerHeight
+  } = props;
+
+  const headerZIndex = maxZIndex - 3;
+  const tabZIndex = maxZIndex - 5;
+  const siderZIndex = mode === 'vertical' || isMobile ? maxZIndex - 1 : maxZIndex - 4;
+  const mobileSiderZIndex = isMobile ? maxZIndex - 2 : 0;
+  const footerZIndex = maxZIndex - 5;
+
+  const cssProps: LayoutCssVarsProps = {
+    headerHeight,
+    headerZIndex,
+    tabHeight,
+    tabZIndex,
+    siderWidth,
+    siderZIndex,
+    mobileSiderZIndex,
+    siderCollapsedWidth,
+    footerHeight,
+    footerZIndex
+  };
+
+  return createLayoutCssVarsByCssVarsProps(cssProps);
+}

+ 3 - 0
packages/materials/src/libs/color-picker/index.ts

@@ -0,0 +1,3 @@
+import ColorPicker from './index.vue';
+
+export default ColorPicker;

+ 116 - 0
packages/materials/src/libs/color-picker/index.vue

@@ -0,0 +1,116 @@
+<script setup lang="ts">
+import { onMounted, ref, watch } from 'vue';
+import ColorPicker from '@simonwep/pickr';
+import '@simonwep/pickr/dist/themes/nano.min.css';
+
+defineOptions({
+  name: 'ColorPicker'
+});
+
+interface Props {
+  color: string;
+  palettes?: string[];
+  disabled?: boolean;
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  palettes: () => [
+    '#3b82f6',
+    '#6366f1',
+    '#8b5cf6',
+    '#a855f7',
+    '#0ea5e9',
+    '#06b6d4',
+    '#f43f5e',
+    '#ef4444',
+    '#ec4899',
+    '#d946ef',
+    '#f97316',
+    '#f59e0b',
+    '#eab308',
+    '#84cc16',
+    '#22c55e',
+    '#10b981',
+    '#14b8a6'
+  ]
+});
+
+interface Emits {
+  (e: 'update:color', value: string): void;
+}
+
+const emit = defineEmits<Emits>();
+
+const domRef = ref<HTMLElement | null>(null);
+const instance = ref<ColorPicker | null>(null);
+
+function handleColorChange(hsva: ColorPicker.HSVaColor) {
+  const color = hsva.toHEXA().toString();
+  emit('update:color', color);
+}
+
+function initColorPicker() {
+  if (!domRef.value) return;
+
+  instance.value = ColorPicker.create({
+    el: domRef.value,
+    theme: 'nano',
+    swatches: props.palettes,
+    lockOpacity: true,
+    default: props.color,
+    disabled: props.disabled,
+    components: {
+      preview: true,
+      opacity: false,
+      hue: true,
+      interaction: {
+        hex: true,
+        rgba: true,
+        input: true
+      }
+    }
+  });
+
+  instance.value.on('change', handleColorChange);
+}
+
+function updateColor(color: string) {
+  if (!instance.value) return;
+
+  instance.value.setColor(color);
+}
+
+function updateDisabled(disabled: boolean) {
+  if (!instance.value) return;
+
+  if (disabled) {
+    instance.value.disable();
+  } else {
+    instance.value.enable();
+  }
+}
+
+watch(
+  () => props.color,
+  value => {
+    updateColor(value);
+  }
+);
+
+watch(
+  () => props.disabled,
+  value => {
+    updateDisabled(value);
+  }
+);
+
+onMounted(() => {
+  initColorPicker();
+});
+</script>
+
+<template>
+  <div ref="domRef"></div>
+</template>
+
+<style scoped></style>

+ 53 - 0
packages/materials/src/libs/page-tab/button-tab.vue

@@ -0,0 +1,53 @@
+<script setup lang="ts">
+import type { PageTabProps } from '../../types';
+import style from './index.module.css';
+
+defineOptions({
+  name: 'ButtonTab'
+});
+
+defineProps<PageTabProps>();
+
+type SlotFn = (props?: Record<string, unknown>) => any;
+
+type Slots = {
+  /**
+   * Slot
+   *
+   * The center content of the tab
+   */
+  default?: SlotFn;
+  /**
+   * Slot
+   *
+   * The left content of the tab
+   */
+  prefix?: SlotFn;
+  /**
+   * Slot
+   *
+   * The right content of the tab
+   */
+  suffix?: SlotFn;
+};
+
+defineSlots<Slots>();
+</script>
+
+<template>
+  <div
+    class=":soy: relative inline-flex cursor-pointer items-center justify-center gap-12px whitespace-nowrap border-(1px solid) rounded-4px px-12px py-4px"
+    :class="[
+      style['button-tab'],
+      { [style['button-tab_dark']]: darkMode },
+      { [style['button-tab_active']]: active },
+      { [style['button-tab_active_dark']]: active && darkMode }
+    ]"
+  >
+    <slot name="prefix"></slot>
+    <slot></slot>
+    <slot name="suffix"></slot>
+  </div>
+</template>
+
+<style scoped></style>

+ 31 - 0
packages/materials/src/libs/page-tab/chrome-tab-bg.vue

@@ -0,0 +1,31 @@
+<script setup lang="ts">
+defineOptions({
+  name: 'ChromeTabBg'
+});
+</script>
+
+<template>
+  <svg class="size-full">
+    <defs>
+      <symbol id="geometry-left" viewBox="0 0 214 36">
+        <path d="M17 0h197v36H0v-2c4.5 0 9-3.5 9-8V8c0-4.5 3.5-8 8-8z" />
+      </symbol>
+      <symbol id="geometry-right" viewBox="0 0 214 36">
+        <use xlink:href="#geometry-left" />
+      </symbol>
+      <clipPath>
+        <rect width="100%" height="100%" x="0" />
+      </clipPath>
+    </defs>
+    <svg width="51%" height="100%">
+      <use xlink:href="#geometry-left" width="214" height="36" fill="currentColor" />
+    </svg>
+    <g transform="scale(-1, 1)">
+      <svg width="51%" height="100%" x="-100%" y="0">
+        <use xlink:href="#geometry-right" width="214" height="36" fill="currentColor" />
+      </svg>
+    </g>
+  </svg>
+</template>
+
+<style scoped></style>

+ 58 - 0
packages/materials/src/libs/page-tab/chrome-tab.vue

@@ -0,0 +1,58 @@
+<script setup lang="ts">
+import type { PageTabProps } from '../../types';
+import ChromeTabBg from './chrome-tab-bg.vue';
+import style from './index.module.css';
+
+defineOptions({
+  name: 'ChromeTab'
+});
+
+defineProps<PageTabProps>();
+
+type SlotFn = (props?: Record<string, unknown>) => any;
+
+type Slots = {
+  /**
+   * Slot
+   *
+   * The center content of the tab
+   */
+  default?: SlotFn;
+  /**
+   * Slot
+   *
+   * The left content of the tab
+   */
+  prefix?: SlotFn;
+  /**
+   * Slot
+   *
+   * The right content of the tab
+   */
+  suffix?: SlotFn;
+};
+
+defineSlots<Slots>();
+</script>
+
+<template>
+  <div
+    class=":soy: relative inline-flex cursor-pointer items-center justify-center gap-16px whitespace-nowrap px-24px py-6px -mr-18px"
+    :class="[
+      style['chrome-tab'],
+      { [style['chrome-tab_dark']]: darkMode },
+      { [style['chrome-tab_active']]: active },
+      { [style['chrome-tab_active_dark']]: active && darkMode }
+    ]"
+  >
+    <div class=":soy: pointer-events-none absolute left-0 top-0 h-full w-full -z-1" :class="[style['chrome-tab__bg']]">
+      <ChromeTabBg />
+    </div>
+    <slot name="prefix"></slot>
+    <slot></slot>
+    <slot name="suffix"></slot>
+    <div class=":soy: absolute right-7px h-16px w-1px bg-#1f2225" :class="[style['chrome-tab-divider']]"></div>
+  </div>
+</template>
+
+<style scoped></style>

+ 97 - 0
packages/materials/src/libs/page-tab/index.module.css

@@ -0,0 +1,97 @@
+/* @type */
+
+.button-tab {
+  border-color: #e5e7eb;
+}
+
+.button-tab_dark {
+  border-color: #ffffff3d;
+}
+
+.button-tab:hover {
+  color: var(--soy-primary-color);
+  border-color: var(--soy-primary-color-opacity3);
+}
+
+.button-tab_active {
+  color: var(--soy-primary-color);
+  border-color: var(--soy-primary-color-opacity3);
+  background-color: var(--soy-primary-color-opacity1);
+}
+
+.button-tab_active_dark {
+  background-color: var(--soy-primary-color-opacity2);
+}
+
+.button-tab .svg-close:hover {
+  font-size: 12px;
+  color: #ffffff;
+  background-color: var(--soy-primary-color);
+}
+
+.button-tab_dark .svg-close:hover {
+  color: #000000;
+}
+
+.chrome-tab:hover {
+  z-index: 9;
+}
+
+.chrome-tab_active {
+  z-index: 10;
+  color: var(--soy-primary-color);
+}
+
+.chrome-tab__bg {
+  color: transparent;
+}
+
+.chrome-tab_active .chrome-tab__bg {
+  color: var(--soy-primary-color1);
+}
+
+.chrome-tab_active_dark .chrome-tab__bg {
+  color: var(--soy-primary-color2);
+}
+
+.chrome-tab:hover .chrome-tab__bg {
+  color: #dee1e6;
+}
+
+.chrome-tab_active:hover .chrome-tab__bg {
+  color: var(--soy-primary-color1);
+}
+
+.chrome-tab_dark:hover .chrome-tab__bg {
+  color: #333333;
+}
+
+.chrome-tab_active_dark:hover .chrome-tab__bg {
+  color: var(--soy-primary-color2);
+}
+
+.chrome-tab .svg-close:hover {
+  font-size: 12px;
+  color: #ffffff;
+  background-color: #9ca3af;
+}
+
+.chrome-tab_active .svg-close:hover {
+  background-color: var(--soy-primary-color);
+}
+
+.chrome-tab_dark .svg-close:hover {
+  color: #000000;
+}
+
+.chrome-tab_active .chrome-tab-divider {
+  opacity: 0;
+}
+
+.chrome-tab:hover .chrome-tab-divider {
+  opacity: 0;
+}
+
+.chrome-tab_dark .chrome-tab-divider {
+  background-color: rgba(255, 255, 255, 0.9);
+}

+ 14 - 0
packages/materials/src/libs/page-tab/index.module.css.d.ts

@@ -0,0 +1,14 @@
+declare const styles: {
+  readonly 'button-tab': string;
+  readonly 'button-tab_dark': string;
+  readonly 'button-tab_active': string;
+  readonly 'button-tab_active_dark': string;
+  readonly 'chrome-tab': string;
+  readonly 'chrome-tab_active': string;
+  readonly 'chrome-tab__bg': string;
+  readonly 'chrome-tab_active_dark': string;
+  readonly 'chrome-tab_dark': string;
+  readonly 'chrome-tab-divider': string;
+  readonly 'svg-close': string;
+};
+export default styles;

+ 3 - 0
packages/materials/src/libs/page-tab/index.ts

@@ -0,0 +1,3 @@
+import PageTab from './index.vue';
+
+export default PageTab;

+ 85 - 0
packages/materials/src/libs/page-tab/index.vue

@@ -0,0 +1,85 @@
+<script setup lang="ts">
+import { computed } from 'vue';
+import type { Component } from 'vue';
+import type { PageTabMode, PageTabProps } from '../../types';
+import { ACTIVE_COLOR, createTabCssVars } from './shared';
+import ChromeTab from './chrome-tab.vue';
+import ButtonTab from './button-tab.vue';
+import SvgClose from './svg-close.vue';
+import style from './index.module.css';
+
+defineOptions({
+  name: 'PageTab'
+});
+
+const props = withDefaults(defineProps<PageTabProps>(), {
+  mode: 'chrome',
+  commonClass: 'transition-all-300',
+  activeColor: ACTIVE_COLOR,
+  closable: true
+});
+
+interface Emits {
+  (e: 'close'): void;
+}
+
+const emit = defineEmits<Emits>();
+
+const activeTabComponent = computed(() => {
+  const { mode, chromeClass, buttonClass } = props;
+
+  const tabComponentMap = {
+    chrome: {
+      component: ChromeTab,
+      class: chromeClass
+    },
+    button: {
+      component: ButtonTab,
+      class: buttonClass
+    }
+  } satisfies Record<PageTabMode, { component: Component; class?: string }>;
+
+  return tabComponentMap[mode];
+});
+
+const cssVars = computed(() => createTabCssVars(props.activeColor));
+
+const bindProps = computed(() => {
+  const { chromeClass: _chromeCls, buttonClass: _btnCls, ...rest } = props;
+
+  return rest;
+});
+
+function handleClose() {
+  emit('close');
+}
+
+function handleMouseup(e: MouseEvent) {
+  // close tab by mouse wheel button click
+  if (e.button === 1) {
+    handleClose();
+  }
+}
+</script>
+
+<template>
+  <component
+    :is="activeTabComponent.component"
+    :class="activeTabComponent.class"
+    :style="cssVars"
+    v-bind="bindProps"
+    @mouseup="handleMouseup"
+  >
+    <template #prefix>
+      <slot name="prefix"></slot>
+    </template>
+    <slot></slot>
+    <template #suffix>
+      <slot name="suffix">
+        <SvgClose v-if="closable" :class="[style['svg-close']]" @click="handleClose" />
+      </slot>
+    </template>
+  </component>
+</template>
+
+<style scoped></style>

+ 31 - 0
packages/materials/src/libs/page-tab/shared.ts

@@ -0,0 +1,31 @@
+import { addColorAlpha, transformColorWithOpacity } from '@sa/utils';
+import type { PageTabCssVars, PageTabCssVarsProps } from '../../types';
+
+/** The active color of the tab */
+export const ACTIVE_COLOR = '#1890ff';
+
+function createCssVars(props: PageTabCssVarsProps) {
+  const cssVars: PageTabCssVars = {
+    '--soy-primary-color': props.primaryColor,
+    '--soy-primary-color1': props.primaryColor1,
+    '--soy-primary-color2': props.primaryColor2,
+    '--soy-primary-color-opacity1': props.primaryColorOpacity1,
+    '--soy-primary-color-opacity2': props.primaryColorOpacity2,
+    '--soy-primary-color-opacity3': props.primaryColorOpacity3
+  };
+
+  return cssVars;
+}
+
+export function createTabCssVars(primaryColor: string) {
+  const cssProps: PageTabCssVarsProps = {
+    primaryColor,
+    primaryColor1: transformColorWithOpacity(primaryColor, 0.1, '#ffffff'),
+    primaryColor2: transformColorWithOpacity(primaryColor, 0.3, '#000000'),
+    primaryColorOpacity1: addColorAlpha(primaryColor, 0.1),
+    primaryColorOpacity2: addColorAlpha(primaryColor, 0.15),
+    primaryColorOpacity3: addColorAlpha(primaryColor, 0.3)
+  };
+
+  return createCssVars(cssProps);
+}

+ 31 - 0
packages/materials/src/libs/page-tab/svg-close.vue

@@ -0,0 +1,31 @@
+<script setup lang="ts">
+defineOptions({
+  name: 'SvgClose'
+});
+
+const emit = defineEmits<Emits>();
+
+interface Emits {
+  (e: 'click'): void;
+}
+
+function handleClick() {
+  emit('click');
+}
+</script>
+
+<template>
+  <div
+    class=":soy: relative h-16px w-16px inline-flex items-center justify-center rd-50% text-14px"
+    @click.stop="handleClick"
+  >
+    <svg width="1em" height="1em" viewBox="0 0 1024 1024">
+      <path
+        fill="currentColor"
+        d="m563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8L295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512L196.9 824.9A7.95 7.95 0 0 0 203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1l216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"
+      />
+    </svg>
+  </div>
+</template>
+
+<style scoped></style>

+ 3 - 0
packages/materials/src/libs/simple-scrollbar/index.ts

@@ -0,0 +1,3 @@
+import SimpleScrollbar from './index.vue';
+
+export default SimpleScrollbar;

+ 18 - 0
packages/materials/src/libs/simple-scrollbar/index.vue

@@ -0,0 +1,18 @@
+<script setup lang="ts">
+import Simplebar from 'simplebar-vue';
+import 'simplebar-vue/dist/simplebar.min.css';
+
+defineOptions({
+  name: 'SimpleScrollbar'
+});
+</script>
+
+<template>
+  <div class="h-full flex-1-hidden">
+    <Simplebar class="h-full">
+      <slot />
+    </Simplebar>
+  </div>
+</template>
+
+<style scoped></style>

+ 294 - 0
packages/materials/src/types/index.ts

@@ -0,0 +1,294 @@
+/** Header config */
+interface AdminLayoutHeaderConfig {
+  /**
+   * Whether header is visible
+   *
+   * @default true
+   */
+  headerVisible?: boolean;
+  /**
+   * Header class
+   *
+   * @default ''
+   */
+  headerClass?: string;
+  /**
+   * Header height
+   *
+   * @default 56px
+   */
+  headerHeight?: number;
+}
+
+/** Tab config */
+interface AdminLayoutTabConfig {
+  /**
+   * Whether tab is visible
+   *
+   * @default true
+   */
+  tabVisible?: boolean;
+  /**
+   * Tab class
+   *
+   * @default ''
+   */
+  tabClass?: string;
+  /**
+   * Tab height
+   *
+   * @default 48px
+   */
+  tabHeight?: number;
+}
+
+/** Sider config */
+interface AdminLayoutSiderConfig {
+  /**
+   * Whether sider is visible
+   *
+   * @default true
+   */
+  siderVisible?: boolean;
+  /**
+   * Sider class
+   *
+   * @default ''
+   */
+  siderClass?: string;
+  /**
+   * Mobile sider class
+   *
+   * @default ''
+   */
+  mobileSiderClass?: string;
+  /**
+   * Sider collapse status
+   *
+   * @default false
+   */
+  siderCollapse?: boolean;
+  /**
+   * Sider width when collapse is false
+   *
+   * @default '220px'
+   */
+  siderWidth?: number;
+  /**
+   * Sider width when collapse is true
+   *
+   * @default '64px'
+   */
+  siderCollapsedWidth?: number;
+}
+
+/** Content config */
+export interface AdminLayoutContentConfig {
+  /**
+   * Content class
+   *
+   * @default ''
+   */
+  contentClass?: string;
+  /**
+   * Whether content is full the page
+   *
+   * If true, other elements will be hidden by `display: none`
+   */
+  fullContent?: boolean;
+}
+
+/** Footer config */
+export interface AdminLayoutFooterConfig {
+  /**
+   * Whether footer is visible
+   *
+   * @default true
+   */
+  footerVisible?: boolean;
+  /**
+   * Whether footer is fixed
+   *
+   * @default true
+   */
+  fixedFooter?: boolean;
+  /**
+   * Footer class
+   *
+   * @default ''
+   */
+  footerClass?: string;
+  /**
+   * Footer height
+   *
+   * @default 48px
+   */
+  footerHeight?: number;
+  /**
+   * Whether footer is on the right side
+   *
+   * When the layout is vertical, the footer is on the right side
+   */
+  rightFooter?: boolean;
+}
+
+/**
+ * Layout mode
+ *
+ * - Horizontal
+ * - Vertical
+ */
+export type LayoutMode = 'horizontal' | 'vertical';
+
+/**
+ * The scroll mode when content overflow
+ *
+ * - Wrapper: the layout component's wrapper element has a scrollbar
+ * - Content: the layout component's content element has a scrollbar
+ *
+ * @default 'wrapper'
+ */
+export type LayoutScrollMode = 'wrapper' | 'content';
+
+/** Admin layout props */
+export interface AdminLayoutProps
+  extends AdminLayoutHeaderConfig,
+    AdminLayoutTabConfig,
+    AdminLayoutSiderConfig,
+    AdminLayoutContentConfig,
+    AdminLayoutFooterConfig {
+  /**
+   * Layout mode
+   *
+   * - {@link LayoutMode}
+   */
+  mode?: LayoutMode;
+  /** Is mobile layout */
+  isMobile?: boolean;
+  /**
+   * Scroll mode
+   *
+   * - {@link ScrollMode}
+   */
+  scrollMode?: LayoutScrollMode;
+  /**
+   * The id of the scroll element of the layout
+   *
+   * It can be used to get the corresponding Dom and scroll it
+   *
+   * @example
+   *   use the default id by import
+   *   ```ts
+   *   import { adminLayoutScrollElId } from '@sa/vue-materials';
+   *   ```
+   *
+   * @default
+   * ```ts
+   * const adminLayoutScrollElId = '__ADMIN_LAYOUT_SCROLL_EL_ID__'
+   * ```
+   */
+  scrollElId?: string;
+  /** The class of the scroll element */
+  scrollElClass?: string;
+  /** The class of the scroll wrapper element */
+  scrollWrapperClass?: string;
+  /**
+   * The common class of the layout
+   *
+   * Is can be used to configure the transition animation
+   *
+   * @default 'transition-all-300'
+   */
+  commonClass?: string;
+  /**
+   * Whether fix the header and tab
+   *
+   * @default true
+   */
+  fixedTop?: boolean;
+  /**
+   * The max z-index of the layout
+   *
+   * The z-index of Header,Tab,Sider and Footer will not exceed this value
+   */
+  maxZIndex?: number;
+}
+
+type Kebab<S extends string> = S extends Uncapitalize<S> ? S : `-${Uncapitalize<S>}`;
+
+type KebabCase<S extends string> = S extends `${infer Start}${infer End}`
+  ? `${Uncapitalize<Start>}${KebabCase<Kebab<End>>}`
+  : S;
+
+type Prefix = '--soy-';
+
+export type LayoutCssVarsProps = Pick<
+  AdminLayoutProps,
+  'headerHeight' | 'tabHeight' | 'siderWidth' | 'siderCollapsedWidth' | 'footerHeight'
+> & {
+  headerZIndex?: number;
+  tabZIndex?: number;
+  siderZIndex?: number;
+  mobileSiderZIndex?: number;
+  footerZIndex?: number;
+};
+
+export type LayoutCssVars = {
+  [K in keyof LayoutCssVarsProps as `${Prefix}${KebabCase<K>}`]: string | number;
+};
+
+/**
+ * The mode of the tab
+ *
+ * - Button: button style
+ * - Chrome: chrome style
+ *
+ * @default chrome
+ */
+export type PageTabMode = 'button' | 'chrome';
+
+export interface PageTabProps {
+  /** Whether is dark mode */
+  darkMode?: boolean;
+  /**
+   * The mode of the tab
+   *
+   * - {@link TabMode}
+   */
+  mode?: PageTabMode;
+  /**
+   * The common class of the layout
+   *
+   * Is can be used to configure the transition animation
+   *
+   * @default 'transition-all-300'
+   */
+  commonClass?: string;
+  /** The class of the button tab */
+  buttonClass?: string;
+  /** The class of the chrome tab */
+  chromeClass?: string;
+  /** Whether the tab is active */
+  active?: boolean;
+  /** The color of the active tab */
+  activeColor?: string;
+  /**
+   * Whether the tab is closable
+   *
+   * Show the close icon when true
+   */
+  closable?: boolean;
+}
+
+export type PageTabCssVarsProps = {
+  primaryColor: string;
+  primaryColor1: string;
+  primaryColor2: string;
+  primaryColorOpacity1: string;
+  primaryColorOpacity2: string;
+  primaryColorOpacity3: string;
+};
+
+export type PageTabCssVars = {
+  [K in keyof PageTabCssVarsProps as `${Prefix}${KebabCase<K>}`]: string | number;
+};

+ 20 - 0
packages/materials/tsconfig.json

@@ -0,0 +1,20 @@
+{
+  "compilerOptions": {
+    "target": "ESNext",
+    "jsx": "preserve",
+    "lib": ["DOM", "ESNext"],
+    "baseUrl": ".",
+    "module": "ESNext",
+    "moduleResolution": "node",
+    "resolveJsonModule": true,
+    "types": ["node"],
+    "strict": true,
+    "strictNullChecks": true,
+    "noUnusedLocals": true,
+    "allowSyntheticDefaultImports": true,
+    "esModuleInterop": true,
+    "forceConsistentCasingInFileNames": true
+  },
+  "include": ["src/**/*"],
+  "exclude": ["node_modules", "dist"]
+}

+ 15 - 0
packages/ofetch/package.json

@@ -0,0 +1,15 @@
+{
+  "name": "@sa/fetch",
+  "version": "1.2.6",
+  "exports": {
+    ".": "./src/index.ts"
+  },
+  "typesVersions": {
+    "*": {
+      "*": ["./src/*"]
+    }
+  },
+  "dependencies": {
+    "ofetch": "1.3.4"
+  }
+}

+ 10 - 0
packages/ofetch/src/index.ts

@@ -0,0 +1,10 @@
+import { ofetch } from 'ofetch';
+import type { FetchOptions } from 'ofetch';
+
+export function createRequest(options: FetchOptions) {
+  const request = ofetch.create(options);
+
+  return request;
+}
+
+export default createRequest;

+ 20 - 0
packages/ofetch/tsconfig.json

@@ -0,0 +1,20 @@
+{
+  "compilerOptions": {
+    "target": "ESNext",
+    "jsx": "preserve",
+    "lib": ["DOM", "ESNext"],
+    "baseUrl": ".",
+    "module": "ESNext",
+    "moduleResolution": "node",
+    "resolveJsonModule": true,
+    "types": ["node"],
+    "strict": true,
+    "strictNullChecks": true,
+    "noUnusedLocals": true,
+    "allowSyntheticDefaultImports": true,
+    "esModuleInterop": true,
+    "forceConsistentCasingInFileNames": true
+  },
+  "include": ["src/**/*"],
+  "exclude": ["node_modules", "dist"]
+}

+ 3 - 0
packages/scripts/bin.ts

@@ -0,0 +1,3 @@
+#!/usr/bin/env tsx
+
+import './src/index.ts';

+ 27 - 0
packages/scripts/package.json

@@ -0,0 +1,27 @@
+{
+  "name": "@sa/scripts",
+  "version": "1.2.6",
+  "bin": {
+    "sa": "./bin.ts"
+  },
+  "exports": {
+    ".": "./src/index.ts"
+  },
+  "typesVersions": {
+    "*": {
+      "*": ["./src/*"]
+    }
+  },
+  "devDependencies": {
+    "@soybeanjs/changelog": "0.3.24",
+    "bumpp": "9.4.1",
+    "c12": "1.11.1",
+    "cac": "6.7.14",
+    "consola": "3.2.3",
+    "enquirer": "2.4.1",
+    "execa": "9.2.0",
+    "kolorist": "1.8.0",
+    "npm-check-updates": "16.14.20",
+    "rimraf": "5.0.7"
+  }
+}

+ 10 - 0
packages/scripts/src/commands/changelog.ts

@@ -0,0 +1,10 @@
+import { generateChangelog, generateTotalChangelog } from '@soybeanjs/changelog';
+import type { ChangelogOption } from '@soybeanjs/changelog';
+
+export async function genChangelog(options?: Partial<ChangelogOption>, total = false) {
+  if (total) {
+    await generateTotalChangelog(options);
+  } else {
+    await generateChangelog(options);
+  }
+}

+ 5 - 0
packages/scripts/src/commands/cleanup.ts

@@ -0,0 +1,5 @@
+import { rimraf } from 'rimraf';
+
+export async function cleanup(paths: string[]) {
+  await rimraf(paths, { glob: true });
+}

+ 86 - 0
packages/scripts/src/commands/git-commit.ts

@@ -0,0 +1,86 @@
+import path from 'node:path';
+import { readFileSync } from 'node:fs';
+import { prompt } from 'enquirer';
+import { bgRed, green, red, yellow } from 'kolorist';
+import { execCommand } from '../shared';
+import type { CliOption } from '../types';
+
+interface PromptObject {
+  types: string;
+  scopes: string;
+  description: string;
+}
+
+/**
+ * Git commit with Conventional Commits standard
+ *
+ * @param gitCommitTypes
+ * @param gitCommitScopes
+ */
+export async function gitCommit(
+  gitCommitTypes: CliOption['gitCommitTypes'],
+  gitCommitScopes: CliOption['gitCommitScopes']
+) {
+  const typesChoices = gitCommitTypes.map(([value, msg]) => {
+    const nameWithSuffix = `${value}:`;
+
+    const message = `${nameWithSuffix.padEnd(12)}${msg}`;
+
+    return {
+      name: value,
+      message
+    };
+  });
+
+  const scopesChoices = gitCommitScopes.map(([value, msg]) => ({
+    name: value,
+    message: `${value.padEnd(30)} (${msg})`
+  }));
+
+  const result = await prompt<PromptObject>([
+    {
+      name: 'types',
+      type: 'select',
+      message: 'Please select a type',
+      choices: typesChoices
+    },
+    {
+      name: 'scopes',
+      type: 'select',
+      message: 'Please select a scope',
+      choices: scopesChoices
+    },
+    {
+      name: 'description',
+      type: 'text',
+      message: `Please enter a description (add prefix ${yellow('!')} to indicate breaking change)`
+    }
+  ]);
+
+  const breaking = result.description.startsWith('!') ? '!' : '';
+
+  const description = result.description.replace(/^!/, '').trim();
+
+  const commitMsg = `${result.types}(${result.scopes})${breaking}: ${description}`;
+
+  await execCommand('git', ['commit', '-m', commitMsg], { stdio: 'inherit' });
+}
+
+/** Git commit message verify */
+export async function gitCommitVerify() {
+  const gitPath = await execCommand('git', ['rev-parse', '--show-toplevel']);
+
+  const gitMsgPath = path.join(gitPath, '.git', 'COMMIT_EDITMSG');
+
+  const commitMsg = readFileSync(gitMsgPath, 'utf8').trim();
+
+  const REG_EXP = /(?<type>[a-z]+)(?:\((?<scope>.+)\))?(?<breaking>!)?: (?<description>.+)/i;
+
+  if (!REG_EXP.test(commitMsg)) {
+    throw new Error(
+      `${bgRed(' ERROR ')} ${red('git commit message must match the Conventional Commits standard!')}\n\n${green(
+        'Recommended to use the command `pnpm commit` to generate Conventional Commits compliant commit information.\nGet more info about Conventional Commits, follow this link: https://conventionalcommits.org'
+      )}`
+    );
+  }
+}

+ 6 - 0
packages/scripts/src/commands/index.ts

@@ -0,0 +1,6 @@
+export * from './git-commit';
+export * from './cleanup';
+export * from './update-pkg';
+export * from './changelog';
+export * from './release';
+export * from './router';

+ 12 - 0
packages/scripts/src/commands/release.ts

@@ -0,0 +1,12 @@
+import { versionBump } from 'bumpp';
+
+export async function release(execute = 'pnpm sa changelog', push = true) {
+  await versionBump({
+    files: ['**/package.json', '!**/node_modules'],
+    execute,
+    all: true,
+    tag: true,
+    commit: 'chore(projects): release v%s',
+    push
+  });
+}

+ 90 - 0
packages/scripts/src/commands/router.ts

@@ -0,0 +1,90 @@
+import process from 'node:process';
+import path from 'node:path';
+import { writeFile } from 'node:fs/promises';
+import { existsSync, mkdirSync } from 'node:fs';
+import { prompt } from 'enquirer';
+import { green, red } from 'kolorist';
+
+interface PromptObject {
+  routeName: string;
+  addRouteParams: boolean;
+  routeParams: string;
+}
+
+/** generate route */
+export async function generateRoute() {
+  const result = await prompt<PromptObject>([
+    {
+      name: 'routeName',
+      type: 'text',
+      message: 'please enter route name',
+      initial: 'demo-route_child'
+    },
+    {
+      name: 'addRouteParams',
+      type: 'confirm',
+      message: 'add route params?',
+      initial: false
+    }
+  ]);
+
+  if (result.addRouteParams) {
+    const answers = await prompt<PromptObject>({
+      name: 'routeParams',
+      type: 'text',
+      message: 'please enter route params',
+      initial: 'id'
+    });
+
+    Object.assign(result, answers);
+  }
+
+  const PAGE_DIR_NAME_PATTERN = /^[\w-]+[0-9a-zA-Z]+$/;
+
+  if (!PAGE_DIR_NAME_PATTERN.test(result.routeName)) {
+    throw new Error(`${red('route name is invalid, it only allow letters, numbers, "-" or "_"')}.
+For example:
+(1) one level route: ${green('demo-route')}
+(2) two level route: ${green('demo-route_child')}
+(3) multi level route: ${green('demo-route_child_child')}
+(4) group route: ${green('_ignore_demo-route')}'
+`);
+  }
+
+  const PARAM_REG = /^\w+$/g;
+
+  if (result.routeParams && !PARAM_REG.test(result.routeParams)) {
+    throw new Error(red('route params is invalid, it only allow letters, numbers or "_".'));
+  }
+
+  const cwd = process.cwd();
+
+  const [dir, ...rest] = result.routeName.split('_') as string[];
+
+  let routeDir = path.join(cwd, 'src', 'views', dir);
+
+  if (rest.length) {
+    routeDir = path.join(routeDir, rest.join('_'));
+  }
+
+  if (!existsSync(routeDir)) {
+    mkdirSync(routeDir, { recursive: true });
+  } else {
+    throw new Error(red('route already exists'));
+  }
+
+  const fileName = result.routeParams ? `[${result.routeParams}].vue` : 'index.vue';
+
+  const vueTemplate = `<script setup lang="ts"></script>
+
+<template>
+  <div>${result.routeName}</div>
+</template>
+
+<style scoped></style>
+`;
+
+  const filePath = path.join(routeDir, fileName);
+
+  await writeFile(filePath, vueTemplate);
+}

+ 5 - 0
packages/scripts/src/commands/update-pkg.ts

@@ -0,0 +1,5 @@
+import { execCommand } from '../shared';
+
+export async function updatePkg(args: string[] = ['--deep', '-u']) {
+  execCommand('npx', ['ncu', ...args], { stdio: 'inherit' });
+}

+ 55 - 0
packages/scripts/src/config/index.ts

@@ -0,0 +1,55 @@
+import process from 'node:process';
+import { loadConfig } from 'c12';
+import type { CliOption } from '../types';
+
+const defaultOptions: CliOption = {
+  cwd: process.cwd(),
+  cleanupDirs: [
+    '**/dist',
+    '**/package-lock.json',
+    '**/yarn.lock',
+    '**/pnpm-lock.yaml',
+    '**/node_modules',
+    '!node_modules/**'
+  ],
+  gitCommitTypes: [
+    ['feat', 'A new feature'],
+    ['fix', 'A bug fix'],
+    ['docs', 'Documentation only changes'],
+    ['style', 'Changes that do not affect the meaning of the code'],
+    ['refactor', 'A code change that neither fixes a bug nor adds a feature'],
+    ['perf', 'A code change that improves performance'],
+    ['optimize', 'A code change that optimizes code quality'],
+    ['test', 'Adding missing tests or correcting existing tests'],
+    ['build', 'Changes that affect the build system or external dependencies'],
+    ['ci', 'Changes to our CI configuration files and scripts'],
+    ['chore', "Other changes that don't modify src or test files"],
+    ['revert', 'Reverts a previous commit']
+  ],
+  gitCommitScopes: [
+    ['projects', 'project'],
+    ['packages', 'packages'],
+    ['components', 'components'],
+    ['hooks', 'hook functions'],
+    ['utils', 'utils functions'],
+    ['types', 'TS declaration'],
+    ['styles', 'style'],
+    ['deps', 'project dependencies'],
+    ['release', 'release project'],
+    ['other', 'other changes']
+  ],
+  ncuCommandArgs: ['--deep', '-u'],
+  changelogOptions: {}
+};
+
+export async function loadCliOptions(overrides?: Partial<CliOption>, cwd = process.cwd()) {
+  const { config } = await loadConfig<Partial<CliOption>>({
+    name: 'soybean',
+    defaults: defaultOptions,
+    overrides,
+    cwd,
+    packageJson: true
+  });
+
+  return config as CliOption;
+}

+ 101 - 0
packages/scripts/src/index.ts

@@ -0,0 +1,101 @@
+import cac from 'cac';
+import { blue, lightGreen } from 'kolorist';
+import { version } from '../package.json';
+import { cleanup, genChangelog, generateRoute, gitCommit, gitCommitVerify, release, updatePkg } from './commands';
+import { loadCliOptions } from './config';
+
+type Command = 'cleanup' | 'update-pkg' | 'git-commit' | 'git-commit-verify' | 'changelog' | 'release' | 'gen-route';
+
+type CommandAction<A extends object> = (args?: A) => Promise<void> | void;
+
+type CommandWithAction<A extends object = object> = Record<Command, { desc: string; action: CommandAction<A> }>;
+
+interface CommandArg {
+  /** Execute additional command after bumping and before git commit. Defaults to 'pnpm sa changelog' */
+  execute?: string;
+  /** Indicates whether to push the git commit and tag. Defaults to true */
+  push?: boolean;
+  /** Generate changelog by total tags */
+  total?: boolean;
+  /**
+   * The glob pattern of dirs to cleanup
+   *
+   * If not set, it will use the default value
+   *
+   * Multiple values use "," to separate them
+   */
+  cleanupDir?: string;
+}
+
+export async function setupCli() {
+  const cliOptions = await loadCliOptions();
+
+  const cli = cac(blue('soybean-admin'));
+
+  cli
+    .version(lightGreen(version))
+    .option(
+      '-e, --execute [command]',
+      "Execute additional command after bumping and before git commit. Defaults to 'npx soy changelog'"
+    )
+    .option('-p, --push', 'Indicates whether to push the git commit and tag')
+    .option('-t, --total', 'Generate changelog by total tags')
+    .option(
+      '-c, --cleanupDir <dir>',
+      'The glob pattern of dirs to cleanup, If not set, it will use the default value, Multiple values use "," to separate them'
+    )
+    .help();
+
+  const commands: CommandWithAction<CommandArg> = {
+    cleanup: {
+      desc: 'delete dirs: node_modules, dist, etc.',
+      action: async () => {
+        await cleanup(cliOptions.cleanupDirs);
+      }
+    },
+    'update-pkg': {
+      desc: 'update package.json dependencies versions',
+      action: async () => {
+        await updatePkg(cliOptions.ncuCommandArgs);
+      }
+    },
+    'git-commit': {
+      desc: 'git commit, generate commit message which match Conventional Commits standard',
+      action: async () => {
+        await gitCommit(cliOptions.gitCommitTypes, cliOptions.gitCommitScopes);
+      }
+    },
+    'git-commit-verify': {
+      desc: 'verify git commit message, make sure it match Conventional Commits standard',
+      action: async () => {
+        await gitCommitVerify();
+      }
+    },
+    changelog: {
+      desc: 'generate changelog',
+      action: async args => {
+        await genChangelog(cliOptions.changelogOptions, args?.total);
+      }
+    },
+    release: {
+      desc: 'release: update version, generate changelog, commit code',
+      action: async args => {
+        await release(args?.execute, args?.push);
+      }
+    },
+    'gen-route': {
+      desc: 'generate route',
+      action: async () => {
+        await generateRoute();
+      }
+    }
+  };
+
+  for (const [command, { desc, action }] of Object.entries(commands)) {
+    cli.command(command, lightGreen(desc)).action(action);
+  }
+
+  cli.parse();
+}
+
+setupCli();

+ 7 - 0
packages/scripts/src/shared/index.ts

@@ -0,0 +1,7 @@
+import type { Options } from 'execa';
+
+export async function execCommand(cmd: string, args: string[], options?: Options) {
+  const { execa } = await import('execa');
+  const res = await execa(cmd, args, options);
+  return (res?.stdout as string)?.trim() || '';
+}

Some files were not shown because too many files changed in this diff