feat: chat presets (experimental)

This commit is contained in:
josc146 2023-06-25 00:07:14 +08:00
parent 08cf09416a
commit db67f30082
15 changed files with 987 additions and 212 deletions

1
.gitignore vendored
View File

@ -8,6 +8,7 @@ __pycache__
*.bin
/config.json
/cache.json
/presets.json
/frontend/stats.html
/frontend/package.json.md5
/py310

View File

@ -18,6 +18,7 @@
"mobx": "^6.9.0",
"mobx-react-lite": "^3.4.3",
"react": "^18.2.0",
"react-beautiful-dnd": "^13.1.1",
"react-dom": "^18.2.0",
"react-i18next": "^12.2.2",
"react-markdown": "^8.0.7",
@ -33,6 +34,7 @@
},
"devDependencies": {
"@types/react": "^18.2.6",
"@types/react-beautiful-dnd": "^13.1.4",
"@types/react-dom": "^18.2.4",
"@types/uuid": "^9.0.1",
"@vitejs/plugin-react": "^4.0.0",
@ -56,7 +58,7 @@
},
"node_modules/@ampproject/remapping": {
"version": "2.2.1",
"resolved": "https://registry.npmmirror.com/@ampproject/remapping/-/remapping-2.2.1.tgz",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
"integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==",
"dev": true,
"dependencies": {
@ -68,42 +70,42 @@
}
},
"node_modules/@babel/code-frame": {
"version": "7.21.4",
"resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.21.4.tgz",
"integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz",
"integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==",
"dev": true,
"dependencies": {
"@babel/highlight": "^7.18.6"
"@babel/highlight": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/compat-data": {
"version": "7.21.7",
"resolved": "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.21.7.tgz",
"integrity": "sha512-KYMqFYTaenzMK4yUtf4EW9wc4N9ef80FsbMtkwool5zpwl4YrT1SdWYSTRcT94KO4hannogdS+LxY7L+arP3gA==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.5.tgz",
"integrity": "sha512-4Jc/YuIaYqKnDDz892kPIledykKg12Aw1PYX5i/TY28anJtacvM1Rrr8wbieB9GfEJwlzqT0hUEao0CxEebiDA==",
"dev": true,
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/core": {
"version": "7.21.8",
"resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.21.8.tgz",
"integrity": "sha512-YeM22Sondbo523Sz0+CirSPnbj9bG3P0CdHcBZdqUuaeOaYEFbOLoGU7lebvGP6P5J/WE9wOn7u7C4J9HvS1xQ==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.5.tgz",
"integrity": "sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg==",
"dev": true,
"dependencies": {
"@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.21.4",
"@babel/generator": "^7.21.5",
"@babel/helper-compilation-targets": "^7.21.5",
"@babel/helper-module-transforms": "^7.21.5",
"@babel/helpers": "^7.21.5",
"@babel/parser": "^7.21.8",
"@babel/template": "^7.20.7",
"@babel/traverse": "^7.21.5",
"@babel/types": "^7.21.5",
"@babel/code-frame": "^7.22.5",
"@babel/generator": "^7.22.5",
"@babel/helper-compilation-targets": "^7.22.5",
"@babel/helper-module-transforms": "^7.22.5",
"@babel/helpers": "^7.22.5",
"@babel/parser": "^7.22.5",
"@babel/template": "^7.22.5",
"@babel/traverse": "^7.22.5",
"@babel/types": "^7.22.5",
"convert-source-map": "^1.7.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.2",
@ -112,15 +114,19 @@
},
"engines": {
"node": ">=6.9.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/babel"
}
},
"node_modules/@babel/generator": {
"version": "7.21.5",
"resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.21.5.tgz",
"integrity": "sha512-SrKK/sRv8GesIW1bDagf9cCG38IOMYZusoe1dfg0D8aiUe3Amvoj1QtjTPAWcfrZFvIwlleLb0gxzQidL9w14w==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.5.tgz",
"integrity": "sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==",
"dev": true,
"dependencies": {
"@babel/types": "^7.21.5",
"@babel/types": "^7.22.5",
"@jridgewell/gen-mapping": "^0.3.2",
"@jridgewell/trace-mapping": "^0.3.17",
"jsesc": "^2.5.1"
@ -130,13 +136,13 @@
}
},
"node_modules/@babel/helper-compilation-targets": {
"version": "7.21.5",
"resolved": "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.5.tgz",
"integrity": "sha512-1RkbFGUKex4lvsB9yhIfWltJM5cZKUftB2eNajaDv3dCMEp49iBG0K14uH8NnX9IPux2+mK7JGEOB0jn48/J6w==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.5.tgz",
"integrity": "sha512-Ji+ywpHeuqxB8WDxraCiqR0xfhYjiDE/e6k7FuIaANnoOFxAHskHChz4vA1mJC9Lbm01s1PVAGhQY4FUKSkGZw==",
"dev": true,
"dependencies": {
"@babel/compat-data": "^7.21.5",
"@babel/helper-validator-option": "^7.21.0",
"@babel/compat-data": "^7.22.5",
"@babel/helper-validator-option": "^7.22.5",
"browserslist": "^4.21.3",
"lru-cache": "^5.1.1",
"semver": "^6.3.0"
@ -149,151 +155,151 @@
}
},
"node_modules/@babel/helper-environment-visitor": {
"version": "7.21.5",
"resolved": "https://registry.npmmirror.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.21.5.tgz",
"integrity": "sha512-IYl4gZ3ETsWocUWgsFZLM5i1BYx9SoemminVEXadgLBa9TdeorzgLKm8wWLA6J1N/kT3Kch8XIk1laNzYoHKvQ==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz",
"integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==",
"dev": true,
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-function-name": {
"version": "7.21.0",
"resolved": "https://registry.npmmirror.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz",
"integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz",
"integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==",
"dev": true,
"dependencies": {
"@babel/template": "^7.20.7",
"@babel/types": "^7.21.0"
"@babel/template": "^7.22.5",
"@babel/types": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-hoist-variables": {
"version": "7.18.6",
"resolved": "https://registry.npmmirror.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz",
"integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz",
"integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==",
"dev": true,
"dependencies": {
"@babel/types": "^7.18.6"
"@babel/types": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-module-imports": {
"version": "7.21.4",
"resolved": "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz",
"integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz",
"integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==",
"dev": true,
"dependencies": {
"@babel/types": "^7.21.4"
"@babel/types": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-module-transforms": {
"version": "7.21.5",
"resolved": "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.5.tgz",
"integrity": "sha512-bI2Z9zBGY2q5yMHoBvJ2a9iX3ZOAzJPm7Q8Yz6YeoUjU/Cvhmi2G4QyTNyPBqqXSgTjUxRg3L0xV45HvkNWWBw==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz",
"integrity": "sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw==",
"dev": true,
"dependencies": {
"@babel/helper-environment-visitor": "^7.21.5",
"@babel/helper-module-imports": "^7.21.4",
"@babel/helper-simple-access": "^7.21.5",
"@babel/helper-split-export-declaration": "^7.18.6",
"@babel/helper-validator-identifier": "^7.19.1",
"@babel/template": "^7.20.7",
"@babel/traverse": "^7.21.5",
"@babel/types": "^7.21.5"
"@babel/helper-environment-visitor": "^7.22.5",
"@babel/helper-module-imports": "^7.22.5",
"@babel/helper-simple-access": "^7.22.5",
"@babel/helper-split-export-declaration": "^7.22.5",
"@babel/helper-validator-identifier": "^7.22.5",
"@babel/template": "^7.22.5",
"@babel/traverse": "^7.22.5",
"@babel/types": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-plugin-utils": {
"version": "7.21.5",
"resolved": "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.21.5.tgz",
"integrity": "sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz",
"integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==",
"dev": true,
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-simple-access": {
"version": "7.21.5",
"resolved": "https://registry.npmmirror.com/@babel/helper-simple-access/-/helper-simple-access-7.21.5.tgz",
"integrity": "sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz",
"integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==",
"dev": true,
"dependencies": {
"@babel/types": "^7.21.5"
"@babel/types": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-split-export-declaration": {
"version": "7.18.6",
"resolved": "https://registry.npmmirror.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz",
"integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz",
"integrity": "sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ==",
"dev": true,
"dependencies": {
"@babel/types": "^7.18.6"
"@babel/types": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-string-parser": {
"version": "7.21.5",
"resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz",
"integrity": "sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz",
"integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==",
"dev": true,
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
"version": "7.19.1",
"resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
"integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz",
"integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==",
"dev": true,
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-option": {
"version": "7.21.0",
"resolved": "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz",
"integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz",
"integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==",
"dev": true,
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helpers": {
"version": "7.21.5",
"resolved": "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.21.5.tgz",
"integrity": "sha512-BSY+JSlHxOmGsPTydUkPf1MdMQ3M81x5xGCOVgWM3G8XH77sJ292Y2oqcp0CbbgxhqBuI46iUz1tT7hqP7EfgA==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.5.tgz",
"integrity": "sha512-pSXRmfE1vzcUIDFQcSGA5Mr+GxBV9oiRKDuDxXvWQQBCh8HoIjs/2DlDB7H8smac1IVrB9/xdXj2N3Wol9Cr+Q==",
"dev": true,
"dependencies": {
"@babel/template": "^7.20.7",
"@babel/traverse": "^7.21.5",
"@babel/types": "^7.21.5"
"@babel/template": "^7.22.5",
"@babel/traverse": "^7.22.5",
"@babel/types": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/highlight": {
"version": "7.18.6",
"resolved": "https://registry.npmmirror.com/@babel/highlight/-/highlight-7.18.6.tgz",
"integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz",
"integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==",
"dev": true,
"dependencies": {
"@babel/helper-validator-identifier": "^7.18.6",
"@babel/helper-validator-identifier": "^7.22.5",
"chalk": "^2.0.0",
"js-tokens": "^4.0.0"
},
@ -302,9 +308,9 @@
}
},
"node_modules/@babel/parser": {
"version": "7.21.8",
"resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.21.8.tgz",
"integrity": "sha512-6zavDGdzG3gUqAdWvlLFfk+36RilI+Pwyuuh7HItyeScCWP3k6i8vKclAQ0bM/0y/Kz/xiwvxhMv9MgTJP5gmA==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.5.tgz",
"integrity": "sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==",
"dev": true,
"bin": {
"parser": "bin/babel-parser.js"
@ -314,12 +320,12 @@
}
},
"node_modules/@babel/plugin-transform-react-jsx-self": {
"version": "7.21.0",
"resolved": "https://registry.npmmirror.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.21.0.tgz",
"integrity": "sha512-f/Eq+79JEu+KUANFks9UZCcvydOOGMgF7jBrcwjHa5jTZD8JivnhCJYvmlhR/WTXBWonDExPoW0eO/CR4QJirA==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.22.5.tgz",
"integrity": "sha512-nTh2ogNUtxbiSbxaT4Ds6aXnXEipHweN9YRgOX/oNXdf0cCrGn/+2LozFa3lnPV5D90MkjhgckCPBrsoSc1a7g==",
"dev": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.20.2"
"@babel/helper-plugin-utils": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
@ -329,12 +335,12 @@
}
},
"node_modules/@babel/plugin-transform-react-jsx-source": {
"version": "7.19.6",
"resolved": "https://registry.npmmirror.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.19.6.tgz",
"integrity": "sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.22.5.tgz",
"integrity": "sha512-yIiRO6yobeEIaI0RTbIr8iAK9FcBHLtZq0S89ZPjDLQXBA4xvghaKqI0etp/tF3htTM0sazJKKLz9oEiGRtu7w==",
"dev": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.19.0"
"@babel/helper-plugin-utils": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
@ -355,33 +361,33 @@
}
},
"node_modules/@babel/template": {
"version": "7.20.7",
"resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.20.7.tgz",
"integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz",
"integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==",
"dev": true,
"dependencies": {
"@babel/code-frame": "^7.18.6",
"@babel/parser": "^7.20.7",
"@babel/types": "^7.20.7"
"@babel/code-frame": "^7.22.5",
"@babel/parser": "^7.22.5",
"@babel/types": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse": {
"version": "7.21.5",
"resolved": "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.21.5.tgz",
"integrity": "sha512-AhQoI3YjWi6u/y/ntv7k48mcrCXmus0t79J9qPNlk/lAsFlCiJ047RmbfMOawySTHtywXhbXgpx/8nXMYd+oFw==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.5.tgz",
"integrity": "sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ==",
"dev": true,
"dependencies": {
"@babel/code-frame": "^7.21.4",
"@babel/generator": "^7.21.5",
"@babel/helper-environment-visitor": "^7.21.5",
"@babel/helper-function-name": "^7.21.0",
"@babel/helper-hoist-variables": "^7.18.6",
"@babel/helper-split-export-declaration": "^7.18.6",
"@babel/parser": "^7.21.5",
"@babel/types": "^7.21.5",
"@babel/code-frame": "^7.22.5",
"@babel/generator": "^7.22.5",
"@babel/helper-environment-visitor": "^7.22.5",
"@babel/helper-function-name": "^7.22.5",
"@babel/helper-hoist-variables": "^7.22.5",
"@babel/helper-split-export-declaration": "^7.22.5",
"@babel/parser": "^7.22.5",
"@babel/types": "^7.22.5",
"debug": "^4.1.0",
"globals": "^11.1.0"
},
@ -390,13 +396,13 @@
}
},
"node_modules/@babel/types": {
"version": "7.21.5",
"resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.21.5.tgz",
"integrity": "sha512-m4AfNvVF2mVC/F7fDEdH2El3HzUg9It/XsCxZiOTTA3m3qYfcSVSbTfM6Q9xG+hYDniZssYhlXKKUMD5m8tF4Q==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz",
"integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==",
"dev": true,
"dependencies": {
"@babel/helper-string-parser": "^7.21.5",
"@babel/helper-validator-identifier": "^7.19.1",
"@babel/helper-string-parser": "^7.22.5",
"@babel/helper-validator-identifier": "^7.22.5",
"to-fast-properties": "^2.0.0"
},
"engines": {
@ -1980,6 +1986,15 @@
"@types/unist": "*"
}
},
"node_modules/@types/hoist-non-react-statics": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
"integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
"dependencies": {
"@types/react": "*",
"hoist-non-react-statics": "^3.3.0"
}
},
"node_modules/@types/mdast": {
"version": "3.0.11",
"resolved": "https://registry.npmmirror.com/@types/mdast/-/mdast-3.0.11.tgz",
@ -2013,6 +2028,15 @@
"csstype": "^3.0.2"
}
},
"node_modules/@types/react-beautiful-dnd": {
"version": "13.1.4",
"resolved": "https://registry.npmjs.org/@types/react-beautiful-dnd/-/react-beautiful-dnd-13.1.4.tgz",
"integrity": "sha512-4bIBdzOr0aavN+88q3C7Pgz+xkb7tz3whORYrmSj77wfVEMfiWiooIwVWFR7KM2e+uGTe5BVrXqSfb0aHeflJA==",
"dev": true,
"dependencies": {
"@types/react": "*"
}
},
"node_modules/@types/react-dom": {
"version": "18.2.4",
"resolved": "https://registry.npmmirror.com/@types/react-dom/-/react-dom-18.2.4.tgz",
@ -2021,6 +2045,17 @@
"@types/react": "*"
}
},
"node_modules/@types/react-redux": {
"version": "7.1.25",
"resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.25.tgz",
"integrity": "sha512-bAGh4e+w5D8dajd6InASVIyCo4pZLJ66oLb80F9OBLO1gKESbZcRCJpTT6uLXX+HAB57zw1WTdwJdAsewuTweg==",
"dependencies": {
"@types/hoist-non-react-statics": "^3.3.0",
"@types/react": "*",
"hoist-non-react-statics": "^3.3.0",
"redux": "^4.0.0"
}
},
"node_modules/@types/scheduler": {
"version": "0.16.3",
"resolved": "https://registry.npmmirror.com/@types/scheduler/-/scheduler-0.16.3.tgz",
@ -2038,14 +2073,14 @@
"dev": true
},
"node_modules/@vitejs/plugin-react": {
"version": "4.0.0",
"resolved": "https://registry.npmmirror.com/@vitejs/plugin-react/-/plugin-react-4.0.0.tgz",
"integrity": "sha512-HX0XzMjL3hhOYm+0s95pb0Z7F8O81G7joUHgfDd/9J/ZZf5k4xX6QAMFkKsHFxaHlf6X7GD7+XuaZ66ULiJuhQ==",
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.0.1.tgz",
"integrity": "sha512-g25lL98essfeSj43HJ0o4DMp0325XK0ITkxpgChzJU/CyemgyChtlxfnRbjfwxDGCTRxTiXtQAsdebQXKMRSOA==",
"dev": true,
"dependencies": {
"@babel/core": "^7.21.4",
"@babel/plugin-transform-react-jsx-self": "^7.21.0",
"@babel/plugin-transform-react-jsx-source": "^7.19.6",
"@babel/core": "^7.22.5",
"@babel/plugin-transform-react-jsx-self": "^7.22.5",
"@babel/plugin-transform-react-jsx-source": "^7.22.5",
"react-refresh": "^0.14.0"
},
"engines": {
@ -2066,7 +2101,7 @@
},
"node_modules/ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
"dependencies": {
@ -2206,7 +2241,7 @@
},
"node_modules/chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"dependencies": {
@ -2285,7 +2320,7 @@
},
"node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dev": true,
"dependencies": {
@ -2294,7 +2329,7 @@
},
"node_modules/color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
"dev": true
},
@ -2320,10 +2355,18 @@
},
"node_modules/convert-source-map": {
"version": "1.9.0",
"resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-1.9.0.tgz",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
"dev": true
},
"node_modules/css-box-model": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz",
"integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==",
"dependencies": {
"tiny-invariant": "^1.0.6"
}
},
"node_modules/cssesc": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz",
@ -2462,7 +2505,7 @@
},
"node_modules/escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
"dev": true,
"engines": {
@ -2576,7 +2619,7 @@
},
"node_modules/gensync": {
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
"dev": true,
"engines": {
@ -2628,7 +2671,7 @@
},
"node_modules/globals": {
"version": "11.12.0",
"resolved": "https://registry.npmmirror.com/globals/-/globals-11.12.0.tgz",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
"dev": true,
"engines": {
@ -2649,7 +2692,7 @@
},
"node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
"dev": true,
"engines": {
@ -2754,6 +2797,19 @@
"node": ">=12.0.0"
}
},
"node_modules/hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
"dependencies": {
"react-is": "^16.7.0"
}
},
"node_modules/hoist-non-react-statics/node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"node_modules/html-parse-stringify": {
"version": "3.0.1",
"resolved": "https://registry.npmmirror.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz",
@ -2918,7 +2974,7 @@
},
"node_modules/jsesc": {
"version": "2.5.2",
"resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-2.5.2.tgz",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
"dev": true,
"bin": {
@ -2930,7 +2986,7 @@
},
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
"dev": true,
"bin": {
@ -2996,7 +3052,7 @@
},
"node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
"dev": true,
"dependencies": {
@ -3176,6 +3232,11 @@
"@types/mdast": "^3.0.0"
}
},
"node_modules/memoize-one": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="
},
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz",
@ -3789,6 +3850,11 @@
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
"dev": true
},
"node_modules/raf-schd": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz",
"integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ=="
},
"node_modules/react": {
"version": "18.2.0",
"resolved": "https://registry.npmmirror.com/react/-/react-18.2.0.tgz",
@ -3800,6 +3866,24 @@
"node": ">=0.10.0"
}
},
"node_modules/react-beautiful-dnd": {
"version": "13.1.1",
"resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.1.1.tgz",
"integrity": "sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==",
"dependencies": {
"@babel/runtime": "^7.9.2",
"css-box-model": "^1.2.0",
"memoize-one": "^5.1.1",
"raf-schd": "^4.0.2",
"react-redux": "^7.2.0",
"redux": "^4.0.4",
"use-memo-one": "^1.1.1"
},
"peerDependencies": {
"react": "^16.8.5 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.8.5 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-dom": {
"version": "18.2.0",
"resolved": "https://registry.npmmirror.com/react-dom/-/react-dom-18.2.0.tgz",
@ -3872,6 +3956,35 @@
"react": ">=16"
}
},
"node_modules/react-redux": {
"version": "7.2.9",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz",
"integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==",
"dependencies": {
"@babel/runtime": "^7.15.4",
"@types/react-redux": "^7.1.20",
"hoist-non-react-statics": "^3.3.2",
"loose-envify": "^1.4.0",
"prop-types": "^15.7.2",
"react-is": "^17.0.2"
},
"peerDependencies": {
"react": "^16.8.3 || ^17 || ^18"
},
"peerDependenciesMeta": {
"react-dom": {
"optional": true
},
"react-native": {
"optional": true
}
}
},
"node_modules/react-redux/node_modules/react-is": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
},
"node_modules/react-refresh": {
"version": "0.14.0",
"resolved": "https://registry.npmmirror.com/react-refresh/-/react-refresh-0.14.0.tgz",
@ -3944,6 +4057,14 @@
"node": ">=8.10.0"
}
},
"node_modules/redux": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz",
"integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==",
"dependencies": {
"@babel/runtime": "^7.9.2"
}
},
"node_modules/regenerator-runtime": {
"version": "0.13.11",
"resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
@ -4145,7 +4266,7 @@
},
"node_modules/semver": {
"version": "6.3.0",
"resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.0.tgz",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"dev": true,
"bin": {
@ -4238,7 +4359,7 @@
},
"node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"dependencies": {
@ -4350,9 +4471,14 @@
"node": ">=0.8"
}
},
"node_modules/tiny-invariant": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz",
"integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw=="
},
"node_modules/to-fast-properties": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
"integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
"dev": true,
"engines": {
@ -4503,6 +4629,14 @@
"react-dom": ">=16.8.0 <19.0.0"
}
},
"node_modules/use-memo-one": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz",
"integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/usehooks-ts": {
"version": "2.9.1",
"resolved": "https://registry.npmmirror.com/usehooks-ts/-/usehooks-ts-2.9.1.tgz",
@ -4580,9 +4714,9 @@
}
},
"node_modules/vite": {
"version": "4.3.6",
"resolved": "https://registry.npmmirror.com/vite/-/vite-4.3.6.tgz",
"integrity": "sha512-cqIyLSbA6gornMS659AXTVKF7cvSHMdKmJJwQ9DXq3lwsT1uZSdktuBRlpHQ8VnOWx0QHtjDwxPpGtyo9Fh/Qg==",
"version": "4.3.9",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.3.9.tgz",
"integrity": "sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==",
"dev": true,
"dependencies": {
"esbuild": "^0.17.5",
@ -4701,7 +4835,7 @@
},
"node_modules/yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"dev": true
},

