feat: chat presets (experimental)
This commit is contained in:
		
							parent
							
								
									08cf09416a
								
							
						
					
					
						commit
						db67f30082
					
				
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -8,6 +8,7 @@ __pycache__
 | 
			
		||||
*.bin
 | 
			
		||||
/config.json
 | 
			
		||||
/cache.json
 | 
			
		||||
/presets.json
 | 
			
		||||
/frontend/stats.html
 | 
			
		||||
/frontend/package.json.md5
 | 
			
		||||
/py310
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										422
									
								
								frontend/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										422
									
								
								frontend/package-lock.json
									
									
									
										generated
									
									
									
								
							@ -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
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@ -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",
 | 
			
		||||
 | 
			
		||||
@ -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>
 | 
			
		||||
  );
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@ -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": "系统"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										17
									
								
								frontend/src/components/CustomToastContainer.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								frontend/src/components/CustomToastContainer.tsx
									
									
									
									
									
										Normal 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'}
 | 
			
		||||
  />;
 | 
			
		||||
@ -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>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -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) => {
 | 
			
		||||
 | 
			
		||||
@ -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.')}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										154
									
								
								frontend/src/pages/PresetsManager/MessagesEditor.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								frontend/src/pages/PresetsManager/MessagesEditor.tsx
									
									
									
									
									
										Normal 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>
 | 
			
		||||
  );
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										432
									
								
								frontend/src/pages/PresetsManager/PresetsButton.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										432
									
								
								frontend/src/pages/PresetsManager/PresetsButton.tsx
									
									
									
									
									
										Normal 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>;
 | 
			
		||||
};
 | 
			
		||||
@ -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(() => {
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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();
 | 
			
		||||
@ -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;
 | 
			
		||||
}
 | 
			
		||||
@ -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'
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user