diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 6f935f380e..ea792f2a00 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -7,7 +7,7 @@ "dependencies": { "@apollo/client": "3.7.3", "@floating-ui/react-dom-interactions": "0.6.3", - "@mattermost/compass-icons": "0.1.32", + "@mattermost/compass-icons": "0.1.47", "@mattermost/types": "7.1.0", "@mdi/js": "^6.5.95", "@mdi/react": "1.5.0", @@ -424,7 +424,6 @@ "node_modules/@babel/compat-data": { "version": "7.16.4", "integrity": "sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -432,7 +431,6 @@ "node_modules/@babel/core": { "version": "7.16.7", "integrity": "sha512-aeLaqcqThRNZYmbMqtulsetOQZ/5gbR/dWruUCJcpas4Qoyy+QeagfDsPdMrqwsPRDNxJvBlRiZxxX7THO7qtA==", - "dev": true, "dependencies": { "@babel/code-frame": "^7.16.7", "@babel/generator": "^7.16.7", @@ -496,7 +494,6 @@ "node_modules/@babel/helper-compilation-targets": { "version": "7.16.7", "integrity": "sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==", - "dev": true, "dependencies": { "@babel/compat-data": "^7.16.4", "@babel/helper-validator-option": "^7.16.7", @@ -640,7 +637,6 @@ "node_modules/@babel/helper-module-transforms": { "version": "7.16.7", "integrity": "sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==", - "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.16.7", "@babel/helper-module-imports": "^7.16.7", @@ -705,7 +701,6 @@ "node_modules/@babel/helper-simple-access": { "version": "7.16.7", "integrity": "sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g==", - "dev": true, "dependencies": { "@babel/types": "^7.16.7" }, @@ -753,7 +748,6 @@ "node_modules/@babel/helper-validator-option": { "version": "7.16.7", "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -775,7 +769,6 @@ "node_modules/@babel/helpers": { "version": "7.16.7", "integrity": "sha512-9ZDoqtfY7AuEOt3cxchfii6C7GDyyMBffktR5B2jvWv8u2+efwvpnVKXMWzNehqy68tKgAfSwfdw/lWpthS2bw==", - "dev": true, "dependencies": { "@babel/template": "^7.16.7", "@babel/traverse": "^7.16.7", @@ -5130,9 +5123,9 @@ } }, "node_modules/@mattermost/compass-icons": { - "version": "0.1.32", - "resolved": "https://registry.npmjs.org/@mattermost/compass-icons/-/compass-icons-0.1.32.tgz", - "integrity": "sha512-SruyY3dJUGoOCuc5M7KkpFZgotfmeV5Osi+nrMObRdTmaLfJ8h9Q6ZueLx4k4LkLt7hW0CAl33pWc6jO7p3egQ==" + "version": "0.1.47", + "resolved": "https://registry.npmjs.org/@mattermost/compass-icons/-/compass-icons-0.1.47.tgz", + "integrity": "sha512-vI4j1m/B9qQu51g5YnMQBft8gDh/ZWp3JHBgZXJwb522uY2o6WjwOpCbFwdJxWnD2uq/N2D9wDNmdutJNddf+w==" }, "node_modules/@mattermost/types": { "version": "7.1.0", @@ -5692,8 +5685,7 @@ }, "node_modules/@types/node": { "version": "14.18.5", - "integrity": "sha512-LMy+vDDcQR48EZdEx5wRX1q/sEl6NdGuHXPnfeL8ixkwCOSZ2qnIyIZmcCbdX0MeRqHhAcHmX+haCbrS8Run+A==", - "dev": true + "integrity": "sha512-LMy+vDDcQR48EZdEx5wRX1q/sEl6NdGuHXPnfeL8ixkwCOSZ2qnIyIZmcCbdX0MeRqHhAcHmX+haCbrS8Run+A==" }, "node_modules/@types/parse-json": { "version": "4.0.0", @@ -7611,7 +7603,6 @@ "node_modules/browserslist": { "version": "4.19.1", "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", - "dev": true, "dependencies": { "caniuse-lite": "^1.0.30001286", "electron-to-chromium": "^1.4.17", @@ -7744,7 +7735,6 @@ "node_modules/caniuse-lite": { "version": "1.0.30001296", "integrity": "sha512-WfrtPEoNSoeATDlf4y3QvkwiELl9GyPLISV5GejTbbQRtQx4LhsXmc9IQ6XCL2d7UxCyEzToEZNMeqR79OUw8Q==", - "dev": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/browserslist" @@ -8992,8 +8982,7 @@ }, "node_modules/electron-to-chromium": { "version": "1.4.37", - "integrity": "sha512-XIvFB1omSAxYgHYX48sC+HR8i/p7lx7R+0cX9faElg1g++h9IilCrJ12+bQuY+d96Wp7zkBiJwMOv+AhLtLrTg==", - "dev": true + "integrity": "sha512-XIvFB1omSAxYgHYX48sC+HR8i/p7lx7R+0cX9faElg1g++h9IilCrJ12+bQuY+d96Wp7zkBiJwMOv+AhLtLrTg==" }, "node_modules/elliptic": { "version": "6.5.4", @@ -9146,7 +9135,6 @@ "node_modules/escalade": { "version": "3.1.1", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, "engines": { "node": ">=6" } @@ -10541,7 +10529,6 @@ "node_modules/gensync": { "version": "1.0.0-beta.2", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -10795,7 +10782,7 @@ "version": "5.11.2", "resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-5.11.2.tgz", "integrity": "sha512-4EiZ3/UXYcjm+xFGP544/yW1+DVI8ZpKASFbzrV5EDTFWJp0ZvLl4Dy2fSZAzz9imKp5pZMIcjB0x/H69Pv/6w==", - "dev": true, + "devOptional": true, "engines": { "node": ">=10" }, @@ -13855,7 +13842,6 @@ "node_modules/json5": { "version": "2.2.0", "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "dev": true, "dependencies": { "minimist": "^1.2.5" }, @@ -15434,8 +15420,7 @@ }, "node_modules/node-releases": { "version": "2.0.1", - "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", - "dev": true + "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==" }, "node_modules/normalize-path": { "version": "3.0.0", @@ -16017,8 +16002,7 @@ }, "node_modules/picocolors": { "version": "1.0.0", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -17635,7 +17619,6 @@ "node_modules/semver": { "version": "6.3.0", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, "bin": { "semver": "bin/semver.js" } @@ -20255,13 +20238,11 @@ }, "@babel/compat-data": { "version": "7.16.4", - "integrity": "sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q==", - "dev": true + "integrity": "sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q==" }, "@babel/core": { "version": "7.16.7", "integrity": "sha512-aeLaqcqThRNZYmbMqtulsetOQZ/5gbR/dWruUCJcpas4Qoyy+QeagfDsPdMrqwsPRDNxJvBlRiZxxX7THO7qtA==", - "dev": true, "requires": { "@babel/code-frame": "^7.16.7", "@babel/generator": "^7.16.7", @@ -20309,7 +20290,6 @@ "@babel/helper-compilation-targets": { "version": "7.16.7", "integrity": "sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==", - "dev": true, "requires": { "@babel/compat-data": "^7.16.4", "@babel/helper-validator-option": "^7.16.7", @@ -20411,7 +20391,6 @@ "@babel/helper-module-transforms": { "version": "7.16.7", "integrity": "sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==", - "dev": true, "requires": { "@babel/helper-environment-visitor": "^7.16.7", "@babel/helper-module-imports": "^7.16.7", @@ -20461,7 +20440,6 @@ "@babel/helper-simple-access": { "version": "7.16.7", "integrity": "sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g==", - "dev": true, "requires": { "@babel/types": "^7.16.7" } @@ -20493,8 +20471,7 @@ }, "@babel/helper-validator-option": { "version": "7.16.7", - "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", - "dev": true + "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==" }, "@babel/helper-wrap-function": { "version": "7.16.7", @@ -20510,7 +20487,6 @@ "@babel/helpers": { "version": "7.16.7", "integrity": "sha512-9ZDoqtfY7AuEOt3cxchfii6C7GDyyMBffktR5B2jvWv8u2+efwvpnVKXMWzNehqy68tKgAfSwfdw/lWpthS2bw==", - "dev": true, "requires": { "@babel/template": "^7.16.7", "@babel/traverse": "^7.16.7", @@ -22515,7 +22491,8 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.3.0.tgz", "integrity": "sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q==", - "dev": true + "dev": true, + "requires": {} }, "has-flag": { "version": "4.0.0", @@ -22566,7 +22543,8 @@ "version": "8.11.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "dev": true + "dev": true, + "requires": {} }, "yargs": { "version": "17.3.1", @@ -22869,7 +22847,8 @@ "version": "8.11.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "dev": true + "dev": true, + "requires": {} } } }, @@ -22942,7 +22921,8 @@ "version": "8.11.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "dev": true + "dev": true, + "requires": {} } } }, @@ -23169,7 +23149,8 @@ "version": "8.11.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "dev": true + "dev": true, + "requires": {} } } }, @@ -23208,7 +23189,8 @@ }, "@graphql-typed-document-node/core": { "version": "3.1.1", - "integrity": "sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg==" + "integrity": "sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg==", + "requires": {} }, "@guyplusplus/turndown-plugin-gfm": { "version": "1.0.7", @@ -23249,7 +23231,8 @@ }, "@icons/material": { "version": "0.2.4", - "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==" + "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==", + "requires": {} }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", @@ -23798,14 +23781,15 @@ } }, "@mattermost/compass-icons": { - "version": "0.1.32", - "resolved": "https://registry.npmjs.org/@mattermost/compass-icons/-/compass-icons-0.1.32.tgz", - "integrity": "sha512-SruyY3dJUGoOCuc5M7KkpFZgotfmeV5Osi+nrMObRdTmaLfJ8h9Q6ZueLx4k4LkLt7hW0CAl33pWc6jO7p3egQ==" + "version": "0.1.47", + "resolved": "https://registry.npmjs.org/@mattermost/compass-icons/-/compass-icons-0.1.47.tgz", + "integrity": "sha512-vI4j1m/B9qQu51g5YnMQBft8gDh/ZWp3JHBgZXJwb522uY2o6WjwOpCbFwdJxWnD2uq/N2D9wDNmdutJNddf+w==" }, "@mattermost/types": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/@mattermost/types/-/types-7.1.0.tgz", - "integrity": "sha512-SVw5oDXwflNdXUWzl3QuTT5npEddglq6bB+kqdHjowPopOv1Fxi4zJh08/LvaRJYQENEk1QnEVIRTNhnoV0/tg==" + "integrity": "sha512-SVw5oDXwflNdXUWzl3QuTT5npEddglq6bB+kqdHjowPopOv1Fxi4zJh08/LvaRJYQENEk1QnEVIRTNhnoV0/tg==", + "requires": {} }, "@mdi/js": { "version": "6.5.95", @@ -23907,7 +23891,8 @@ "@restart/context": { "version": "2.1.4", "integrity": "sha512-INJYZQJP7g+IoDUh/475NlGiTeMfwTXUEr3tmRneckHIxNolGOW9CTq83S8cxq0CgJwwcMzMJFchxvlwe7Rk8Q==", - "dev": true + "dev": true, + "requires": {} }, "@restart/hooks": { "version": "0.3.27", @@ -24261,8 +24246,7 @@ }, "@types/node": { "version": "14.18.5", - "integrity": "sha512-LMy+vDDcQR48EZdEx5wRX1q/sEl6NdGuHXPnfeL8ixkwCOSZ2qnIyIZmcCbdX0MeRqHhAcHmX+haCbrS8Run+A==", - "dev": true + "integrity": "sha512-LMy+vDDcQR48EZdEx5wRX1q/sEl6NdGuHXPnfeL8ixkwCOSZ2qnIyIZmcCbdX0MeRqHhAcHmX+haCbrS8Run+A==" }, "@types/parse-json": { "version": "4.0.0", @@ -24908,7 +24892,8 @@ "@webpack-cli/configtest": { "version": "1.1.0", "integrity": "sha512-ttOkEkoalEHa7RaFYpM0ErK1xc4twg3Am9hfHhL7MVqlHebnkYd2wuI/ZqTDj0cVzZho6PdinY0phFZV3O0Mzg==", - "dev": true + "dev": true, + "requires": {} }, "@webpack-cli/info": { "version": "1.4.0", @@ -24921,7 +24906,8 @@ "@webpack-cli/serve": { "version": "1.6.0", "integrity": "sha512-ZkVeqEmRpBV2GHvjjUZqEai2PpUbuq8Bqd//vEYsp63J8WyexI8ppCqVS3Zs0QADf6aWuPdU+0XsPI647PVlQA==", - "dev": true + "dev": true, + "requires": {} }, "@whatwg-node/fetch": { "version": "0.5.4", @@ -25022,13 +25008,15 @@ "acorn-import-assertions": { "version": "1.8.0", "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "dev": true + "dev": true, + "requires": {} }, "acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true + "dev": true, + "requires": {} }, "acorn-walk": { "version": "7.2.0", @@ -25103,7 +25091,8 @@ "ajv-keywords": { "version": "3.5.2", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true + "dev": true, + "requires": {} }, "ansi-escapes": { "version": "4.3.2", @@ -25816,7 +25805,6 @@ "browserslist": { "version": "4.19.1", "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", - "dev": true, "requires": { "caniuse-lite": "^1.0.30001286", "electron-to-chromium": "^1.4.17", @@ -25909,8 +25897,7 @@ }, "caniuse-lite": { "version": "1.0.30001296", - "integrity": "sha512-WfrtPEoNSoeATDlf4y3QvkwiELl9GyPLISV5GejTbbQRtQx4LhsXmc9IQ6XCL2d7UxCyEzToEZNMeqR79OUw8Q==", - "dev": true + "integrity": "sha512-WfrtPEoNSoeATDlf4y3QvkwiELl9GyPLISV5GejTbbQRtQx4LhsXmc9IQ6XCL2d7UxCyEzToEZNMeqR79OUw8Q==" }, "capital-case": { "version": "1.0.4", @@ -25971,7 +25958,8 @@ "chartjs-plugin-annotation": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/chartjs-plugin-annotation/-/chartjs-plugin-annotation-2.1.2.tgz", - "integrity": "sha512-kmEp2WtpogwnKKnDPO3iO3mVwvVGtmG5BkZVtAEZm5YzJ9CYxojjYEgk7OTrFbJ5vU098b84UeJRe8kRfNcq5g==" + "integrity": "sha512-kmEp2WtpogwnKKnDPO3iO3mVwvVGtmG5BkZVtAEZm5YzJ9CYxojjYEgk7OTrFbJ5vU098b84UeJRe8kRfNcq5g==", + "requires": {} }, "chokidar": { "version": "3.5.2", @@ -26899,8 +26887,7 @@ }, "electron-to-chromium": { "version": "1.4.37", - "integrity": "sha512-XIvFB1omSAxYgHYX48sC+HR8i/p7lx7R+0cX9faElg1g++h9IilCrJ12+bQuY+d96Wp7zkBiJwMOv+AhLtLrTg==", - "dev": true + "integrity": "sha512-XIvFB1omSAxYgHYX48sC+HR8i/p7lx7R+0cX9faElg1g++h9IilCrJ12+bQuY+d96Wp7zkBiJwMOv+AhLtLrTg==" }, "elliptic": { "version": "6.5.4", @@ -27021,8 +27008,7 @@ }, "escalade": { "version": "3.1.1", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" }, "escape-html": { "version": "1.0.3", @@ -27514,7 +27500,8 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/eslint-plugin-import-newlines/-/eslint-plugin-import-newlines-1.3.0.tgz", "integrity": "sha512-8rokf6NvxC10ugA1VNmzEIO75CzId7IDF3Ai2GNXl0Xr4VORpb8u+bxsjRuE+2BS8MfDbrK/MHUQZI2G9qQyyA==", - "dev": true + "dev": true, + "requires": {} }, "eslint-plugin-no-relative-import-paths": { "version": "1.5.0", @@ -27565,7 +27552,8 @@ "eslint-plugin-react-hooks": { "version": "4.3.0", "integrity": "sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA==", - "dev": true + "dev": true, + "requires": {} }, "eslint-plugin-unused-imports": { "version": "2.0.0", @@ -28069,8 +28057,7 @@ }, "gensync": { "version": "1.0.0-beta.2", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" }, "get-caller-file": { "version": "2.0.5", @@ -28217,7 +28204,8 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.2.0.tgz", "integrity": "sha512-NkANeMnaHrlaSSlpKGyvn2R4rqUDeE/9E5YHx+b4nwo0R8dZyAqcih8/gxpCZvqWP9Vf6xuLpMSzSgdVEIM78g==", - "dev": true + "dev": true, + "requires": {} }, "minimatch": { "version": "4.2.1", @@ -28253,7 +28241,8 @@ "version": "5.11.2", "resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-5.11.2.tgz", "integrity": "sha512-4EiZ3/UXYcjm+xFGP544/yW1+DVI8ZpKASFbzrV5EDTFWJp0ZvLl4Dy2fSZAzz9imKp5pZMIcjB0x/H69Pv/6w==", - "dev": true + "devOptional": true, + "requires": {} }, "handle-thing": { "version": "2.0.1", @@ -28574,7 +28563,8 @@ "icss-utils": { "version": "5.1.0", "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true + "dev": true, + "requires": {} }, "identity-obj-proxy": { "version": "3.0.0", @@ -29080,7 +29070,8 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", - "dev": true + "dev": true, + "requires": {} }, "istanbul-lib-coverage": { "version": "3.2.0", @@ -29815,7 +29806,8 @@ "jest-pnp-resolver": { "version": "1.2.2", "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", - "dev": true + "dev": true, + "requires": {} }, "jest-regex-util": { "version": "27.4.0", @@ -30476,7 +30468,6 @@ "json5": { "version": "2.2.0", "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "dev": true, "requires": { "minimist": "^1.2.5" } @@ -31377,7 +31368,8 @@ "redux-thunk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", - "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==" + "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==", + "requires": {} }, "sass": { "version": "1.56.1", @@ -31483,7 +31475,8 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/meros/-/meros-1.2.1.tgz", "integrity": "sha512-R2f/jxYqCAGI19KhAvaxSOxALBMkaXWH2a7rOyqQw+ZmizX5bKkEYWLzdhC+U82ZVVPVp6MCXe3EkVligh+12g==", - "dev": true + "dev": true, + "requires": {} }, "methods": { "version": "1.1.2", @@ -31707,8 +31700,7 @@ }, "node-releases": { "version": "2.0.1", - "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", - "dev": true + "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==" }, "normalize-path": { "version": "3.0.0", @@ -32133,8 +32125,7 @@ }, "picocolors": { "version": "1.0.0", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" }, "picomatch": { "version": "2.3.1", @@ -32234,7 +32225,8 @@ "postcss-modules-extract-imports": { "version": "3.0.0", "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", - "dev": true + "dev": true, + "requires": {} }, "postcss-modules-local-by-default": { "version": "4.0.0", @@ -32537,7 +32529,8 @@ "react-chartjs-2": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-4.3.1.tgz", - "integrity": "sha512-5i3mjP6tU7QSn0jvb8I4hudTzHJqS8l00ORJnVwI2sYu0ihpj83Lv2YzfxunfxTZkscKvZu2F2w9LkwNBhj6xA==" + "integrity": "sha512-5i3mjP6tU7QSn0jvb8I4hudTzHJqS8l00ORJnVwI2sYu0ihpj83Lv2YzfxunfxTZkscKvZu2F2w9LkwNBhj6xA==", + "requires": {} }, "react-color": { "version": "2.19.3", @@ -32896,7 +32889,8 @@ }, "react-universal-interface": { "version": "0.6.2", - "integrity": "sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw==" + "integrity": "sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw==", + "requires": {} }, "react-use": { "version": "17.3.2", @@ -32927,7 +32921,8 @@ "react-virtualized-auto-sizer": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.7.tgz", - "integrity": "sha512-Mxi6lwOmjwIjC1X4gABXMJcKHsOo0xWl3E3ugOgufB8GJU+MqrtY35aBuvCYv/razQ1Vbp7h1gWJjGjoNN5pmA==" + "integrity": "sha512-Mxi6lwOmjwIjC1X4gABXMJcKHsOo0xWl3E3ugOgufB8GJU+MqrtY35aBuvCYv/razQ1Vbp7h1gWJjGjoNN5pmA==", + "requires": {} }, "react-window": { "version": "1.8.8", @@ -32941,7 +32936,8 @@ "react-window-infinite-loader": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/react-window-infinite-loader/-/react-window-infinite-loader-1.0.8.tgz", - "integrity": "sha512-907ZLAiZZfBHuZyiY0V7uiSL4P/rI6UQyCF9wES1cDWTeyNLgGLaxu+BZkcUW3R5tSCQcbCcWBl0jVIpYzrKGQ==" + "integrity": "sha512-907ZLAiZZfBHuZyiY0V7uiSL4P/rI6UQyCF9wES1cDWTeyNLgGLaxu+BZkcUW3R5tSCQcbCcWBl0jVIpYzrKGQ==", + "requires": {} }, "reactcss": { "version": "1.2.3", @@ -32987,7 +32983,8 @@ }, "redux-batched-actions": { "version": "0.5.0", - "integrity": "sha512-6orZWyCnIQXMGY4DUGM0oj0L7oYnwTACsfsru/J7r94RM3P9eS7SORGpr3LCeRCMoIMQcpfKZ7X4NdyFHBS8Eg==" + "integrity": "sha512-6orZWyCnIQXMGY4DUGM0oj0L7oYnwTACsfsru/J7r94RM3P9eS7SORGpr3LCeRCMoIMQcpfKZ7X4NdyFHBS8Eg==", + "requires": {} }, "redux-mock-store": { "version": "1.5.4", @@ -32999,12 +32996,14 @@ }, "redux-persist": { "version": "6.0.0", - "integrity": "sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ==" + "integrity": "sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ==", + "requires": {} }, "redux-thunk": { "version": "2.4.1", "integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==", - "dev": true + "dev": true, + "requires": {} }, "regenerate": { "version": "1.4.2", @@ -33326,8 +33325,7 @@ }, "semver": { "version": "6.3.0", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" }, "send": { "version": "0.17.2", @@ -33866,7 +33864,8 @@ "style-loader": { "version": "3.3.1", "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==", - "dev": true + "dev": true, + "requires": {} }, "styled-components": { "version": "5.3.3", @@ -34467,11 +34466,13 @@ "use-isomorphic-layout-effect": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", - "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==" + "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==", + "requires": {} }, "use-memo-one": { "version": "1.1.2", - "integrity": "sha512-u2qFKtxLsia/r8qG0ZKkbytbztzRb317XCkT7yP8wxL0tZ/CzK2G+WWie5vWvpyeP7+YoPIwbJoIHJ4Ba4k0oQ==" + "integrity": "sha512-u2qFKtxLsia/r8qG0ZKkbytbztzRb317XCkT7yP8wxL0tZ/CzK2G+WWie5vWvpyeP7+YoPIwbJoIHJ4Ba4k0oQ==", + "requires": {} }, "util-deprecate": { "version": "1.0.2", @@ -34874,7 +34875,8 @@ "ws": { "version": "8.4.0", "integrity": "sha512-IHVsKe2pjajSUIl4KYMQOdlyliovpEPquKkqbwswulszzI7r0SfQrxnXdWAEqOlDCLrVSJzo+O1hAwdog2sKSQ==", - "dev": true + "dev": true, + "requires": {} } } }, @@ -35024,7 +35026,8 @@ "ws": { "version": "7.5.6", "integrity": "sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA==", - "dev": true + "dev": true, + "requires": {} }, "xml": { "version": "1.0.1", diff --git a/webapp/package.json b/webapp/package.json index cf0a2a63d6..191a2a0254 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -7,7 +7,7 @@ "dependencies": { "@apollo/client": "3.7.3", "@floating-ui/react-dom-interactions": "0.6.3", - "@mattermost/compass-icons": "0.1.32", + "@mattermost/compass-icons": "0.1.47", "@mattermost/types": "7.1.0", "@mdi/js": "^6.5.95", "@mdi/react": "1.5.0", diff --git a/webapp/src/actions.ts b/webapp/src/actions.ts index df0f4ef139..5687eacce7 100644 --- a/webapp/src/actions.ts +++ b/webapp/src/actions.ts @@ -151,7 +151,7 @@ export function openUpdateRunStatusModal( hasPermission: boolean, message?: string, reminderInSeconds?: number, - finishRunChecked?: boolean + finishRunChecked?: boolean, ) { return modals.openModal(makeUpdateRunStatusModalDefinition({ playbookRunId, diff --git a/webapp/src/ai_integration.ts b/webapp/src/ai_integration.ts new file mode 100644 index 0000000000..cb0d6b2fdc --- /dev/null +++ b/webapp/src/ai_integration.ts @@ -0,0 +1,36 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {GlobalState} from 'mattermost-webapp/packages/types/src/store'; +import {useSelector} from 'react-redux'; + +import {Bot, BotSelector, BotsLoaderHook} from './types/ai'; + +export const aiPluginID = 'mattermost-ai'; + +export const useAIAvailable = () => { + //@ts-ignore plugins state is a thing + return useSelector((state) => Boolean(state.plugins?.plugins?.[aiPluginID])); +}; + +export const useAIAvailableBots = () => { + return useSelector((state) => { + //@ts-ignore plugins state is a thing + return state['plugins-' + aiPluginID]?.bots || []; + }); +}; + +export const useBotSelector = () => { + return useSelector((state) => { + //@ts-ignore plugins state is a thing + return state['plugins-' + aiPluginID]?.botSelector; + }); +}; + +export const useBotsLoaderHook = () => { + return useSelector((state) => { + //@ts-ignore plugins state is a thing + return state['plugins-' + aiPluginID]?.botsLoaderHook || (() => null); + }); +}; + diff --git a/webapp/src/client.ts b/webapp/src/client.ts index a0b12fab05..9681dba77d 100644 --- a/webapp/src/client.ts +++ b/webapp/src/client.ts @@ -62,6 +62,10 @@ export const setSiteUrl = (url?: string): void => { apiUrl = `${basePath}/plugins/${manifest.id}/api/v0`; }; +function playbookRunRoute(playbookRunID: string): string { + return `${basePath}/plugins/mattermost-ai/playbook_run/${playbookRunID}`; +} + export const getSiteUrl = (): string => { return siteURL; }; @@ -811,3 +815,25 @@ export async function getTeamTopPlaybooks(timeRange: string, page: number, perPa } return data as InsightsResponse; } + +export async function generateStatusUpdate(playbookRunID: string, botId: string, instructions: string[], messages: string[]) { + const url = `${playbookRunRoute(playbookRunID)}/generate_status`; + const response = await fetch(url, Client4.getOptions({ + method: 'POST', + body: JSON.stringify({ + instructions, + messages, + bot: botId, + }), + })); + + if (response.ok) { + return; + } + + throw new ClientError(Client4.url, { + message: '', + status_code: response.status, + url, + }); +} diff --git a/webapp/src/components/assets/buttons.tsx b/webapp/src/components/assets/buttons.tsx index 05b62eade8..b8496023cb 100644 --- a/webapp/src/components/assets/buttons.tsx +++ b/webapp/src/components/assets/buttons.tsx @@ -154,6 +154,31 @@ export const InvertedTertiaryButton = styled(Button)` } `; +export const QuaternaryButton = styled(Button)` + transition: all 0.15s ease-out; + + && { + color: var(--center-channel-color-64); + background-color: rgba(var(--center-channel-color-rgb), 0.08); + } + + &&:hover:not([disabled]) { + color: var(--center-channel-color-75); + background-color: rgba(var(--center-channel-color-rgb), 0.12); + } + + &&:active:not([disabled]) { + color: var(--button-bg-rgb); + background: rgba(var(--button-bg-rgb), 0.16); + } + + &&:focus:not([disabled]) { + color: var(--button-bg-rgb); + background-color: rgba(var(--button-color-rgb), 0.08); + box-shadow: inset 0px 0px 0px 2px var(--sidebar-text-active-border-rgb); + } +`; + export const SecondaryButton = styled(TertiaryButton)` background: var(--button-color-rgb); border: 1px solid var(--button-bg); diff --git a/webapp/src/components/assets/icons/ai.tsx b/webapp/src/components/assets/icons/ai.tsx new file mode 100644 index 0000000000..57983df6a8 --- /dev/null +++ b/webapp/src/components/assets/icons/ai.tsx @@ -0,0 +1,34 @@ +import React from 'react'; + +import Svg from 'src/components/assets/svg'; + +interface Props { + size?: number; +} + +const IconAI = ({size = 16}: Props) => ( + + + + + + +); + +export default IconAI; diff --git a/webapp/src/components/markdown_textbox.tsx b/webapp/src/components/markdown_textbox.tsx index 227ef27acd..926bc332c6 100644 --- a/webapp/src/components/markdown_textbox.tsx +++ b/webapp/src/components/markdown_textbox.tsx @@ -35,6 +35,7 @@ type Props = { hideHelpBar?: boolean; previewByDefault?: boolean; autoFocus?: boolean; + minHeight?: string; } & ComponentProps; const MarkdownTextbox = ({ @@ -48,6 +49,7 @@ const MarkdownTextbox = ({ previewByDefault, autoFocus, hideHelpBar, + minHeight = '104px', ...textboxProps }: Props) => { const [showPreview, setShowPreview] = useState(previewByDefault); @@ -63,7 +65,10 @@ const MarkdownTextbox = ({ }); return ( - + ` .textarea-wrapper { margin-bottom: 6px; } @@ -121,7 +126,7 @@ const Wrapper = styled.div` } height: unset; - min-height: 104px; + min-height: ${(props) => props.$minHeight || '104px'}; max-height: 324px; overflow: auto; padding: 12px 30px 12px 16px; diff --git a/webapp/src/components/modals/ai_modal.tsx b/webapp/src/components/modals/ai_modal.tsx new file mode 100644 index 0000000000..3049b4bf44 --- /dev/null +++ b/webapp/src/components/modals/ai_modal.tsx @@ -0,0 +1,427 @@ +import {WebSocketMessage} from '@mattermost/client'; + +import React, { + useCallback, + useEffect, + useRef, + useState, +} from 'react'; +import styled from 'styled-components'; +import {FormattedMessage, useIntl} from 'react-intl'; + +import IconAI from 'src/components/assets/icons/ai'; +import GenericModal from 'src/components/widgets/generic_modal'; +import {Textbox} from 'src/webapp_globals'; + +import {generateStatusUpdate} from 'src/client'; +import {useBotSelector, useBotsLoaderHook} from 'src/ai_integration'; + +import {Bot} from 'src/types/ai'; + +import postEventListener, {PostUpdateWebsocketMessage} from 'src/websocket'; + +const COPY_FEEDBACK_TIMEOUT_MS = 1000; +const SCROLL_TO_BOTTOM_TIMEOUT_MS = 0; + +type Version = { + instruction: string + prevValue: string + value: string +} + +type Props = { + playbookRunId: string + onAccept: (text: string) => void + onClose: () => void + isOpen: boolean +} + +const StyledAIModal = styled(GenericModal)` + &&& { + + .modal-content { + width: 90%; + margin-left: 5%; + } + + .modal-body { + padding: 0 24px; + } + + .modal-header .close { + z-index: 5; + } + } +`; + +const AIModal = ({playbookRunId, onAccept, onClose, isOpen}: Props) => { + const intl = useIntl(); + const [copied, setCopied] = useState(false); + const [instruction, setInstruction] = useState(''); + const suggestionBox = useRef(null); + const BotSelector = useBotSelector(); + const useBotlist = useBotsLoaderHook(); + const {bots, activeBot, setActiveBot} = useBotlist(); + const [currentVersion, setCurrentVersion] = useState(0); + const [versions, setVersions] = useState([]); + const [generating, setGenerating] = useState(false); + + useEffect(() => { + if (activeBot?.id && isOpen) { + setCurrentVersion(versions.length + 1); + setVersions([...versions, {instruction: '', value: '', prevValue: ''}]); + setGenerating(true); + generateStatusUpdate(playbookRunId, activeBot.id, [], []); + } + }, [isOpen]); + + useEffect(() => { + if (generating) { + postEventListener.registerPostUpdateListener('playbooks_post_update', (msg: WebSocketMessage) => { + const data = msg.data; + if (!data.control) { + setGenerating(true); + const newVersions = [...versions]; + newVersions[versions.length - 1] = {...newVersions[versions.length - 1], value: data.next}; + setVersions(newVersions); + setTimeout(() => suggestionBox.current?.scrollTo(0, suggestionBox.current?.scrollHeight), SCROLL_TO_BOTTOM_TIMEOUT_MS); + } else if (data.control === 'end') { + setGenerating(false); + } + }); + } + return () => { + postEventListener.unregisterPostUpdateListener('playbooks_post_update'); + }; + }, [generating]); + + const onBotChange = useCallback((bot: Bot) => { + setActiveBot(bot); + setCurrentVersion(versions.length + 1); + setVersions([...versions, {instruction: '', value: '', prevValue: ''}]); + setGenerating(true); + generateStatusUpdate(playbookRunId, bot.id, [], []); + }, [versions, playbookRunId]); + + const regenerate = useCallback(() => { + setGenerating(true); + generateStatusUpdate(playbookRunId, activeBot?.id, [versions[currentVersion - 1].instruction], [versions[currentVersion - 1].prevValue]); + setCurrentVersion(versions.length + 1); + setVersions([...versions, {...versions[currentVersion - 1], value: ''}]); + }, [versions, playbookRunId, instruction, versions, currentVersion, activeBot?.id]); + + const copyText = useCallback(() => { + navigator.clipboard.writeText(versions[currentVersion - 1].value); + setCopied(true); + setTimeout(() => setCopied(false), COPY_FEEDBACK_TIMEOUT_MS); + }, [versions, currentVersion]); + + const onInputEnter = useCallback((e: React.KeyboardEvent) => { + // Detect hitting enter and run the generateStatusUpdate + if (e.key === 'Enter') { + generateStatusUpdate(playbookRunId, activeBot?.id, [instruction], [versions[currentVersion - 1].value]); + setVersions([...versions, {instruction, prevValue: versions[currentVersion - 1].value, value: ''}]); + setCurrentVersion(versions.length + 1); + setInstruction(''); + setGenerating(true); + setTimeout(() => suggestionBox.current?.scrollTo(0, suggestionBox.current?.scrollHeight), SCROLL_TO_BOTTOM_TIMEOUT_MS); + } + }, [versions, instruction, playbookRunId, versions, currentVersion, activeBot?.id]); + + const stopGenerating = useCallback(() => { + setGenerating(false); + }, []); + + if (!activeBot?.id) { + return null; + } + + if (!isOpen) { + return null; + } + + return ( + + + + + setCurrentVersion(currentVersion - 1)} + className={currentVersion === 1 ? 'disabled' : ''} + aria-label={intl.formatMessage({defaultMessage: 'Go to previous version'})} + aria-disabled={currentVersion === 1} + > + + + + setCurrentVersion(currentVersion + 1)} + className={currentVersion === versions.length ? 'disabled' : ''} + aria-label={intl.formatMessage({defaultMessage: 'Go to next version'})} + aria-disabled={currentVersion === versions.length} + > + + + + + + + + + + {generating && + + + + + } + {!generating && + + + + + + + + onAccept(versions[currentVersion - 1].value)} + aria-label={intl.formatMessage({defaultMessage: 'Accept and insert generated content'})} + > + + + + + + + + } + + + setInstruction(e.target.value)} + value={instruction} + onKeyUp={onInputEnter} + /> + + + + ); +}; + +const IconButton = styled.span` + display: inline-block; + height: 24px; + width: 24px; + border-radius: 4px; + background: transparent; + text-decoration: none; + color: var(--center-channel-color-64); + padding: 8px; + cursor: pointer; + display: inline-flex; + align-items: center; + justify-content: center; + + .icon { + font-size: 14.4px; + } + + &:hover { + background: var(--center-channel-color-08); + color: var(--center-channel-color-75); + } + + &:active { + color: rgba(var(--button-bg-rgb)); + background: rgba(var(--button-bg-rgb), 0.16); + } + + &.disabled { + color: var(--center-channel-color-56); + pointer-events: none; + cursor: not-allowed; + } +`; + +const StopGeneratingButton = styled.button` + display: inline-flex; + margin: 12px 0; + align-items: center; + gap: 4px; + border-radius: 4px; + background: rgba(var(--center-channel-color-rgb), 0.08); + padding: 0px 10px 0 6px; + height: 24px; + color: var(--center-channel-color-64); + cursor: pointer; + text-decoration: none; + font-weight: 600; + font-size: 11px; + text-align: center; + border: 0; + + &:hover { + background: rgba(var(--center-channel-color-rgb), 0.12); + color: var(--center-channel-color-75); + } + + &:active { + background: rgba(var(--center-channel-color-rgb), 0.16); + } + + &:focus { + outline: none; + } +`; + +const AIModalContainer = styled.div` + position: relative; + margin-top: -56px; + padding-bottom: 24px; + + &&&& textarea { + min-height: 250px; + max-height: 250px; + border: 0; + outline: 0; + box-shadow: none; + &:focus, &:hover, &:active { + border: 0; + outline: 0; + } + } + .autosize_textarea_placeholder { + display: none; + } +`; + +const AIModalFooter = styled.div` + margin: 12px 0; + display: flex; + justify-content: flex-start; + align-items: center; + gap: 4px; +`; + +const ExtraInstructionsInput = styled.div` + display: flex; + width: 100%; + padding: 6px 16px; + min-height: 40px; + border-radius: 4px; + border: 1px solid var(--center-channel-color-16); + align-items: center; + transition: border-color 0.15s ease; + + &:focus-within { + border: 2px solid var(--button-bg); + padding: 5px 15px; /* Reduce padding by 1px to maintain same size with 2px border */ + } + + input { + font-size: 14px; + border: 0px; + width: 100%; + margin-left: 8px; + + &:focus { + outline: none; + } + } + + svg { + color: var(--center-channel-color-64); + } +`; + +const InsertButton = styled.button` + display: inline-block; + border-radius: 4px; + background: var(--button-bg-08); + padding: 0px 10px 0 6px; + height: 24px; + color: var(--button-bg); + cursor: pointer; + text-decoration: none; + font-weight: 600; + font-size: 11px; + text-align: center; + border: 0; + &:hover { + background: var(--button-bg-16); + } + &:active { + background: var(--button-bg-24); + } + &:focus { + outline: none; + } +`; + +const AssistantMessageBox = styled.div` + max-height: 200px; + height: 200px; + overflow-y: auto; + &&&& .custom-textarea { + border: none; + box-shadow: none; + height: 100%; + } +`; + +const Copied = styled.span<{ copied: boolean }>` + color: var(--online-indicator); + font-weight: 600; + margin: 0 4px; + font-size: 11px; + transition: opacity 1s; + opacity: ${(props) => (props.copied ? 1 : 0)}; +`; + +const TopBar = styled.div` + display: flex; + justify-content: space-between; + padding-bottom: 12px; + margin-right: 24px; +`; + +const Versions = styled.div` + display: flex; + align-items: center; + color: var(--center-channel-color-75); + font-size: 12px; + gap: 4px; + font-weight: 600; + flex-grow: 1; + + &.disabled { + color: var(--center-channel-color-64); + pointer-events: none; + } +`; + +export default AIModal; diff --git a/webapp/src/components/modals/update_run_status_modal.tsx b/webapp/src/components/modals/update_run_status_modal.tsx index 12c5d0e8ef..43432b46c6 100644 --- a/webapp/src/components/modals/update_run_status_modal.tsx +++ b/webapp/src/components/modals/update_run_status_modal.tsx @@ -10,7 +10,7 @@ import React, { import {Link} from 'react-router-dom'; import {useDispatch, useSelector} from 'react-redux'; import styled from 'styled-components'; -import {useIntl} from 'react-intl'; +import {FormattedMessage, useIntl} from 'react-intl'; import {DateTime, Duration} from 'luxon'; import {GlobalState} from '@mattermost/types/store'; @@ -19,8 +19,13 @@ import {getChannel} from 'mattermost-redux/selectors/entities/channels'; import {ApolloProvider, useQuery} from '@apollo/client'; +import {QuaternaryButton, TertiaryButton} from 'src/components/assets/buttons'; + import GenericModal, {Description, Label} from 'src/components/widgets/generic_modal'; import UnsavedChangesModal from 'src/components/widgets/unsaved_changes_modal'; +import IconAI from 'src/components/assets/icons/ai'; +import AIModal from 'src/components/modals/ai_modal'; +import {useAIAvailable, useAIAvailableBots} from 'src/ai_integration'; import { Mode, @@ -102,6 +107,9 @@ const UpdateRunStatusModal = ({ const dispatch = useDispatch(); const {formatMessage, formatList} = useIntl(); const currentUserId = useSelector(getCurrentUserId); + const [aiModalOpen, setAIModalOpen] = useState(false); + const aiAvailable = useAIAvailable(); + const aiAvailableBots = useAIAvailableBots(); const {data} = useQuery(runStatusModalQueryDocument, { variables: { runID: playbookRunId, @@ -258,11 +266,48 @@ const UpdateRunStatusModal = ({ const form = ( {description()} - + + + { aiAvailable && aiAvailableBots.length > 0 && + (aiModalOpen ? ( + { + setAIModalOpen(true); + }} + > + + + + ) : ( + { + setAIModalOpen(true); + }} + > + + + + )) + } + + { aiAvailable && + + { + setMessage(text); + setAIModalOpen(false); + }} + onClose={() => setAIModalOpen(false)} + isOpen={aiModalOpen} + /> + + } {hasPermission ? form : warning} @@ -502,6 +548,7 @@ const FooterContainer = styled.div` display: flex; flex-direction: row-reverse; align-items: center; + width: 100%; `; const StyledCheckboxInput = styled(CheckboxInput)` @@ -514,6 +561,28 @@ const StyledCheckboxInput = styled(CheckboxInput)` } `; +const AiModalContainer = styled.div` + position: relative; + box-shadow: var(--elevation-6); +`; + +const LastChangeSince = styled.div<{disabled: boolean}>` + display: flex; + justify-content: space-between; + pointer-events: ${(props) => (props.disabled ? 'none' : 'auto')}; + + >div { + margin: 24px 0 8px 0; + } + >button { + margin-top: 20px; + height: 24px; + gap: 6px; + padding: 0 10px; + font-size: 12px; + } +`; + const ApolloWrappedModal = (props: Props) => { const client = getPlaybooksGraphQLClient(); return ; diff --git a/webapp/src/components/widgets/generic_modal.tsx b/webapp/src/components/widgets/generic_modal.tsx index 1b048d9a05..005b2d1ee9 100644 --- a/webapp/src/components/widgets/generic_modal.tsx +++ b/webapp/src/components/widgets/generic_modal.tsx @@ -13,6 +13,7 @@ type Props = { className?: string; onHide: () => void; onExited?: () => void; + compassDesign?: boolean; modalHeaderText?: React.ReactNode; modalHeaderSideText?: React.ReactNode; modalHeaderIcon?: React.ReactNode; @@ -211,7 +212,7 @@ export const ModalHeading = styled.h1` font-size: 22px; line-height: 28px; color: var(--center-channel-color); - margin: 0; + margin: 8px 0 0; `; export const ModalSideheading = styled.h6` diff --git a/webapp/src/index.tsx b/webapp/src/index.tsx index 6f9c7e21ce..4b2732d8d0 100644 --- a/webapp/src/index.tsx +++ b/webapp/src/index.tsx @@ -49,6 +49,7 @@ import { handleWebsocketUserRemoved, } from 'src/websocket_events'; import { + WEBSOCKET_MATTERMOST_AI_POSTUPDATE, WEBSOCKET_PLAYBOOK_ARCHIVED, WEBSOCKET_PLAYBOOK_CREATED, WEBSOCKET_PLAYBOOK_RESTORED, @@ -73,6 +74,7 @@ import {setPlaybooksGraphQLClient} from './graphql_client'; import {RHSTitlePlaceholder} from './rhs_title_remote_render'; import {ApolloWrapper, makeGraphqlClient} from './graphql/apollo'; import PresetTemplates from './components/templates/template_data'; +import postEventListener from './websocket'; const GlobalHeaderCenter = () => { return null; @@ -239,6 +241,7 @@ export default class Plugin { registry.registerWebSocketEventHandler(WebsocketEvents.POST_DELETED, handleWebsocketPostEditedOrDeleted(store.getState, store.dispatch)); registry.registerWebSocketEventHandler(WebsocketEvents.POST_EDITED, handleWebsocketPostEditedOrDeleted(store.getState, store.dispatch)); registry.registerWebSocketEventHandler(WebsocketEvents.CHANNEL_UPDATED, handleWebsocketChannelUpdated(store.getState, store.dispatch)); + registry.registerWebSocketEventHandler(WEBSOCKET_MATTERMOST_AI_POSTUPDATE, postEventListener.handlePostUpdateWebsockets); // Local slash commands registry.registerSlashCommandWillBePostedHook(makeSlashCommandHook(store)); diff --git a/webapp/src/manifest.js b/webapp/src/manifest.js new file mode 100644 index 0000000000..19ec8b232a --- /dev/null +++ b/webapp/src/manifest.js @@ -0,0 +1,47 @@ +// This file is automatically generated. Do not modify it manually. + +const manifest = JSON.parse(` +{ + "id": "playbooks", + "name": "Playbooks", + "description": "Mattermost Playbooks enable reliable and repeatable processes for your teams using checklists, automation, and retrospectives.", + "homepage_url": "https://github.com/mattermost/mattermost-plugin-playbooks/", + "support_url": "https://github.com/mattermost/mattermost-plugin-playbooks/issues", + "release_notes_url": "https://github.com/mattermost/mattermost-plugin-playbooks/releases/tag/v1.33.0+alpha.4", + "icon_path": "assets/plugin_icon.svg", + "version": "1.33.0+alpha.4+29103d5b", + "min_server_version": "6.3.0", + "server": { + "executables": { + "darwin-amd64": "server/dist/plugin-darwin-amd64", + "darwin-arm64": "server/dist/plugin-darwin-arm64", + "linux-amd64": "server/dist/plugin-linux-amd64", + "linux-arm64": "server/dist/plugin-linux-arm64", + "windows-amd64": "server/dist/plugin-windows-amd64.exe" + }, + "executable": "" + }, + "webapp": { + "bundle_path": "webapp/dist/main.js" + }, + "settings_schema": { + "header": "", + "footer": "", + "settings": [ + { + "key": "EnableExperimentalFeatures", + "display_name": "Enable Experimental Features:", + "type": "bool", + "help_text": "Enable experimental features that come with in-progress UI, bugs, and cool stuff.", + "placeholder": "", + "default": null + } + ] + } +} +`); + +export default manifest; +export const id = manifest.id; +export const version = manifest.version; +export const pluginId = manifest.id; diff --git a/webapp/src/types/ai.tsx b/webapp/src/types/ai.tsx new file mode 100644 index 0000000000..1e06d5d8cf --- /dev/null +++ b/webapp/src/types/ai.tsx @@ -0,0 +1,14 @@ +import React from 'react'; + +export type Bot = { + id: string +} + +type BotSelectorProps = { + bots: Bot[] + activeBot: Bot + setActiveBot: (bot: Bot) => void +} + +export type BotsLoaderHook = () => BotSelectorProps +export type BotSelector = React.FunctionComponent diff --git a/webapp/src/types/websocket_events.ts b/webapp/src/types/websocket_events.ts index 9c51582ad8..7dd964923b 100644 --- a/webapp/src/types/websocket_events.ts +++ b/webapp/src/types/websocket_events.ts @@ -8,3 +8,4 @@ export const WEBSOCKET_PLAYBOOK_RUN_CREATED = `custom_${manifest.id}_playbook_ru export const WEBSOCKET_PLAYBOOK_CREATED = `custom_${manifest.id}_playbook_created`; export const WEBSOCKET_PLAYBOOK_ARCHIVED = `custom_${manifest.id}_playbook_archived`; export const WEBSOCKET_PLAYBOOK_RESTORED = `custom_${manifest.id}_playbook_restored`; +export const WEBSOCKET_MATTERMOST_AI_POSTUPDATE = 'custom_mattermost-ai_postupdate'; diff --git a/webapp/src/websocket.tsx b/webapp/src/websocket.tsx new file mode 100644 index 0000000000..73e204712c --- /dev/null +++ b/webapp/src/websocket.tsx @@ -0,0 +1,30 @@ +import {WebSocketMessage} from '@mattermost/client'; + +export interface PostUpdateWebsocketMessage { + next: string + post_id: string + control?: string +} + +type WebsocketListener = (msg: WebSocketMessage) => void +type WebsocketListeners = Map + +class PostEventListener { + postUpdateWebsocketListeners: WebsocketListeners = new Map(); + + public registerPostUpdateListener = (postID: string, listener: WebsocketListener) => { + this.postUpdateWebsocketListeners.set(postID, listener); + }; + + public unregisterPostUpdateListener = (postID: string) => { + this.postUpdateWebsocketListeners.delete(postID); + }; + + public handlePostUpdateWebsockets = (msg: WebSocketMessage) => { + const postID = msg.data.post_id; + this.postUpdateWebsocketListeners.get(postID)?.(msg); + }; +} + +const postEventListener = new PostEventListener(); +export default postEventListener;