View File

@ -19,6 +19,7 @@
"mobx": "^6.9.0",
"mobx-react-lite": "^3.4.3",
"react": "^18.2.0",
"react-beautiful-dnd": "^13.1.1",
"react-dom": "^18.2.0",
"react-i18next": "^12.2.2",
"react-markdown": "^8.0.7",
@ -34,6 +35,7 @@
},
"devDependencies": {
"@types/react": "^18.2.6",
"@types/react-beautiful-dnd": "^13.1.4",
"@types/react-dom": "^18.2.4",
"@types/uuid": "^9.0.1",
"@vitejs/plugin-react": "^4.0.0",

View File

@ -28,10 +28,10 @@ import { FC, useEffect, useState } from 'react';
import { Route, Routes, useLocation, useNavigate } from 'react-router';
import { pages } from './pages';
import { useMediaQuery } from 'usehooks-ts';
import { ToastContainer } from 'react-toastify';
import commonStore from './stores/commonStore';
import { observer } from 'mobx-react-lite';
import { useTranslation } from 'react-i18next';
import { CustomToastContainer } from './components/CustomToastContainer';
const App: FC = observer(() => {
const { t } = useTranslation();
@ -87,21 +87,7 @@ const App: FC = observer(() => {
</Routes>
</div>
</div>
<ToastContainer
style={{
width: '350px'
}}
position="top-center"
autoClose={4000}
pauseOnHover={true}
hideProgressBar={true}
newestOnTop={true}
closeOnClick={false}
rtl={false}
pauseOnFocusLoss={false}
draggable={false}
theme={commonStore.settings.darkMode ? 'dark' : 'light'}
/>
<CustomToastContainer />
</FluentProvider>
);
});

View File

@ -163,5 +163,30 @@
"The model file is corrupted, please download again.": "模型文件损坏,请重新下载",
"Found no NVIDIA driver, please install the latest driver.": "没有找到NVIDIA驱动请安装最新驱动",
"VRAM is not enough, please reduce stored layers or use a lower precision in Configs page.": "显存不足,请在配置页面减少载入显存层数,或使用更低的精度",
"Failed to enable custom CUDA kernel, ninja is required to load C++ extensions. You may be using the CPU version of PyTorch, please reinstall PyTorch with CUDA. Or if you are using a custom Python interpreter, you must compile the CUDA kernel by yourself or disable Custom CUDA kernel acceleration.": "自定义CUDA算子开启失败需要安装Ninja来读取C++扩展。你可能正在使用CPU版本的PyTorch请重新安装CUDA版本的PyTorch。如果你正在使用自定义Python解释器你必须自己编译CUDA算子或禁用自定义CUDA算子加速"
"Failed to enable custom CUDA kernel, ninja is required to load C++ extensions. You may be using the CPU version of PyTorch, please reinstall PyTorch with CUDA. Or if you are using a custom Python interpreter, you must compile the CUDA kernel by yourself or disable Custom CUDA kernel acceleration.": "自定义CUDA算子开启失败需要安装Ninja来读取C++扩展。你可能正在使用CPU版本的PyTorch请重新安装CUDA版本的PyTorch。如果你正在使用自定义Python解释器你必须自己编译CUDA算子或禁用自定义CUDA算子加速",
"Presets": "预设",
"Online": "在线",
"english": "英文",
"chinese": "中文",
"default": "默认",
"japanese": "日文",
"New Preset": "新建预设",
"Import": "导入",
"Name": "名称",
"Imported successfully": "导入成功",
"Failed to import. Please copy a preset to the clipboard.": "导入失败。请复制一个预设到剪贴板",
"Clipboard is empty.": "剪贴板没有内容",
"Successfully copied to clipboard.": "成功复制到剪贴板",
"Edit Messages": "编辑对话",
"Go Back": "返回",
"Description": "描述",
"Avatar Url": "头像图片地址",
"Welcome Message": "欢迎语",
"Display Preset Messages": "显示预设中的对话",
"Tag": "标签",
"Activate": "激活",
"New": "新建",
"user": "用户",
"assistant": "AI",
"system": "系统"
}

View File

@ -0,0 +1,17 @@
import commonStore from '../stores/commonStore';
import { ToastContainer } from 'react-toastify';
export const CustomToastContainer = () =>
<ToastContainer
style={{ width: '350px' }}
position="top-center"
autoClose={4000}
pauseOnHover={true}
hideProgressBar={true}
newestOnTop={true}
closeOnClick={false}
rtl={false}
pauseOnFocusLoss={false}
draggable={false}
theme={commonStore.settings.darkMode ? 'dark' : 'light'}
/>;

View File

@ -1,4 +1,4 @@
import React, { FC, MouseEventHandler, ReactElement } from 'react';
import React, { CSSProperties, FC, MouseEventHandler, ReactElement } from 'react';
import { Button, Tooltip } from '@fluentui/react-components';
export const ToolTipButton: FC<{
@ -6,6 +6,7 @@ export const ToolTipButton: FC<{
desc: string,
icon?: ReactElement,
className?: string,
style?: CSSProperties,
size?: 'small' | 'medium' | 'large',
shape?: 'rounded' | 'circular' | 'square';
appearance?: 'secondary' | 'primary' | 'outline' | 'subtle' | 'transparent';
@ -17,6 +18,7 @@ export const ToolTipButton: FC<{
desc,
icon,
className,
style,
size,
shape,
appearance,
@ -26,8 +28,8 @@ export const ToolTipButton: FC<{
}) => {
return (
<Tooltip content={desc} showDelay={showDelay} hideDelay={0} relationship="label">
<Button className={className} disabled={disabled} icon={icon} onClick={onClick} size={size} shape={shape}
appearance={appearance}>{text}</Button>
<Button style={style} className={className} disabled={disabled} icon={icon} onClick={onClick} size={size}
shape={shape} appearance={appearance}>{text}</Button>
</Tooltip>
);
};

View File

@ -7,7 +7,6 @@ import { v4 as uuid } from 'uuid';
import classnames from 'classnames';
import { fetchEventSource } from '@microsoft/fetch-event-source';
import { KebabHorizontalIcon, PencilIcon, SyncIcon, TrashIcon } from '@primer/octicons-react';
import { ConversationPair } from '../utils/get-conversation-pairs';
import logo from '../assets/images/logo.jpg';
import MarkdownRender from '../components/MarkdownRender';
import { ToolTipButton } from '../components/ToolTipButton';
@ -19,6 +18,8 @@ import { WorkHeader } from '../components/WorkHeader';
import { DialogButton } from '../components/DialogButton';
import { OpenFileFolder, OpenSaveFileDialog } from '../../wailsjs/go/backend_golang/App';
import { toastWithButton } from '../utils';
import { PresetsButton } from './PresetsManager/PresetsButton';
import { useMediaQuery } from 'usehooks-ts';
export const userName = 'M E';
export const botName = 'A I';
@ -49,6 +50,13 @@ export type Conversation = {
[uuid: string]: MessageItem
}
export type Role = 'assistant' | 'user' | 'system';
export type ConversationMessage = {
role: Role;
content: string;
}
let chatSseController: AbortController | null = null;
const MoreUtilsButton: FC<{ uuid: string, setEditing: (editing: boolean) => void }> = observer(({
@ -123,7 +131,7 @@ const ChatMessageItem: FC<{
<Avatar
color={messageItem.color}
name={messageItem.sender}
image={messageItem.avatarImg ? { src: messageItem.avatarImg } : undefined}
image={(commonStore.activePreset && messageItem.sender === botName) ? { src: commonStore.activePreset.avatarImg } : messageItem.avatarImg ? { src: messageItem.avatarImg } : undefined}
/>
<div
className={classnames(
@ -175,6 +183,7 @@ const ChatPanel: FC = observer(() => {
const { t } = useTranslation();
const bodyRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLTextAreaElement>(null);
const mq = useMediaQuery('(min-width: 640px)');
const port = commonStore.getCurrentModelConfig().apiParameters.apiPort;
let lastMessageId: string;
@ -255,7 +264,7 @@ const ChatPanel: FC = observer(() => {
let endIndex = endUuid ? (commonStore.conversationOrder.indexOf(endUuid) + (includeEndUuid ? 1 : 0)) : commonStore.conversationOrder.length;
let targetRange = commonStore.conversationOrder.slice(startIndex, endIndex);
const messages: ConversationPair[] = [];
const messages: ConversationMessage[] = [];
targetRange.forEach((uuid, index) => {
if (uuid === welcomeUuid)
return;
@ -357,10 +366,11 @@ const ChatPanel: FC = observer(() => {
<ChatMessageItem key={uuid} uuid={uuid} onSubmit={onSubmit} />
)}
</div>
<div className="flex items-end gap-2">
<div className={classnames('flex items-end', mq ? 'gap-2' : '')}>
<PresetsButton tab="Chat" size={mq ? 'large' : 'small'} shape="circular" appearance="subtle" />
<DialogButton tooltip={t('Clear')}
icon={<Delete28Regular />}
size="large" shape="circular" appearance="subtle" title={t('Clear')}
size={mq ? 'large' : 'small'} shape="circular" appearance="subtle" title={t('Clear')}
contentText={t('Are you sure you want to clear the conversation? It cannot be undone.')}
onConfirm={() => {
if (generating)
@ -370,6 +380,7 @@ const ChatPanel: FC = observer(() => {
}} />
<Textarea
ref={inputRef}
style={{ minWidth: 0 }}
className="grow"
resize="vertical"
placeholder={t('Type your message here')!}
@ -379,7 +390,7 @@ const ChatPanel: FC = observer(() => {
/>
<ToolTipButton desc={generating ? t('Stop') : t('Send')}
icon={generating ? <RecordStop28Regular /> : <ArrowCircleUp28Regular />}
size="large" shape="circular" appearance="subtle"
size={mq ? 'large' : 'small'} shape="circular" appearance="subtle"
onClick={(e) => {
if (generating) {
chatSseController?.abort();
@ -395,7 +406,7 @@ const ChatPanel: FC = observer(() => {
}} />
<ToolTipButton desc={t('Save')}
icon={<Save28Regular />}
size="large" shape="circular" appearance="subtle"
size={mq ? 'large' : 'small'} shape="circular" appearance="subtle"
onClick={() => {
let savedContent: string = '';
commonStore.conversationOrder.forEach((uuid) => {

View File

@ -10,6 +10,7 @@ import commonStore, { ModelStatus } from '../stores/commonStore';
import { fetchEventSource } from '@microsoft/fetch-event-source';
import { toast } from 'react-toastify';
import { DialogButton } from '../components/DialogButton';
import { PresetsButton } from './PresetsManager/PresetsButton';
export type CompletionParams = Omit<ApiParameters, 'apiPort'> & {
stop: string,
@ -261,19 +262,23 @@ const CompletionPanel: FC = observer(() => {
onChange={(e) => setPrompt(e.target.value)}
/>
<div className="flex flex-col gap-1 max-h-48 sm:max-w-sm sm:max-h-full">
<Dropdown style={{ minWidth: 0 }}
value={t(commonStore.completionPreset!.name)!}
selectedOptions={[commonStore.completionPreset!.name]}
onOptionSelect={(_, data) => {
if (data.optionValue) {
setPreset(defaultPresets.find((preset) => preset.name === data.optionValue)!);
<div className="flex gap-2">
<Dropdown style={{ minWidth: 0 }}
className="grow"
value={t(commonStore.completionPreset!.name)!}
selectedOptions={[commonStore.completionPreset!.name]}
onOptionSelect={(_, data) => {
if (data.optionValue) {
setPreset(defaultPresets.find((preset) => preset.name === data.optionValue)!);
}
}}>
{
defaultPresets.map((preset) =>
<Option key={preset.name} value={preset.name}>{t(preset.name)!}</Option>)
}
}}>
{
defaultPresets.map((preset) =>
<Option key={preset.name} value={preset.name}>{t(preset.name)!}</Option>)
}
</Dropdown>
</Dropdown>
<PresetsButton tab="Completion" />
</div>
<div className="flex flex-col gap-1 overflow-x-hidden overflow-y-auto p-1">
<Labeled flex breakline label={t('Max Response Token')}
desc={t('By default, the maximum number of tokens that can be answered in a single response, it can be changed by the user by specifying API parameters.')}

View File

@ -0,0 +1,154 @@
import React, { FC, useState } from 'react';
import { DragDropContext, Draggable, Droppable, DropResult } from 'react-beautiful-dnd';
import commonStore from '../../stores/commonStore';
import { Preset } from './PresetsButton';
import { observer } from 'mobx-react-lite';
import { v4 as uuid } from 'uuid';
import { Button, Card, Dropdown, Option, Textarea } from '@fluentui/react-components';
import { useTranslation } from 'react-i18next';
import { ToolTipButton } from '../../components/ToolTipButton';
import { Delete20Regular, ReOrderDotsVertical20Regular } from '@fluentui/react-icons';
import { ConversationMessage, Role } from '../Chat';
type Item = {
id: string;
role: Role;
content: string;
}
const getItems = (messages: ConversationMessage[]) =>
messages.map((message, index) => ({
id: uuid(),
role: message.role,
content: message.content
})) as Item[];
const reorder = (list: Item[], startIndex: number, endIndex: number) => {
const result = Array.from(list);
const [removed] = result.splice(startIndex, 1);
result.splice(endIndex, 0, removed);
return result;
};
export const MessagesEditor: FC = observer(() => {
const { t } = useTranslation();
const editingPreset = commonStore.editingPreset!;
const setEditingPreset = (newParams: Partial<Preset>) => {
commonStore.setEditingPreset({
...editingPreset,
...newParams
});
};
const [items, setItems] = useState(getItems(editingPreset.messages));
const updateItems = (items: Item[]) => {
setEditingPreset({
messages: items.map(item => ({
role: item.role,
content: item.content
}))
});
setItems(items);
};
const onDragEnd = (result: DropResult) => {
if (!result.destination) {
return;
}
const newItems = reorder(
items,
result.source.index,
result.destination.index
);
updateItems(newItems);
};
const createNewItem = () => {
const newItems: Item[] = [...items, {
id: uuid(),
role: 'assistant',
content: ''
}];
updateItems(newItems);
};
const deleteItem = (id: string) => {
const newItems: Item[] = items.filter(item => item.id !== id);
updateItems(newItems);
};
return (
<div className="grid grid-cols-1 gap-2 overflow-hidden">
<Button style={{ width: '100%' }} onClick={createNewItem}>{t('New')}</Button>
<div className="overflow-x-hidden overflow-y-auto p-2">
<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId="droppable">
{(provided, snapshot) => (
<div
{...provided.droppableProps}
ref={provided.innerRef}
>
{items.map((item, index) => (
<Draggable key={item.id} draggableId={item.id} index={index}>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
style={provided.draggableProps.style}
className="select-none mb-2"
>
<div className="flex">
<Card appearance="outline"
style={{ borderTopRightRadius: 0, borderBottomRightRadius: 0 }}>
<ReOrderDotsVertical20Regular />
</Card>
<Dropdown style={{ minWidth: 0, borderRadius: 0 }} listbox={{ style: { minWidth: 0 } }}
value={t(item.role)!}
selectedOptions={[item.role]}
onOptionSelect={(_, data) => {
if (data.optionValue) {
items[index] = {
...item,
role: data.optionValue as Role
};
updateItems([...items]);
}
}}>
<Option value="user">{t('user')!}</Option>
<Option value="assistant">{t('assistant')!}</Option>
<Option value="system">{t('system')!}</Option>
</Dropdown>
<Textarea resize="vertical" className="grow" value={item.content}
style={{ minWidth: 0, borderRadius: 0 }}
onChange={(e, data) => {
items[index] = {
...item,
content: data.value
};
updateItems([...items]);
}}></Textarea>
<ToolTipButton
style={{ borderTopLeftRadius: 0, borderBottomLeftRadius: 0 }} desc={t('Delete')}
icon={<Delete20Regular />} onClick={() => {
deleteItem(item.id);
}} />
</div>
</div>
)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
</div>
</div>
);
});

View File

@ -0,0 +1,432 @@
// TODO refactor
import React, { FC, PropsWithChildren, ReactElement, useState } from 'react';
import {
Button,
Dialog,
DialogBody,
DialogContent,
DialogSurface,
DialogTrigger,
Input,
Switch,
Tab,
TabList,
Text
} from '@fluentui/react-components';
import {
Accessibility28Regular,
Chat20Regular,
ClipboardEdit20Regular,
Delete20Regular,
Dismiss20Regular,
Edit20Regular,
Globe20Regular
} from '@fluentui/react-icons';
import { ToolTipButton } from '../../components/ToolTipButton';
import { useTranslation } from 'react-i18next';
import { botName, Conversation, ConversationMessage, MessageType, userName } from '../Chat';
import { SelectTabEventHandler } from '@fluentui/react-tabs';
import { Labeled } from '../../components/Labeled';
import commonStore from '../../stores/commonStore';
import logo from '../../assets/images/logo.jpg';
import { observer } from 'mobx-react-lite';
import { MessagesEditor } from './MessagesEditor';
import { ClipboardGetText, ClipboardSetText } from '../../../wailsjs/runtime';
import { toast } from 'react-toastify';
import { CustomToastContainer } from '../../components/CustomToastContainer';
import { v4 as uuid } from 'uuid';
export type PresetType = 'chat' | 'completion' | 'chatInCompletion'
export type Preset = {
name: string,
tag: string,
// if name and sourceUrl are same, it will be overridden when importing
sourceUrl: string,
desc: string,
avatarImg: string,
type: PresetType,
// chat
welcomeMessage: string,
messages: ConversationMessage[],
displayPresetMessages: boolean,
// completion
prompt: string,
stop: string,
injectStart: string,
injectEnd: string,
}
export const defaultPreset: Preset = {
name: 'RWKV',
tag: 'default',
sourceUrl: '',
desc: '',
avatarImg: logo,
type: 'chat',
welcomeMessage: '',
displayPresetMessages: true,
messages: [],
prompt: '',
stop: '',
injectStart: '',
injectEnd: ''
};
const setActivePreset = (preset: Preset) => {
commonStore.setActivePreset(preset);
//TODO if (preset.displayPresetMessages) {
const conversation: Conversation = {};
const conversationOrder: string[] = [];
for (const message of preset.messages) {
const newUuid = uuid();
conversationOrder.push(newUuid);
conversation[newUuid] = {
sender: message.role === 'user' ? userName : botName,
type: MessageType.Normal,
color: message.role === 'user' ? 'brand' : 'colorful',
time: new Date().toISOString(),
content: message.content,
side: message.role === 'user' ? 'right' : 'left',
done: true
};
}
commonStore.setConversation(conversation);
commonStore.setConversationOrder(conversationOrder);
//}
};
export const PresetCardFrame: FC<PropsWithChildren & { onClick?: () => void }> = (props) => {
return <Button
className="flex flex-col gap-1 w-32 h-56 break-all"
style={{ minWidth: 0, borderRadius: '0.75rem', justifyContent: 'unset' }}
onClick={props.onClick}
>
{props.children}
</Button>;
};
export const PresetCard: FC<{
avatarImg: string,
name: string,
desc: string,
tag: string,
editable: boolean,
presetIndex: number,
onClick?: () => void
}> = observer(({
avatarImg, name, desc, tag, editable, presetIndex, onClick
}) => {
const { t } = useTranslation();
return <PresetCardFrame onClick={onClick}>
<img src={avatarImg} className="rounded-xl select-none ml-auto mr-auto h-28" />
<Text size={400}>{name}</Text>
<Text size={200} style={{
overflow: 'hidden', textOverflow: 'ellipsis',
display: '-webkit-box', WebkitLineClamp: 3, WebkitBoxOrient: 'vertical'
}}>{desc}</Text>
<div className="grow" />
<div className="flex justify-between w-full items-end">
<div className="text-xs font-thin text-gray-500">{t(tag)}</div>
{editable ?
<ChatPresetEditor presetIndex={presetIndex} triggerButton={
<ToolTipButton size="small" appearance="transparent" desc={t('Edit')} icon={<Edit20Regular />}
onClick={() => {
commonStore.setEditingPreset({ ...commonStore.presets[presetIndex] });
}} />
} />
: <div />
}
</div>
</PresetCardFrame>;
});
export const ChatPresetEditor: FC<{
triggerButton: ReactElement,
presetIndex: number
}> = observer(({ triggerButton, presetIndex }) => {
const { t } = useTranslation();
const [editingMessages, setEditingMessages] = useState(false);
if (!commonStore.editingPreset)
commonStore.setEditingPreset({ ...defaultPreset });
const editingPreset = commonStore.editingPreset!;
const setEditingPreset = (newParams: Partial<Preset>) => {
commonStore.setEditingPreset({
...editingPreset,
...newParams
});
};
const importPreset = () => {
ClipboardGetText().then((text) => {
try {
const preset = JSON.parse(text);
setEditingPreset(preset);
toast(t('Imported successfully'), {
type: 'success',
autoClose: 1000
});
} catch (e) {
toast(t('Failed to import. Please copy a preset to the clipboard.'), {
type: 'error',
autoClose: 2500
});
}
}).catch(() => {
toast(t('Clipboard is empty.'), {
type: 'info',
autoClose: 1000
});
});
};
const copyPreset = () => {
ClipboardSetText(JSON.stringify(editingPreset)).then((success) => {
if (success)
toast(t('Successfully copied to clipboard.'), {
type: 'success',
autoClose: 1000
});
});
};
const savePreset = () => {
if (presetIndex === -1) {
commonStore.setPresets([...commonStore.presets, { ...editingPreset }]);
setEditingPreset(defaultPreset);
} else {
commonStore.presets[presetIndex] = editingPreset;
commonStore.setPresets(commonStore.presets);
}
};
const activatePreset = () => {
savePreset();
setActivePreset(editingPreset);
};
const deletePreset = () => {
commonStore.presets.splice(presetIndex, 1);
commonStore.setPresets(commonStore.presets);
};
return <Dialog>
<DialogTrigger disableButtonEnhancement>
{triggerButton}
</DialogTrigger>
<DialogSurface style={{
paddingTop: 0,
maxWidth: '80vw',
maxHeight: '80vh',
width: '500px',
height: '100%'
}}>
<DialogBody style={{ height: '100%', overflow: 'hidden' }}>
<DialogContent className="flex flex-col gap-1 overflow-hidden">
<CustomToastContainer />
<div className="flex justify-between">{
presetIndex === -1
? <div />
: <DialogTrigger disableButtonEnhancement>
<Button appearance="subtle" icon={<Delete20Regular />} onClick={deletePreset} />
</DialogTrigger>
}
<DialogTrigger disableButtonEnhancement>
<Button appearance="subtle" icon={<Dismiss20Regular />} />
</DialogTrigger>
</div>
<img src={editingPreset.avatarImg} className="rounded-xl select-none ml-auto mr-auto h-28" />
<Labeled flex breakline label={t('Name')}
content={
<div className="flex gap-2">
<Input className="grow" value={editingPreset.name} onChange={(e, data) => {
setEditingPreset({
name: data.value
});
}} />
<ToolTipButton desc={!editingMessages ? t('Edit Messages') : t('Go Back')}
icon={!editingMessages ? <Chat20Regular /> : <Dismiss20Regular />} onClick={() => {
setEditingMessages(!editingMessages);
}} />
</div>
} />
{
editingMessages ?
<MessagesEditor /> :
<div className="flex flex-col gap-1 p-2 overflow-x-hidden overflow-y-auto">
<Labeled flex breakline label={t('Description')}
content={
<Input value={editingPreset.desc} onChange={(e, data) => {
setEditingPreset({
desc: data.value
});
}} />
} />
<Labeled flex breakline label={t('Avatar Url')}
content={
<Input value={editingPreset.avatarImg} onChange={(e, data) => {
setEditingPreset({
avatarImg: data.value
});
}} />
} />
<Labeled flex breakline label={t('Welcome Message')}
content={
<Input disabled value={editingPreset.welcomeMessage} onChange={(e, data) => {
setEditingPreset({
welcomeMessage: data.value
});
}} />
} />
<Labeled flex spaceBetween label={t('Display Preset Messages')}
content={
<Switch disabled checked={editingPreset.displayPresetMessages}
onChange={(e, data) => {
setEditingPreset({
displayPresetMessages: data.checked
});
}} />
} />
<Labeled flex breakline label={t('Tag')}
content={
<Input value={editingPreset.tag} onChange={(e, data) => {
setEditingPreset({
tag: data.value
});
}} />
} />
</div>
}
<div className="grow" />
<div className="flex justify-between">
<Button onClick={importPreset}>{t('Import')}</Button>
<Button onClick={copyPreset}>{t('Copy')}</Button>
</div>
<div className="flex justify-between">
<DialogTrigger disableButtonEnhancement>
<Button appearance="primary" onClick={savePreset}>{t('Save')}</Button>
</DialogTrigger>
<DialogTrigger disableButtonEnhancement>
<Button appearance="primary" onClick={activatePreset}>{t('Activate')}</Button>
</DialogTrigger>
</div>
</DialogContent>
</DialogBody>
</DialogSurface>
</Dialog>;
});
export const ChatPresets: FC = observer(() => {
const { t } = useTranslation();
return <div className="flex flex-wrap gap-2">
<ChatPresetEditor presetIndex={-1} triggerButton={
<PresetCardFrame>
<div className="h-full flex items-center">
{t('New Preset')}
</div>
</PresetCardFrame>}
/>
{/*TODO <PresetCardFrame>*/}
{/* <div className="h-full flex items-center">*/}
{/* {t('Import')}*/}
{/* </div>*/}
{/*</PresetCardFrame>*/}
<PresetCard
presetIndex={-1}
editable={false}
onClick={() => {
setActivePreset(defaultPreset);
}} avatarImg={defaultPreset.avatarImg} name={defaultPreset.name} desc={defaultPreset.desc} tag={defaultPreset.tag}
/>
{commonStore.presets.map((preset, index) => {
return <PresetCard
presetIndex={index}
editable={true}
onClick={() => {
setActivePreset(preset);
}}
key={index} avatarImg={preset.avatarImg} name={preset.name} desc={preset.desc} tag={preset.tag}
/>;
})}
</div>;
});
type PresetsNavigationItem = {
icon: ReactElement;
element: ReactElement;
};
const pages: { [label: string]: PresetsNavigationItem } = {
Chat: {
icon: <Chat20Regular />,
element: <ChatPresets />
},
Completion: {
icon: <ClipboardEdit20Regular />,
element: <div>In Development</div>
},
Online: {
icon: <Globe20Regular />,
element: <div>In Development</div>
}
};
export const PresetsManager: FC<{ initTab: string }> = ({ initTab }) => {
const { t } = useTranslation();
const [tab, setTab] = useState(initTab);
const selectTab: SelectTabEventHandler = (e, data) =>
typeof data.value === 'string' ? setTab(data.value) : null;
return <div className="flex flex-col gap-2 w-full h-full">
<div className="flex justify-between">
<TabList
size="small"
appearance="subtle"
selectedValue={tab}
onTabSelect={selectTab}
>
{Object.entries(pages).map(([label, { icon }]) => (
<Tab icon={icon} key={label} value={label}>
{t(label)}
</Tab>
))}
</TabList>
<DialogTrigger disableButtonEnhancement>
<Button appearance="subtle" icon={<Dismiss20Regular />} />
</DialogTrigger>
</div>
<div className="grow overflow-x-hidden overflow-y-auto">
{pages[tab].element}
</div>
</div>;
};
export const PresetsButton: FC<{
tab: string,
size?: 'small' | 'medium' | 'large',
shape?: 'rounded' | 'circular' | 'square';
appearance?: 'secondary' | 'primary' | 'outline' | 'subtle' | 'transparent';
}> = ({ tab, size, shape, appearance }) => {
const { t } = useTranslation();
return <Dialog>
<DialogTrigger disableButtonEnhancement>
<ToolTipButton desc={t('Presets')} size={size} shape={shape} appearance={appearance}
icon={<Accessibility28Regular />} />
</DialogTrigger>
<DialogSurface style={{ paddingTop: 0, maxWidth: '90vw', width: 'fit-content' }}>
<DialogBody>
<DialogContent>
<CustomToastContainer />
<PresetsManager initTab={tab} />
</DialogContent>
</DialogBody>
</DialogSurface>
</Dialog>;
};

View File

@ -5,6 +5,7 @@ import { getStatus } from './apis';
import { EventsOn } from '../wailsjs/runtime';
import manifest from '../../manifest.json';
import { defaultModelConfigs, defaultModelConfigsMac } from './pages/defaultModelConfigs';
import { Preset } from './pages/PresetsManager/PresetsButton';
export async function startup() {
downloadProgramFiles();
@ -13,6 +14,8 @@ export async function startup() {
commonStore.setDownloadList(data);
});
initPresets();
await GetPlatform().then(p => commonStore.setPlatform(p as Platform));
await initConfig();
@ -65,4 +68,11 @@ async function initCache(initUnfinishedModels: boolean) {
}).catch(() => {
});
await refreshModels(false, initUnfinishedModels);
}
}
async function initPresets() {
await ReadJson('presets.json').then((presets: Preset[]) => {
commonStore.setPresets(presets, false);
}).catch(() => {
});
}

View File

@ -1,5 +1,5 @@
import { makeAutoObservable } from 'mobx';
import { getUserLanguage, isSystemLightMode, saveConfigs } from '../utils';
import { getUserLanguage, isSystemLightMode, saveConfigs, savePresets } from '../utils';
import { WindowSetDarkTheme, WindowSetLightTheme } from '../../wailsjs/runtime';
import manifest from '../../../manifest.json';
import { ModelConfig } from '../pages/Configs';
@ -13,6 +13,7 @@ import i18n from 'i18next';
import { CompletionPreset } from '../pages/Completion';
import { defaultModelConfigs, defaultModelConfigsMac } from '../pages/defaultModelConfigs';
import commonStore from './commonStore';
import { Preset } from '../pages/PresetsManager/PresetsButton';
export enum ModelStatus {
Offline,
@ -38,12 +39,16 @@ class CommonStore {
};
depComplete: boolean = false;
platform: Platform = 'windows';
// presets manager
editingPreset: Preset | null = null;
presets: Preset[] = [];
// home
introduction: IntroductionContent = manifest.introduction;
// chat
currentInput: string = '';
conversation: Conversation = {};
conversationOrder: string[] = [];
activePreset: Preset | null = null;
// completion
completionPreset: CompletionPreset | null = null;
completionGenerating: boolean = false;
@ -202,6 +207,20 @@ class CommonStore {
setLastUnfinishedModelDownloads(value: DownloadStatus[]) {
this.lastUnfinishedModelDownloads = value;
}
setEditingPreset(value: Preset) {
this.editingPreset = value;
}
setPresets(value: Preset[], savePreset: boolean = true) {
this.presets = value;
if (savePreset)
savePresets();
}
setActivePreset(value: Preset) {
this.activePreset = value;
}
}
export default new CommonStore();

View File

@ -1,27 +0,0 @@
export type Record = {
question: string;
answer: string;
}
export type ConversationPair = {
role: string;
content: string;
}
export function getConversationPairs(records: Record[], isCompletion: boolean): string | ConversationPair[] {
let pairs;
if (isCompletion) {
pairs = '';
for (const record of records) {
pairs += 'Human: ' + record.question + '\nAI: ' + record.answer + '\n';
}
} else {
pairs = [];
for (const record of records) {
pairs.push({ role: 'user', content: record.question });
pairs.push({ role: 'assistant', content: record.answer });
}
}
return pairs;
}

View File

@ -206,6 +206,10 @@ export const saveCache = async () => {
return SaveJson('cache.json', data);
};
export const savePresets = async () => {
return SaveJson('presets.json', commonStore.presets);
};
export function getUserLanguage(): Language {
// const l = navigator.language.toLowerCase();
// if (['zh-hk', 'zh-mo', 'zh-tw', 'zh-cht', 'zh-hant'].includes(l)) return 'zhHant'