mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-05-07 14:56:06 +02:00
replace el-form
This commit is contained in:
Generated
+94
-32
@@ -8,8 +8,7 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"hazardous": "^0.3.0",
|
"hazardous": "^0.3.0",
|
||||||
"node-api-dotnet": "^0.9.18",
|
"node-api-dotnet": "^0.9.18"
|
||||||
"vue-sonner": "^2.0.9"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@electron/rebuild": "^4.0.2",
|
"@electron/rebuild": "^4.0.2",
|
||||||
@@ -28,6 +27,7 @@
|
|||||||
"@tanstack/vue-virtual": "^3.13.18",
|
"@tanstack/vue-virtual": "^3.13.18",
|
||||||
"@types/jest": "^30.0.0",
|
"@types/jest": "^30.0.0",
|
||||||
"@types/node": "^25.0.6",
|
"@types/node": "^25.0.6",
|
||||||
|
"@vee-validate/zod": "^4.15.1",
|
||||||
"@vitejs/plugin-vue": "^6.0.3",
|
"@vitejs/plugin-vue": "^6.0.3",
|
||||||
"@vitejs/plugin-vue-jsx": "^5.1.3",
|
"@vitejs/plugin-vue-jsx": "^5.1.3",
|
||||||
"@vueuse/core": "^14.1.0",
|
"@vueuse/core": "^14.1.0",
|
||||||
@@ -61,6 +61,7 @@
|
|||||||
"tailwind-merge": "^3.4.0",
|
"tailwind-merge": "^3.4.0",
|
||||||
"tailwindcss": "^4.1.18",
|
"tailwindcss": "^4.1.18",
|
||||||
"tw-animate-css": "^1.4.0",
|
"tw-animate-css": "^1.4.0",
|
||||||
|
"vee-validate": "^4.15.1",
|
||||||
"vite": "^7.3.1",
|
"vite": "^7.3.1",
|
||||||
"vue": "^3.5.26",
|
"vue": "^3.5.26",
|
||||||
"vue-i18n": "^11.2.8",
|
"vue-i18n": "^11.2.8",
|
||||||
@@ -68,8 +69,10 @@
|
|||||||
"vue-marquee-text-component": "^2.0.1",
|
"vue-marquee-text-component": "^2.0.1",
|
||||||
"vue-router": "^4.6.4",
|
"vue-router": "^4.6.4",
|
||||||
"vue-showdown": "^4.2.0",
|
"vue-showdown": "^4.2.0",
|
||||||
|
"vue-sonner": "^2.0.9",
|
||||||
"worker-timers": "^8.0.28",
|
"worker-timers": "^8.0.28",
|
||||||
"yargs": "^18.0.0"
|
"yargs": "^18.0.0",
|
||||||
|
"zod": "^4.3.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/code-frame": {
|
"node_modules/@babel/code-frame": {
|
||||||
@@ -103,6 +106,7 @@
|
|||||||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.27.1",
|
"@babel/code-frame": "^7.27.1",
|
||||||
"@babel/generator": "^7.28.5",
|
"@babel/generator": "^7.28.5",
|
||||||
@@ -1260,7 +1264,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cross-dirname": "^0.1.0",
|
"cross-dirname": "^0.1.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
@@ -1282,7 +1285,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"graceful-fs": "^4.2.0",
|
"graceful-fs": "^4.2.0",
|
||||||
"jsonfile": "^6.0.1",
|
"jsonfile": "^6.0.1",
|
||||||
@@ -1299,7 +1301,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"universalify": "^2.0.0"
|
"universalify": "^2.0.0"
|
||||||
},
|
},
|
||||||
@@ -1314,7 +1315,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10.0.0"
|
"node": ">= 10.0.0"
|
||||||
}
|
}
|
||||||
@@ -5300,6 +5300,7 @@
|
|||||||
"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
|
"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/lodash": "*"
|
"@types/lodash": "*"
|
||||||
}
|
}
|
||||||
@@ -5690,6 +5691,33 @@
|
|||||||
"win32"
|
"win32"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/@vee-validate/zod": {
|
||||||
|
"version": "4.15.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vee-validate/zod/-/zod-4.15.1.tgz",
|
||||||
|
"integrity": "sha512-329Z4TDBE5Vx0FdbA8S4eR9iGCFFUNGbxjpQ20ff5b5wGueScjocUIx9JHPa79LTG06RnlUR4XogQsjN4tecKA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"type-fest": "^4.8.3",
|
||||||
|
"vee-validate": "4.15.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"zod": "^3.24.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vee-validate/zod/node_modules/type-fest": {
|
||||||
|
"version": "4.41.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
|
||||||
|
"integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "(MIT OR CC0-1.0)",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@vitejs/plugin-vue": {
|
"node_modules/@vitejs/plugin-vue": {
|
||||||
"version": "6.0.3",
|
"version": "6.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.3.tgz",
|
||||||
@@ -6017,6 +6045,7 @@
|
|||||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
@@ -6050,6 +6079,7 @@
|
|||||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-deep-equal": "^3.1.1",
|
"fast-deep-equal": "^3.1.1",
|
||||||
"fast-json-stable-stringify": "^2.0.0",
|
"fast-json-stable-stringify": "^2.0.0",
|
||||||
@@ -6814,6 +6844,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.9.0",
|
"baseline-browser-mapping": "^2.9.0",
|
||||||
"caniuse-lite": "^1.0.30001759",
|
"caniuse-lite": "^1.0.30001759",
|
||||||
@@ -7762,8 +7793,7 @@
|
|||||||
"integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==",
|
"integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/cross-env": {
|
"node_modules/cross-env": {
|
||||||
"version": "10.1.0",
|
"version": "10.1.0",
|
||||||
@@ -8064,6 +8094,7 @@
|
|||||||
"integrity": "sha512-ce4Ogns4VMeisIuCSK0C62umG0lFy012jd8LMZ6w/veHUeX4fqfDrGe+HTWALAEwK6JwKP+dhPvizhArSOsFbg==",
|
"integrity": "sha512-ce4Ogns4VMeisIuCSK0C62umG0lFy012jd8LMZ6w/veHUeX4fqfDrGe+HTWALAEwK6JwKP+dhPvizhArSOsFbg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"app-builder-lib": "26.4.0",
|
"app-builder-lib": "26.4.0",
|
||||||
"builder-util": "26.3.4",
|
"builder-util": "26.3.4",
|
||||||
@@ -8468,7 +8499,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron/asar": "^3.2.1",
|
"@electron/asar": "^3.2.1",
|
||||||
"debug": "^4.1.1",
|
"debug": "^4.1.1",
|
||||||
@@ -8489,7 +8519,6 @@
|
|||||||
"integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
|
"integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"graceful-fs": "^4.1.2",
|
"graceful-fs": "^4.1.2",
|
||||||
"jsonfile": "^4.0.0",
|
"jsonfile": "^4.0.0",
|
||||||
@@ -8647,7 +8676,8 @@
|
|||||||
"resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz",
|
||||||
"integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==",
|
"integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/embla-carousel-reactive-utils": {
|
"node_modules/embla-carousel-reactive-utils": {
|
||||||
"version": "8.6.0",
|
"version": "8.6.0",
|
||||||
@@ -8693,17 +8723,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/encoding": {
|
|
||||||
"version": "0.1.13",
|
|
||||||
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
|
|
||||||
"integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
|
||||||
"iconv-lite": "^0.6.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/end-of-stream": {
|
"node_modules/end-of-stream": {
|
||||||
"version": "1.4.5",
|
"version": "1.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
|
||||||
@@ -8832,6 +8851,7 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"esbuild": "bin/esbuild"
|
"esbuild": "bin/esbuild"
|
||||||
},
|
},
|
||||||
@@ -8911,6 +8931,7 @@
|
|||||||
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
|
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.8.0",
|
"@eslint-community/eslint-utils": "^4.8.0",
|
||||||
"@eslint-community/regexpp": "^4.12.1",
|
"@eslint-community/regexpp": "^4.12.1",
|
||||||
@@ -8971,6 +8992,7 @@
|
|||||||
"integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
|
"integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"eslint-config-prettier": "bin/cli.js"
|
"eslint-config-prettier": "bin/cli.js"
|
||||||
},
|
},
|
||||||
@@ -13440,14 +13462,16 @@
|
|||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/lodash-es": {
|
"node_modules/lodash-es": {
|
||||||
"version": "4.17.21",
|
"version": "4.17.21",
|
||||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||||
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
|
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/lodash-unified": {
|
"node_modules/lodash-unified": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
@@ -14673,6 +14697,7 @@
|
|||||||
"integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==",
|
"integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/devtools-api": "^7.7.7"
|
"@vue/devtools-api": "^7.7.7"
|
||||||
},
|
},
|
||||||
@@ -14843,7 +14868,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"commander": "^9.4.0"
|
"commander": "^9.4.0"
|
||||||
},
|
},
|
||||||
@@ -14861,7 +14885,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^12.20.0 || >=14"
|
"node": "^12.20.0 || >=14"
|
||||||
}
|
}
|
||||||
@@ -14882,6 +14905,7 @@
|
|||||||
"integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==",
|
"integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"prettier": "bin/prettier.cjs"
|
"prettier": "bin/prettier.cjs"
|
||||||
},
|
},
|
||||||
@@ -15325,7 +15349,6 @@
|
|||||||
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"glob": "^7.1.3"
|
"glob": "^7.1.3"
|
||||||
},
|
},
|
||||||
@@ -15340,7 +15363,6 @@
|
|||||||
"deprecated": "Glob versions prior to v9 are no longer supported",
|
"deprecated": "Glob versions prior to v9 are no longer supported",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fs.realpath": "^1.0.0",
|
"fs.realpath": "^1.0.0",
|
||||||
"inflight": "^1.0.4",
|
"inflight": "^1.0.4",
|
||||||
@@ -15362,7 +15384,6 @@
|
|||||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
},
|
},
|
||||||
@@ -17200,7 +17221,6 @@
|
|||||||
"integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==",
|
"integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"mkdirp": "^0.5.1",
|
"mkdirp": "^0.5.1",
|
||||||
"rimraf": "~2.6.2"
|
"rimraf": "~2.6.2"
|
||||||
@@ -17264,7 +17284,6 @@
|
|||||||
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"minimist": "^1.2.6"
|
"minimist": "^1.2.6"
|
||||||
},
|
},
|
||||||
@@ -17383,6 +17402,7 @@
|
|||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -17892,6 +17912,33 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/vee-validate": {
|
||||||
|
"version": "4.15.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/vee-validate/-/vee-validate-4.15.1.tgz",
|
||||||
|
"integrity": "sha512-DkFsiTwEKau8VIxyZBGdO6tOudD+QoUBPuHj3e6QFqmbfCRj1ArmYWue9lEp6jLSWBIw4XPlDLjFIZNLdRAMSg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/devtools-api": "^7.5.2",
|
||||||
|
"type-fest": "^4.8.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": "^3.4.26"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vee-validate/node_modules/type-fest": {
|
||||||
|
"version": "4.41.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
|
||||||
|
"integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "(MIT OR CC0-1.0)",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/verror": {
|
"node_modules/verror": {
|
||||||
"version": "1.10.1",
|
"version": "1.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz",
|
||||||
@@ -17914,6 +17961,7 @@
|
|||||||
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
|
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.27.0",
|
"esbuild": "^0.27.0",
|
||||||
"fdir": "^6.5.0",
|
"fdir": "^6.5.0",
|
||||||
@@ -18007,6 +18055,7 @@
|
|||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -18020,6 +18069,7 @@
|
|||||||
"integrity": "sha512-SJ/NTccVyAoNUJmkM9KUqPcYlY+u8OVL1X5EW9RIs3ch5H2uERxyyIUI4MRxVCSOiEcupX9xNGde1tL9ZKpimA==",
|
"integrity": "sha512-SJ/NTccVyAoNUJmkM9KUqPcYlY+u8OVL1X5EW9RIs3ch5H2uERxyyIUI4MRxVCSOiEcupX9xNGde1tL9ZKpimA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-dom": "3.5.26",
|
"@vue/compiler-dom": "3.5.26",
|
||||||
"@vue/compiler-sfc": "3.5.26",
|
"@vue/compiler-sfc": "3.5.26",
|
||||||
@@ -18216,6 +18266,7 @@
|
|||||||
"version": "2.0.9",
|
"version": "2.0.9",
|
||||||
"resolved": "https://registry.npmjs.org/vue-sonner/-/vue-sonner-2.0.9.tgz",
|
"resolved": "https://registry.npmjs.org/vue-sonner/-/vue-sonner-2.0.9.tgz",
|
||||||
"integrity": "sha512-i6BokNlNDL93fpzNxN/LZSn6D6MzlO+i3qXt6iVZne3x1k7R46d5HlFB4P8tYydhgqOrRbIZEsnRd3kG7qGXyw==",
|
"integrity": "sha512-i6BokNlNDL93fpzNxN/LZSn6D6MzlO+i3qXt6iVZne3x1k7R46d5HlFB4P8tYydhgqOrRbIZEsnRd3kG7qGXyw==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@nuxt/kit": "^4.0.3",
|
"@nuxt/kit": "^4.0.3",
|
||||||
@@ -18633,6 +18684,17 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/zod": {
|
||||||
|
"version": "4.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.5.tgz",
|
||||||
|
"integrity": "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/zrender": {
|
"node_modules/zrender": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/zrender/-/zrender-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/zrender/-/zrender-6.0.0.tgz",
|
||||||
|
|||||||
+6
-3
@@ -48,6 +48,7 @@
|
|||||||
"@tanstack/vue-virtual": "^3.13.18",
|
"@tanstack/vue-virtual": "^3.13.18",
|
||||||
"@types/jest": "^30.0.0",
|
"@types/jest": "^30.0.0",
|
||||||
"@types/node": "^25.0.6",
|
"@types/node": "^25.0.6",
|
||||||
|
"@vee-validate/zod": "^4.15.1",
|
||||||
"@vitejs/plugin-vue": "^6.0.3",
|
"@vitejs/plugin-vue": "^6.0.3",
|
||||||
"@vitejs/plugin-vue-jsx": "^5.1.3",
|
"@vitejs/plugin-vue-jsx": "^5.1.3",
|
||||||
"@vueuse/core": "^14.1.0",
|
"@vueuse/core": "^14.1.0",
|
||||||
@@ -81,6 +82,7 @@
|
|||||||
"tailwind-merge": "^3.4.0",
|
"tailwind-merge": "^3.4.0",
|
||||||
"tailwindcss": "^4.1.18",
|
"tailwindcss": "^4.1.18",
|
||||||
"tw-animate-css": "^1.4.0",
|
"tw-animate-css": "^1.4.0",
|
||||||
|
"vee-validate": "^4.15.1",
|
||||||
"vite": "^7.3.1",
|
"vite": "^7.3.1",
|
||||||
"vue": "^3.5.26",
|
"vue": "^3.5.26",
|
||||||
"vue-i18n": "^11.2.8",
|
"vue-i18n": "^11.2.8",
|
||||||
@@ -88,8 +90,10 @@
|
|||||||
"vue-marquee-text-component": "^2.0.1",
|
"vue-marquee-text-component": "^2.0.1",
|
||||||
"vue-router": "^4.6.4",
|
"vue-router": "^4.6.4",
|
||||||
"vue-showdown": "^4.2.0",
|
"vue-showdown": "^4.2.0",
|
||||||
|
"vue-sonner": "^2.0.9",
|
||||||
"worker-timers": "^8.0.28",
|
"worker-timers": "^8.0.28",
|
||||||
"yargs": "^18.0.0"
|
"yargs": "^18.0.0",
|
||||||
|
"zod": "^4.3.5"
|
||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"appId": "app.vrcx",
|
"appId": "app.vrcx",
|
||||||
@@ -176,7 +180,6 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"hazardous": "^0.3.0",
|
"hazardous": "^0.3.0",
|
||||||
"node-api-dotnet": "^0.9.18",
|
"node-api-dotnet": "^0.9.18"
|
||||||
"vue-sonner": "^2.0.9"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,7 +145,7 @@
|
|||||||
side="top"
|
side="top"
|
||||||
:content="t('dialog.group.actions.unrepresent_tooltip')">
|
:content="t('dialog.group.actions.unrepresent_tooltip')">
|
||||||
<Button
|
<Button
|
||||||
class="rounded-full"
|
class="rounded-full mr-2"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
size="icon-lg"
|
size="icon-lg"
|
||||||
style="margin-left: 5px"
|
style="margin-left: 5px"
|
||||||
@@ -156,10 +156,9 @@
|
|||||||
<TooltipWrapper v-else side="top" :content="t('dialog.group.actions.represent_tooltip')">
|
<TooltipWrapper v-else side="top" :content="t('dialog.group.actions.represent_tooltip')">
|
||||||
<span>
|
<span>
|
||||||
<Button
|
<Button
|
||||||
class="rounded-full"
|
class="rounded-full mr-2"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="icon-lg"
|
size="icon-lg"
|
||||||
style="margin-left: 5px"
|
|
||||||
:disabled="groupDialog.ref.privacy === 'private'"
|
:disabled="groupDialog.ref.privacy === 'private'"
|
||||||
@click="setGroupRepresentation(groupDialog.id)">
|
@click="setGroupRepresentation(groupDialog.id)">
|
||||||
<StarFilled />
|
<StarFilled />
|
||||||
@@ -171,10 +170,9 @@
|
|||||||
<TooltipWrapper side="top" :content="t('dialog.group.actions.cancel_join_request_tooltip')">
|
<TooltipWrapper side="top" :content="t('dialog.group.actions.cancel_join_request_tooltip')">
|
||||||
<span>
|
<span>
|
||||||
<Button
|
<Button
|
||||||
class="rounded-full"
|
class="rounded-full mr-2"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="icon-lg"
|
size="icon-lg"
|
||||||
style="margin-left: 5px"
|
|
||||||
@click="cancelGroupRequest(groupDialog.id)">
|
@click="cancelGroupRequest(groupDialog.id)">
|
||||||
<Close />
|
<Close />
|
||||||
</Button>
|
</Button>
|
||||||
@@ -185,10 +183,9 @@
|
|||||||
<TooltipWrapper side="top" :content="t('dialog.group.actions.pending_request_tooltip')">
|
<TooltipWrapper side="top" :content="t('dialog.group.actions.pending_request_tooltip')">
|
||||||
<span>
|
<span>
|
||||||
<Button
|
<Button
|
||||||
class="rounded-full"
|
class="rounded-full mr-2"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="icon-lg"
|
size="icon-lg"
|
||||||
style="margin-left: 5px"
|
|
||||||
@click="joinGroup(groupDialog.id)">
|
@click="joinGroup(groupDialog.id)">
|
||||||
<Check />
|
<Check />
|
||||||
</Button>
|
</Button>
|
||||||
@@ -201,10 +198,9 @@
|
|||||||
side="top"
|
side="top"
|
||||||
:content="t('dialog.group.actions.request_join_tooltip')">
|
:content="t('dialog.group.actions.request_join_tooltip')">
|
||||||
<Button
|
<Button
|
||||||
class="rounded-full"
|
class="rounded-full mr-2"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="icon-lg"
|
size="icon-lg"
|
||||||
style="margin-left: 5px"
|
|
||||||
@click="joinGroup(groupDialog.id)">
|
@click="joinGroup(groupDialog.id)">
|
||||||
<Message />
|
<Message />
|
||||||
</Button>
|
</Button>
|
||||||
@@ -214,12 +210,7 @@
|
|||||||
side="top"
|
side="top"
|
||||||
:content="t('dialog.group.actions.invite_required_tooltip')">
|
:content="t('dialog.group.actions.invite_required_tooltip')">
|
||||||
<span>
|
<span>
|
||||||
<Button
|
<Button class="rounded-full mr-2" variant="outline" size="icon-lg" disabled>
|
||||||
class="rounded-full"
|
|
||||||
variant="outline"
|
|
||||||
size="icon-lg"
|
|
||||||
disabled
|
|
||||||
style="margin-left: 5px">
|
|
||||||
<Message />
|
<Message />
|
||||||
</Button>
|
</Button>
|
||||||
</span>
|
</span>
|
||||||
@@ -229,10 +220,9 @@
|
|||||||
side="top"
|
side="top"
|
||||||
:content="t('dialog.group.actions.join_group_tooltip')">
|
:content="t('dialog.group.actions.join_group_tooltip')">
|
||||||
<Button
|
<Button
|
||||||
class="rounded-full"
|
class="rounded-full mr-2"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="icon-lg"
|
size="icon-lg"
|
||||||
style="margin-left: 5px"
|
|
||||||
@click="joinGroup(groupDialog.id)">
|
@click="joinGroup(groupDialog.id)">
|
||||||
<Check />
|
<Check />
|
||||||
</Button>
|
</Button>
|
||||||
@@ -245,8 +235,7 @@
|
|||||||
:variant="
|
:variant="
|
||||||
groupDialog.ref.membershipStatus === 'userblocked' ? 'destructive' : 'outline'
|
groupDialog.ref.membershipStatus === 'userblocked' ? 'destructive' : 'outline'
|
||||||
"
|
"
|
||||||
size="icon-lg"
|
size="icon-lg">
|
||||||
style="margin-left: 5px">
|
|
||||||
<MoreFilled />
|
<MoreFilled />
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
|
|||||||
@@ -5,25 +5,35 @@
|
|||||||
width="650px"
|
width="650px"
|
||||||
append-to-body>
|
append-to-body>
|
||||||
<div v-if="groupPostEditDialog.visible">
|
<div v-if="groupPostEditDialog.visible">
|
||||||
<h3 v-text="groupPostEditDialog.groupRef.name"></h3>
|
<FieldGroup class="gap-4">
|
||||||
<el-form :model="groupPostEditDialog" label-width="150px">
|
<Field>
|
||||||
<el-form-item :label="t('dialog.group_post_edit.title')">
|
<FieldLabel>{{ t('dialog.group_post_edit.title') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<InputGroupField v-model="groupPostEditDialog.title" size="sm" />
|
<InputGroupField v-model="groupPostEditDialog.title" size="sm" />
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
<el-form-item :label="t('dialog.group_post_edit.message')">
|
</Field>
|
||||||
|
<Field>
|
||||||
|
<FieldLabel>{{ t('dialog.group_post_edit.message') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<InputGroupTextareaField
|
<InputGroupTextareaField
|
||||||
v-model="groupPostEditDialog.text"
|
v-model="groupPostEditDialog.text"
|
||||||
:rows="4"
|
:rows="4"
|
||||||
style="margin-top: 10px"
|
style="margin-top: 10px"
|
||||||
input-class="resize-none" />
|
input-class="resize-none" />
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
<el-form-item>
|
</Field>
|
||||||
<label v-if="!groupPostEditDialog.postId" class="inline-flex items-center gap-2">
|
<Field v-if="!groupPostEditDialog.postId">
|
||||||
|
<FieldLabel class="sr-only">{{ t('dialog.group_post_edit.send_notification') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
|
<label class="inline-flex items-center gap-2">
|
||||||
<Checkbox v-model="groupPostEditDialog.sendNotification" />
|
<Checkbox v-model="groupPostEditDialog.sendNotification" />
|
||||||
<span>{{ t('dialog.group_post_edit.send_notification') }}</span>
|
<span>{{ t('dialog.group_post_edit.send_notification') }}</span>
|
||||||
</label>
|
</label>
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
<el-form-item :label="t('dialog.group_post_edit.post_visibility')">
|
</Field>
|
||||||
|
<Field>
|
||||||
|
<FieldLabel>{{ t('dialog.group_post_edit.post_visibility') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<RadioGroup v-model="groupPostEditDialog.visibility" class="flex items-center gap-4">
|
<RadioGroup v-model="groupPostEditDialog.visibility" class="flex items-center gap-4">
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center space-x-2">
|
||||||
<RadioGroupItem id="groupPostVisibility-public" value="public" />
|
<RadioGroupItem id="groupPostVisibility-public" value="public" />
|
||||||
@@ -38,8 +48,11 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
<el-form-item v-if="groupPostEditDialog.visibility === 'group'" :label="t('dialog.new_instance.roles')">
|
</Field>
|
||||||
|
<Field v-if="groupPostEditDialog.visibility === 'group'">
|
||||||
|
<FieldLabel>{{ t('dialog.new_instance.roles') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<Select
|
<Select
|
||||||
multiple
|
multiple
|
||||||
:model-value="Array.isArray(groupPostEditDialog.roleIds) ? groupPostEditDialog.roleIds : []"
|
:model-value="Array.isArray(groupPostEditDialog.roleIds) ? groupPostEditDialog.roleIds : []"
|
||||||
@@ -62,8 +75,11 @@
|
|||||||
</SelectGroup>
|
</SelectGroup>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
<el-form-item :label="t('dialog.group_post_edit.image')">
|
</Field>
|
||||||
|
<Field>
|
||||||
|
<FieldLabel>{{ t('dialog.group_post_edit.image') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<template v-if="gallerySelectDialog.selectedFileId">
|
<template v-if="gallerySelectDialog.selectedFileId">
|
||||||
<div style="display: inline-block; flex: none; margin-right: 5px">
|
<div style="display: inline-block; flex: none; margin-right: 5px">
|
||||||
<img
|
<img
|
||||||
@@ -85,8 +101,9 @@
|
|||||||
{{ t('dialog.invite_message.select_image') }}
|
{{ t('dialog.invite_message.select_image') }}
|
||||||
</Button>
|
</Button>
|
||||||
</template>
|
</template>
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
</el-form>
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
</div>
|
</div>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
@@ -109,6 +126,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { Field, FieldContent, FieldGroup, FieldLabel } from '@/components/ui/field';
|
||||||
import { InputGroupField, InputGroupTextareaField } from '@/components/ui/input-group';
|
import { InputGroupField, InputGroupTextareaField } from '@/components/ui/input-group';
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
|||||||
@@ -1,63 +1,68 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dialog :z-index="launchDialogIndex" v-model="isVisible" :title="t('dialog.launch.header')" width="450px">
|
<el-dialog :z-index="launchDialogIndex" v-model="isVisible" :title="t('dialog.launch.header')" width="450px">
|
||||||
<el-form :model="launchDialog" label-width="100px">
|
<FieldGroup class="gap-4">
|
||||||
<el-form-item :label="t('dialog.launch.url')">
|
<Field>
|
||||||
|
<FieldLabel>{{ t('dialog.launch.url') }}</FieldLabel>
|
||||||
|
<FieldContent class="flex-row items-center gap-2">
|
||||||
<InputGroupField
|
<InputGroupField
|
||||||
v-model="launchDialog.url"
|
v-model="launchDialog.url"
|
||||||
size="sm"
|
size="sm"
|
||||||
style="width: 230px"
|
|
||||||
@click="$event.target.tagName === 'INPUT' && $event.target.select()" />
|
@click="$event.target.tagName === 'INPUT' && $event.target.select()" />
|
||||||
<TooltipWrapper side="right" :content="t('dialog.launch.copy_tooltip')">
|
<TooltipWrapper side="right" :content="t('dialog.launch.copy_tooltip')">
|
||||||
<Button
|
<Button
|
||||||
class="rounded-full ml-1"
|
class="rounded-full"
|
||||||
size="icon-sm"
|
size="icon-sm"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
@click="copyInstanceMessage(launchDialog.url)"
|
@click="copyInstanceMessage(launchDialog.url)"
|
||||||
><Copy
|
><Copy
|
||||||
/></Button>
|
/></Button>
|
||||||
</TooltipWrapper>
|
</TooltipWrapper>
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
<el-form-item v-if="launchDialog.shortUrl">
|
</Field>
|
||||||
<template #label>
|
<Field v-if="launchDialog.shortUrl">
|
||||||
<div class="flex items-center">
|
<FieldLabel>
|
||||||
|
<span class="flex items-center gap-1">
|
||||||
<span>{{ t('dialog.launch.short_url') }}</span>
|
<span>{{ t('dialog.launch.short_url') }}</span>
|
||||||
<TooltipWrapper side="top" :content="t('dialog.launch.short_url_notice')">
|
<TooltipWrapper side="top" :content="t('dialog.launch.short_url_notice')">
|
||||||
<el-icon style="display: inline-block; margin-left: 5px"><Warning /></el-icon>
|
<el-icon><Warning /></el-icon>
|
||||||
</TooltipWrapper>
|
</TooltipWrapper>
|
||||||
</div>
|
</span>
|
||||||
</template>
|
</FieldLabel>
|
||||||
|
<FieldContent class="flex-row items-center gap-2">
|
||||||
<InputGroupField
|
<InputGroupField
|
||||||
v-model="launchDialog.shortUrl"
|
v-model="launchDialog.shortUrl"
|
||||||
size="sm"
|
size="sm"
|
||||||
style="width: 230px"
|
|
||||||
@click="$event.target.tagName === 'INPUT' && $event.target.select()" />
|
@click="$event.target.tagName === 'INPUT' && $event.target.select()" />
|
||||||
<TooltipWrapper side="right" :content="t('dialog.launch.copy_tooltip')">
|
<TooltipWrapper side="right" :content="t('dialog.launch.copy_tooltip')">
|
||||||
<Button
|
<Button
|
||||||
class="rounded-full ml-1"
|
class="rounded-full"
|
||||||
size="icon-sm"
|
size="icon-sm"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
@click="copyInstanceMessage(launchDialog.shortUrl)"
|
@click="copyInstanceMessage(launchDialog.shortUrl)"
|
||||||
><Copy
|
><Copy
|
||||||
/></Button>
|
/></Button>
|
||||||
</TooltipWrapper>
|
</TooltipWrapper>
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
<el-form-item :label="t('dialog.launch.location')">
|
</Field>
|
||||||
|
<Field>
|
||||||
|
<FieldLabel>{{ t('dialog.launch.location') }}</FieldLabel>
|
||||||
|
<FieldContent class="flex-row items-center gap-2">
|
||||||
<InputGroupField
|
<InputGroupField
|
||||||
v-model="launchDialog.location"
|
v-model="launchDialog.location"
|
||||||
size="sm"
|
size="sm"
|
||||||
style="width: 230px"
|
|
||||||
@click="$event.target.tagName === 'INPUT' && $event.target.select()" />
|
@click="$event.target.tagName === 'INPUT' && $event.target.select()" />
|
||||||
<TooltipWrapper side="right" :content="t('dialog.launch.copy_tooltip')">
|
<TooltipWrapper side="right" :content="t('dialog.launch.copy_tooltip')">
|
||||||
<Button
|
<Button
|
||||||
class="rounded-full ml-1"
|
class="rounded-full"
|
||||||
size="icon-sm"
|
size="icon-sm"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
@click="copyInstanceMessage(launchDialog.location)"
|
@click="copyInstanceMessage(launchDialog.location)"
|
||||||
><Copy
|
><Copy
|
||||||
/></Button>
|
/></Button>
|
||||||
</TooltipWrapper>
|
</TooltipWrapper>
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
</el-form>
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="flex justify-end">
|
<div class="flex justify-end">
|
||||||
<Button
|
<Button
|
||||||
@@ -126,6 +131,7 @@
|
|||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
DropdownMenuTrigger
|
DropdownMenuTrigger
|
||||||
} from '@/components/ui/dropdown-menu';
|
} from '@/components/ui/dropdown-menu';
|
||||||
|
import { Field, FieldContent, FieldGroup, FieldLabel } from '@/components/ui/field';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { ButtonGroup } from '@/components/ui/button-group';
|
import { ButtonGroup } from '@/components/ui/button-group';
|
||||||
import { Copy } from 'lucide-vue-next';
|
import { Copy } from 'lucide-vue-next';
|
||||||
|
|||||||
@@ -7,8 +7,10 @@
|
|||||||
append-to-body>
|
append-to-body>
|
||||||
<el-tabs v-model="newInstanceDialog.selectedTab" @tab-click="newInstanceTabClick">
|
<el-tabs v-model="newInstanceDialog.selectedTab" @tab-click="newInstanceTabClick">
|
||||||
<el-tab-pane name="Normal" :label="t('dialog.new_instance.normal')">
|
<el-tab-pane name="Normal" :label="t('dialog.new_instance.normal')">
|
||||||
<el-form :model="newInstanceDialog" label-width="150px">
|
<FieldGroup class="gap-4">
|
||||||
<el-form-item :label="t('dialog.new_instance.access_type')">
|
<Field>
|
||||||
|
<FieldLabel>{{ t('dialog.new_instance.access_type') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<ToggleGroup
|
<ToggleGroup
|
||||||
type="single"
|
type="single"
|
||||||
required
|
required
|
||||||
@@ -40,10 +42,11 @@
|
|||||||
t('dialog.new_instance.access_type_invite')
|
t('dialog.new_instance.access_type_invite')
|
||||||
}}</ToggleGroupItem>
|
}}</ToggleGroupItem>
|
||||||
</ToggleGroup>
|
</ToggleGroup>
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
<el-form-item
|
</Field>
|
||||||
v-if="newInstanceDialog.accessType === 'group'"
|
<Field v-if="newInstanceDialog.accessType === 'group'">
|
||||||
:label="t('dialog.new_instance.group_access_type')">
|
<FieldLabel>{{ t('dialog.new_instance.group_access_type') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<ToggleGroup
|
<ToggleGroup
|
||||||
type="single"
|
type="single"
|
||||||
required
|
required
|
||||||
@@ -73,14 +76,19 @@
|
|||||||
<ToggleGroupItem
|
<ToggleGroupItem
|
||||||
value="public"
|
value="public"
|
||||||
:disabled="
|
:disabled="
|
||||||
!hasGroupPermission(newInstanceDialog.groupRef, 'group-instance-public-create') ||
|
!hasGroupPermission(
|
||||||
newInstanceDialog.groupRef.privacy === 'private'
|
newInstanceDialog.groupRef,
|
||||||
|
'group-instance-public-create'
|
||||||
|
) || newInstanceDialog.groupRef.privacy === 'private'
|
||||||
"
|
"
|
||||||
>{{ t('dialog.new_instance.group_access_type_public') }}</ToggleGroupItem
|
>{{ t('dialog.new_instance.group_access_type_public') }}</ToggleGroupItem
|
||||||
>
|
>
|
||||||
</ToggleGroup>
|
</ToggleGroup>
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
<el-form-item :label="t('dialog.new_instance.region')">
|
</Field>
|
||||||
|
<Field>
|
||||||
|
<FieldLabel>{{ t('dialog.new_instance.region') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<ToggleGroup
|
<ToggleGroup
|
||||||
type="single"
|
type="single"
|
||||||
required
|
required
|
||||||
@@ -93,38 +101,52 @@
|
|||||||
buildInstance();
|
buildInstance();
|
||||||
}
|
}
|
||||||
">
|
">
|
||||||
<ToggleGroupItem value="US West">{{ t('dialog.new_instance.region_usw') }}</ToggleGroupItem>
|
<ToggleGroupItem value="US West">{{
|
||||||
<ToggleGroupItem value="US East">{{ t('dialog.new_instance.region_use') }}</ToggleGroupItem>
|
t('dialog.new_instance.region_usw')
|
||||||
<ToggleGroupItem value="Europe">{{ t('dialog.new_instance.region_eu') }}</ToggleGroupItem>
|
}}</ToggleGroupItem>
|
||||||
<ToggleGroupItem value="Japan">{{ t('dialog.new_instance.region_jp') }}</ToggleGroupItem>
|
<ToggleGroupItem value="US East">{{
|
||||||
|
t('dialog.new_instance.region_use')
|
||||||
|
}}</ToggleGroupItem>
|
||||||
|
<ToggleGroupItem value="Europe">{{
|
||||||
|
t('dialog.new_instance.region_eu')
|
||||||
|
}}</ToggleGroupItem>
|
||||||
|
<ToggleGroupItem value="Japan">{{
|
||||||
|
t('dialog.new_instance.region_jp')
|
||||||
|
}}</ToggleGroupItem>
|
||||||
</ToggleGroup>
|
</ToggleGroup>
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
<el-form-item
|
</Field>
|
||||||
v-if="newInstanceDialog.accessType === 'group'"
|
<Field v-if="newInstanceDialog.accessType === 'group'">
|
||||||
:label="t('dialog.new_instance.queueEnabled')">
|
<FieldLabel>{{ t('dialog.new_instance.queueEnabled') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<Checkbox v-model="newInstanceDialog.queueEnabled" @update:modelValue="buildInstance" />
|
<Checkbox v-model="newInstanceDialog.queueEnabled" @update:modelValue="buildInstance" />
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
<el-form-item
|
</Field>
|
||||||
v-if="newInstanceDialog.accessType === 'group'"
|
<Field v-if="newInstanceDialog.accessType === 'group'">
|
||||||
:label="t('dialog.new_instance.ageGate')">
|
<FieldLabel>{{ t('dialog.new_instance.ageGate') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
v-model="newInstanceDialog.ageGate"
|
v-model="newInstanceDialog.ageGate"
|
||||||
:disabled="
|
:disabled="
|
||||||
!hasGroupPermission(newInstanceDialog.groupRef, 'group-instance-age-gated-create')
|
!hasGroupPermission(newInstanceDialog.groupRef, 'group-instance-age-gated-create')
|
||||||
"
|
"
|
||||||
@update:modelValue="buildInstance" />
|
@update:modelValue="buildInstance" />
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
<el-form-item :label="t('dialog.new_instance.display_name')">
|
</Field>
|
||||||
|
<Field>
|
||||||
|
<FieldLabel>{{ t('dialog.new_instance.display_name') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<InputGroupField
|
<InputGroupField
|
||||||
:disabled="!isLocalUserVrcPlusSupporter"
|
:disabled="!isLocalUserVrcPlusSupporter"
|
||||||
v-model="newInstanceDialog.displayName"
|
v-model="newInstanceDialog.displayName"
|
||||||
size="sm"
|
size="sm"
|
||||||
@click="$event.target.tagName === 'INPUT' && $event.target.select()"
|
@click="$event.target.tagName === 'INPUT' && $event.target.select()"
|
||||||
@change="buildInstance" />
|
@change="buildInstance" />
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
<el-form-item
|
</Field>
|
||||||
v-if="newInstanceDialog.accessType === 'group'"
|
<Field v-if="newInstanceDialog.accessType === 'group'">
|
||||||
:label="t('dialog.new_instance.group_id')">
|
<FieldLabel>{{ t('dialog.new_instance.group_id') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<VirtualCombobox
|
<VirtualCombobox
|
||||||
v-model="newInstanceDialog.groupId"
|
v-model="newInstanceDialog.groupId"
|
||||||
:groups="normalGroupPickerGroups"
|
:groups="normalGroupPickerGroups"
|
||||||
@@ -142,16 +164,20 @@
|
|||||||
<div class="detail">
|
<div class="detail">
|
||||||
<span class="name" v-text="item.label"></span>
|
<span class="name" v-text="item.label"></span>
|
||||||
</div>
|
</div>
|
||||||
<CheckIcon :class="['ml-auto size-4', selected ? 'opacity-100' : 'opacity-0']" />
|
<CheckIcon
|
||||||
|
:class="['ml-auto size-4', selected ? 'opacity-100' : 'opacity-0']" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</VirtualCombobox>
|
</VirtualCombobox>
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
<el-form-item
|
</Field>
|
||||||
|
<Field
|
||||||
v-if="
|
v-if="
|
||||||
newInstanceDialog.accessType === 'group' && newInstanceDialog.groupAccessType === 'members'
|
newInstanceDialog.accessType === 'group' && newInstanceDialog.groupAccessType === 'members'
|
||||||
"
|
"
|
||||||
:label="t('dialog.new_instance.roles')">
|
class="items-start">
|
||||||
|
<FieldLabel>{{ t('dialog.new_instance.roles') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<Select
|
<Select
|
||||||
multiple
|
multiple
|
||||||
:model-value="Array.isArray(newInstanceDialog.roleIds) ? newInstanceDialog.roleIds : []"
|
:model-value="Array.isArray(newInstanceDialog.roleIds) ? newInstanceDialog.roleIds : []"
|
||||||
@@ -174,24 +200,33 @@
|
|||||||
</SelectGroup>
|
</SelectGroup>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
|
</Field>
|
||||||
<template v-if="newInstanceDialog.instanceCreated">
|
<template v-if="newInstanceDialog.instanceCreated">
|
||||||
<el-form-item :label="t('dialog.new_instance.location')">
|
<Field>
|
||||||
|
<FieldLabel>{{ t('dialog.new_instance.location') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<InputGroupField
|
<InputGroupField
|
||||||
v-model="newInstanceDialog.location"
|
v-model="newInstanceDialog.location"
|
||||||
size="sm"
|
size="sm"
|
||||||
readonly
|
readonly
|
||||||
@click="$event.target.tagName === 'INPUT' && $event.target.select()" />
|
@click="$event.target.tagName === 'INPUT' && $event.target.select()" />
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
<el-form-item :label="t('dialog.new_instance.url')">
|
</Field>
|
||||||
|
<Field>
|
||||||
|
<FieldLabel>{{ t('dialog.new_instance.url') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<InputGroupField v-model="newInstanceDialog.url" size="sm" readonly />
|
<InputGroupField v-model="newInstanceDialog.url" size="sm" readonly />
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
|
</Field>
|
||||||
</template>
|
</template>
|
||||||
</el-form>
|
</FieldGroup>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane name="Legacy" :label="t('dialog.new_instance.legacy')">
|
<el-tab-pane name="Legacy" :label="t('dialog.new_instance.legacy')">
|
||||||
<el-form :model="newInstanceDialog" label-width="150px">
|
<FieldGroup class="gap-4">
|
||||||
<el-form-item :label="t('dialog.new_instance.access_type')">
|
<Field>
|
||||||
|
<FieldLabel>{{ t('dialog.new_instance.access_type') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<ToggleGroup
|
<ToggleGroup
|
||||||
type="single"
|
type="single"
|
||||||
required
|
required
|
||||||
@@ -223,10 +258,11 @@
|
|||||||
t('dialog.new_instance.access_type_invite')
|
t('dialog.new_instance.access_type_invite')
|
||||||
}}</ToggleGroupItem>
|
}}</ToggleGroupItem>
|
||||||
</ToggleGroup>
|
</ToggleGroup>
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
<el-form-item
|
</Field>
|
||||||
v-if="newInstanceDialog.accessType === 'group'"
|
<Field v-if="newInstanceDialog.accessType === 'group'">
|
||||||
:label="t('dialog.new_instance.group_access_type')">
|
<FieldLabel>{{ t('dialog.new_instance.group_access_type') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<ToggleGroup
|
<ToggleGroup
|
||||||
type="single"
|
type="single"
|
||||||
required
|
required
|
||||||
@@ -249,8 +285,11 @@
|
|||||||
t('dialog.new_instance.group_access_type_public')
|
t('dialog.new_instance.group_access_type_public')
|
||||||
}}</ToggleGroupItem>
|
}}</ToggleGroupItem>
|
||||||
</ToggleGroup>
|
</ToggleGroup>
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
<el-form-item :label="t('dialog.new_instance.region')">
|
</Field>
|
||||||
|
<Field>
|
||||||
|
<FieldLabel>{{ t('dialog.new_instance.region') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<ToggleGroup
|
<ToggleGroup
|
||||||
type="single"
|
type="single"
|
||||||
required
|
required
|
||||||
@@ -263,38 +302,56 @@
|
|||||||
buildLegacyInstance();
|
buildLegacyInstance();
|
||||||
}
|
}
|
||||||
">
|
">
|
||||||
<ToggleGroupItem value="US West">{{ t('dialog.new_instance.region_usw') }}</ToggleGroupItem>
|
<ToggleGroupItem value="US West">{{
|
||||||
<ToggleGroupItem value="US East">{{ t('dialog.new_instance.region_use') }}</ToggleGroupItem>
|
t('dialog.new_instance.region_usw')
|
||||||
<ToggleGroupItem value="Europe">{{ t('dialog.new_instance.region_eu') }}</ToggleGroupItem>
|
}}</ToggleGroupItem>
|
||||||
<ToggleGroupItem value="Japan">{{ t('dialog.new_instance.region_jp') }}</ToggleGroupItem>
|
<ToggleGroupItem value="US East">{{
|
||||||
|
t('dialog.new_instance.region_use')
|
||||||
|
}}</ToggleGroupItem>
|
||||||
|
<ToggleGroupItem value="Europe">{{
|
||||||
|
t('dialog.new_instance.region_eu')
|
||||||
|
}}</ToggleGroupItem>
|
||||||
|
<ToggleGroupItem value="Japan">{{
|
||||||
|
t('dialog.new_instance.region_jp')
|
||||||
|
}}</ToggleGroupItem>
|
||||||
</ToggleGroup>
|
</ToggleGroup>
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
<el-form-item
|
</Field>
|
||||||
v-if="newInstanceDialog.accessType === 'group'"
|
<Field v-if="newInstanceDialog.accessType === 'group'">
|
||||||
:label="t('dialog.new_instance.ageGate')">
|
<FieldLabel>{{ t('dialog.new_instance.ageGate') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<Checkbox v-model="newInstanceDialog.ageGate" @update:modelValue="buildInstance" />
|
<Checkbox v-model="newInstanceDialog.ageGate" @update:modelValue="buildInstance" />
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
<el-form-item :label="t('dialog.new_instance.world_id')">
|
</Field>
|
||||||
|
<Field>
|
||||||
|
<FieldLabel>{{ t('dialog.new_instance.world_id') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<InputGroupField
|
<InputGroupField
|
||||||
v-model="newInstanceDialog.worldId"
|
v-model="newInstanceDialog.worldId"
|
||||||
size="sm"
|
size="sm"
|
||||||
@click="$event.target.tagName === 'INPUT' && $event.target.select()"
|
@click="$event.target.tagName === 'INPUT' && $event.target.select()"
|
||||||
@change="buildLegacyInstance" />
|
@change="buildLegacyInstance" />
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
<el-form-item :label="t('dialog.new_instance.instance_id')">
|
</Field>
|
||||||
|
<Field>
|
||||||
|
<FieldLabel>{{ t('dialog.new_instance.instance_id') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<InputGroupField
|
<InputGroupField
|
||||||
v-model="newInstanceDialog.instanceName"
|
v-model="newInstanceDialog.instanceName"
|
||||||
:placeholder="t('dialog.new_instance.instance_id_placeholder')"
|
:placeholder="t('dialog.new_instance.instance_id_placeholder')"
|
||||||
size="sm"
|
size="sm"
|
||||||
@change="buildLegacyInstance" />
|
@change="buildLegacyInstance" />
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
<el-form-item
|
</Field>
|
||||||
|
<Field
|
||||||
v-if="
|
v-if="
|
||||||
newInstanceDialog.selectedTab === 'Legacy' &&
|
newInstanceDialog.selectedTab === 'Legacy' &&
|
||||||
newInstanceDialog.accessType !== 'public' &&
|
newInstanceDialog.accessType !== 'public' &&
|
||||||
newInstanceDialog.accessType !== 'group'
|
newInstanceDialog.accessType !== 'group'
|
||||||
"
|
"
|
||||||
:label="t('dialog.new_instance.instance_creator')">
|
class="items-start">
|
||||||
|
<FieldLabel>{{ t('dialog.new_instance.instance_creator') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<VirtualCombobox
|
<VirtualCombobox
|
||||||
v-model="newInstanceDialog.userId"
|
v-model="newInstanceDialog.userId"
|
||||||
:groups="creatorPickerGroups"
|
:groups="creatorPickerGroups"
|
||||||
@@ -321,14 +378,16 @@
|
|||||||
<span v-text="item.label"></span>
|
<span v-text="item.label"></span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<CheckIcon :class="['ml-auto size-4', selected ? 'opacity-100' : 'opacity-0']" />
|
<CheckIcon
|
||||||
|
:class="['ml-auto size-4', selected ? 'opacity-100' : 'opacity-0']" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</VirtualCombobox>
|
</VirtualCombobox>
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
<el-form-item
|
</Field>
|
||||||
v-if="newInstanceDialog.accessType === 'group'"
|
<Field v-if="newInstanceDialog.accessType === 'group'">
|
||||||
:label="t('dialog.new_instance.group_id')">
|
<FieldLabel>{{ t('dialog.new_instance.group_id') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<VirtualCombobox
|
<VirtualCombobox
|
||||||
v-model="newInstanceDialog.groupId"
|
v-model="newInstanceDialog.groupId"
|
||||||
:groups="legacyGroupPickerGroups"
|
:groups="legacyGroupPickerGroups"
|
||||||
@@ -346,22 +405,30 @@
|
|||||||
<div class="detail">
|
<div class="detail">
|
||||||
<span class="name" v-text="item.label"></span>
|
<span class="name" v-text="item.label"></span>
|
||||||
</div>
|
</div>
|
||||||
<CheckIcon :class="['ml-auto size-4', selected ? 'opacity-100' : 'opacity-0']" />
|
<CheckIcon
|
||||||
|
:class="['ml-auto size-4', selected ? 'opacity-100' : 'opacity-0']" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</VirtualCombobox>
|
</VirtualCombobox>
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
<el-form-item :label="t('dialog.new_instance.location')">
|
</Field>
|
||||||
|
<Field>
|
||||||
|
<FieldLabel>{{ t('dialog.new_instance.location') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<InputGroupField
|
<InputGroupField
|
||||||
v-model="newInstanceDialog.location"
|
v-model="newInstanceDialog.location"
|
||||||
size="sm"
|
size="sm"
|
||||||
readonly
|
readonly
|
||||||
@click="$event.target.tagName === 'INPUT' && $event.target.select()" />
|
@click="$event.target.tagName === 'INPUT' && $event.target.select()" />
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
<el-form-item :label="t('dialog.new_instance.url')">
|
</Field>
|
||||||
|
<Field>
|
||||||
|
<FieldLabel>{{ t('dialog.new_instance.url') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<InputGroupField v-model="newInstanceDialog.url" size="sm" readonly />
|
<InputGroupField v-model="newInstanceDialog.url" size="sm" readonly />
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
</el-form>
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
<template v-if="newInstanceDialog.selectedTab === 'Normal'" #footer>
|
<template v-if="newInstanceDialog.selectedTab === 'Normal'" #footer>
|
||||||
@@ -441,11 +508,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { Field, FieldContent, FieldGroup, FieldLabel } from '@/components/ui/field';
|
||||||
import { computed, nextTick, ref, watch } from 'vue';
|
import { computed, nextTick, ref, watch } from 'vue';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { InputGroupField } from '@/components/ui/input-group';
|
|
||||||
import { Checkbox } from '@/components/ui/checkbox';
|
|
||||||
import { Check as CheckIcon } from 'lucide-vue-next';
|
import { Check as CheckIcon } from 'lucide-vue-next';
|
||||||
|
import { Checkbox } from '@/components/ui/checkbox';
|
||||||
|
import { InputGroupField } from '@/components/ui/input-group';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { toast } from 'vue-sonner';
|
import { toast } from 'vue-sonner';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|||||||
@@ -766,8 +766,8 @@
|
|||||||
import { computed, defineAsyncComponent, nextTick, ref, watch } from 'vue';
|
import { computed, defineAsyncComponent, nextTick, ref, watch } from 'vue';
|
||||||
import { Ellipsis, RefreshCcw, Star, Trash2 } from 'lucide-vue-next';
|
import { Ellipsis, RefreshCcw, Star, Trash2 } from 'lucide-vue-next';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { InputGroupTextareaField } from '@/components/ui/input-group';
|
|
||||||
import { ElMessageBox } from 'element-plus';
|
import { ElMessageBox } from 'element-plus';
|
||||||
|
import { InputGroupTextareaField } from '@/components/ui/input-group';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { toast } from 'vue-sonner';
|
import { toast } from 'vue-sonner';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<script setup>
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
import { fieldVariants } from '.';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
class: { type: null, required: false },
|
||||||
|
orientation: { type: null, required: false }
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
role="group"
|
||||||
|
data-slot="field"
|
||||||
|
:data-orientation="orientation"
|
||||||
|
:class="cn(fieldVariants({ orientation }), props.class)">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<script setup>
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
class: { type: null, required: false }
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
data-slot="field-content"
|
||||||
|
:class="cn('group/field-content flex flex-1 flex-col gap-1.5 leading-snug', props.class)">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<script setup>
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
class: { type: null, required: false }
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<p
|
||||||
|
data-slot="field-description"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'text-muted-foreground text-sm leading-normal font-normal group-has-[[data-orientation=horizontal]]/field:text-balance',
|
||||||
|
'last:mt-0 nth-last-2:-mt-1 [[data-variant=legend]+&]:-mt-1.5',
|
||||||
|
'[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4',
|
||||||
|
props.class
|
||||||
|
)
|
||||||
|
">
|
||||||
|
<slot />
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
<script setup>
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
import { computed } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
class: { type: null, required: false },
|
||||||
|
errors: { type: Array, required: false }
|
||||||
|
});
|
||||||
|
|
||||||
|
const content = computed(() => {
|
||||||
|
if (!props.errors || props.errors.length === 0) return null;
|
||||||
|
|
||||||
|
const uniqueErrors = [
|
||||||
|
...new Map(
|
||||||
|
props.errors.filter(Boolean).map((error) => {
|
||||||
|
const message = typeof error === 'string' ? error : error?.message;
|
||||||
|
return [message, error];
|
||||||
|
})
|
||||||
|
).values()
|
||||||
|
];
|
||||||
|
|
||||||
|
if (uniqueErrors.length === 1 && uniqueErrors[0]) {
|
||||||
|
return typeof uniqueErrors[0] === 'string' ? uniqueErrors[0] : uniqueErrors[0].message;
|
||||||
|
}
|
||||||
|
|
||||||
|
return uniqueErrors.map((error) => (typeof error === 'string' ? error : error?.message));
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
v-if="$slots.default || content"
|
||||||
|
role="alert"
|
||||||
|
data-slot="field-error"
|
||||||
|
:class="cn('text-destructive text-sm font-normal', props.class)">
|
||||||
|
<slot v-if="$slots.default" />
|
||||||
|
|
||||||
|
<template v-else-if="typeof content === 'string'">
|
||||||
|
{{ content }}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<ul v-else-if="Array.isArray(content)" class="ml-4 flex list-disc flex-col gap-1">
|
||||||
|
<li v-for="(error, index) in content" :key="index">
|
||||||
|
{{ error }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<script setup>
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
class: { type: null, required: false }
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
data-slot="field-group"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'group/field-group @container/field-group flex w-full flex-col gap-7 data-[slot=checkbox-group]:gap-3 [&>[data-slot=field-group]]:gap-4',
|
||||||
|
props.class
|
||||||
|
)
|
||||||
|
">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<script setup>
|
||||||
|
import { Label } from '@/components/ui/label';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
class: { type: null, required: false }
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Label
|
||||||
|
data-slot="field-label"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50',
|
||||||
|
'has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border [&>*]:data-[slot=field]:p-4',
|
||||||
|
'has-data-[state=checked]:bg-primary/5 has-data-[state=checked]:border-primary dark:has-data-[state=checked]:bg-primary/10',
|
||||||
|
props.class
|
||||||
|
)
|
||||||
|
">
|
||||||
|
<slot />
|
||||||
|
</Label>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<script setup>
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
class: { type: null, required: false },
|
||||||
|
variant: { type: String, required: false }
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<legend
|
||||||
|
data-slot="field-legend"
|
||||||
|
:data-variant="variant"
|
||||||
|
:class="cn('mb-3 font-medium', 'data-[variant=legend]:text-base', 'data-[variant=label]:text-sm', props.class)">
|
||||||
|
<slot />
|
||||||
|
</legend>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<script setup>
|
||||||
|
import { Separator } from '@/components/ui/separator';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
class: { type: null, required: false }
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
data-slot="field-separator"
|
||||||
|
:data-content="!!$slots.default"
|
||||||
|
:class="cn('relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2', props.class)">
|
||||||
|
<Separator class="absolute inset-0 top-1/2" />
|
||||||
|
<span
|
||||||
|
v-if="$slots.default"
|
||||||
|
class="bg-background text-muted-foreground relative mx-auto block w-fit px-2"
|
||||||
|
data-slot="field-separator-content">
|
||||||
|
<slot />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<script setup>
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
class: { type: null, required: false }
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<fieldset
|
||||||
|
data-slot="field-set"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'flex flex-col gap-6',
|
||||||
|
'has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3',
|
||||||
|
props.class
|
||||||
|
)
|
||||||
|
">
|
||||||
|
<slot />
|
||||||
|
</fieldset>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<script setup>
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
class: { type: null, required: false }
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
data-slot="field-label"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'flex w-fit items-center gap-2 text-sm leading-snug font-medium group-data-[disabled=true]/field:opacity-50',
|
||||||
|
props.class
|
||||||
|
)
|
||||||
|
">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { cva } from 'class-variance-authority';
|
||||||
|
|
||||||
|
export const fieldVariants = cva(
|
||||||
|
'group/field flex w-full gap-3 data-[invalid=true]:text-destructive',
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
orientation: {
|
||||||
|
vertical: ['flex-col [&>*]:w-full [&>.sr-only]:w-auto'],
|
||||||
|
horizontal: [
|
||||||
|
'flex-row items-center',
|
||||||
|
'[&>[data-slot=field-label]]:flex-auto',
|
||||||
|
'has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px'
|
||||||
|
],
|
||||||
|
responsive: [
|
||||||
|
'flex-col [&>*]:w-full [&>.sr-only]:w-auto @md/field-group:flex-row @md/field-group:items-center @md/field-group:[&>*]:w-auto',
|
||||||
|
'@md/field-group:[&>[data-slot=field-label]]:flex-auto',
|
||||||
|
'@md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
orientation: 'vertical'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export { default as Field } from './Field.vue';
|
||||||
|
export { default as FieldContent } from './FieldContent.vue';
|
||||||
|
export { default as FieldDescription } from './FieldDescription.vue';
|
||||||
|
export { default as FieldError } from './FieldError.vue';
|
||||||
|
export { default as FieldGroup } from './FieldGroup.vue';
|
||||||
|
export { default as FieldLabel } from './FieldLabel.vue';
|
||||||
|
export { default as FieldLegend } from './FieldLegend.vue';
|
||||||
|
export { default as FieldSeparator } from './FieldSeparator.vue';
|
||||||
|
export { default as FieldSet } from './FieldSet.vue';
|
||||||
|
export { default as FieldTitle } from './FieldTitle.vue';
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<script setup>
|
||||||
|
import { Slot } from 'reka-ui';
|
||||||
|
|
||||||
|
import { useFormField } from './useFormField';
|
||||||
|
|
||||||
|
const { error, formItemId, formDescriptionId, formMessageId } = useFormField();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Slot
|
||||||
|
:id="formItemId"
|
||||||
|
data-slot="form-control"
|
||||||
|
:aria-describedby="!error ? `${formDescriptionId}` : `${formDescriptionId} ${formMessageId}`"
|
||||||
|
:aria-invalid="!!error">
|
||||||
|
<slot />
|
||||||
|
</Slot>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<script setup>
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
import { useFormField } from './useFormField';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
class: { type: null, required: false }
|
||||||
|
});
|
||||||
|
|
||||||
|
const { formDescriptionId } = useFormField();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<p :id="formDescriptionId" data-slot="form-description" :class="cn('text-muted-foreground text-sm', props.class)">
|
||||||
|
<slot />
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<script setup>
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
import { provide } from 'vue';
|
||||||
|
import { useId } from 'reka-ui';
|
||||||
|
|
||||||
|
import { FORM_ITEM_INJECTION_KEY } from './injectionKeys';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
class: { type: null, required: false }
|
||||||
|
});
|
||||||
|
|
||||||
|
const id = useId();
|
||||||
|
provide(FORM_ITEM_INJECTION_KEY, id);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div data-slot="form-item" :class="cn('grid gap-2', props.class)">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<script setup>
|
||||||
|
import { Label } from '@/components/ui/label';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
import { useFormField } from './useFormField';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
for: { type: String, required: false },
|
||||||
|
asChild: { type: Boolean, required: false },
|
||||||
|
as: { type: null, required: false },
|
||||||
|
class: { type: null, required: false }
|
||||||
|
});
|
||||||
|
|
||||||
|
const { error, formItemId } = useFormField();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Label
|
||||||
|
data-slot="form-label"
|
||||||
|
:data-error="!!error"
|
||||||
|
:class="cn('data-[error=true]:text-destructive', props.class)"
|
||||||
|
:for="formItemId">
|
||||||
|
<slot />
|
||||||
|
</Label>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ErrorMessage } from 'vee-validate';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
import { toValue } from 'vue';
|
||||||
|
|
||||||
|
import { useFormField } from './useFormField';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
class: { type: null, required: false }
|
||||||
|
});
|
||||||
|
|
||||||
|
const { name, formMessageId } = useFormField();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ErrorMessage
|
||||||
|
:id="formMessageId"
|
||||||
|
data-slot="form-message"
|
||||||
|
as="p"
|
||||||
|
:name="toValue(name)"
|
||||||
|
:class="cn('text-destructive text-sm', props.class)" />
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
export { default as FormControl } from './FormControl.vue';
|
||||||
|
export { default as FormDescription } from './FormDescription.vue';
|
||||||
|
export { default as FormItem } from './FormItem.vue';
|
||||||
|
export { default as FormLabel } from './FormLabel.vue';
|
||||||
|
export { default as FormMessage } from './FormMessage.vue';
|
||||||
|
export { FORM_ITEM_INJECTION_KEY } from './injectionKeys';
|
||||||
|
export {
|
||||||
|
Form,
|
||||||
|
Field as FormField,
|
||||||
|
FieldArray as FormFieldArray
|
||||||
|
} from 'vee-validate';
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export const FORM_ITEM_INJECTION_KEY = Symbol();
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { computed, inject } from 'vue';
|
||||||
|
import { FieldContextKey } from 'vee-validate';
|
||||||
|
|
||||||
|
import { FORM_ITEM_INJECTION_KEY } from './injectionKeys';
|
||||||
|
|
||||||
|
export function useFormField() {
|
||||||
|
const fieldContext = inject(FieldContextKey);
|
||||||
|
const fieldItemContext = inject(FORM_ITEM_INJECTION_KEY);
|
||||||
|
|
||||||
|
if (!fieldContext)
|
||||||
|
throw new Error('useFormField should be used within <FormField>');
|
||||||
|
|
||||||
|
const { name, errorMessage: error, meta } = fieldContext;
|
||||||
|
const id = fieldItemContext;
|
||||||
|
|
||||||
|
const fieldState = {
|
||||||
|
valid: computed(() => meta.valid),
|
||||||
|
isDirty: computed(() => meta.dirty),
|
||||||
|
isTouched: computed(() => meta.touched),
|
||||||
|
error
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
formItemId: `${id}-form-item`,
|
||||||
|
formDescriptionId: `${id}-form-item-description`,
|
||||||
|
formMessageId: `${id}-form-item-message`,
|
||||||
|
...fieldState
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Button
|
<Button
|
||||||
|
type="button"
|
||||||
:data-size="props.size"
|
:data-size="props.size"
|
||||||
:variant="props.variant"
|
:variant="props.variant"
|
||||||
:class="cn(inputGroupButtonVariants({ size: props.size }), props.class)">
|
:class="cn(inputGroupButtonVariants({ size: props.size }), props.class)">
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<script setup>
|
||||||
|
import { Label } from 'reka-ui';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
import { reactiveOmit } from '@vueuse/core';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
for: { type: String, required: false },
|
||||||
|
asChild: { type: Boolean, required: false },
|
||||||
|
as: { type: null, required: false },
|
||||||
|
class: { type: null, required: false }
|
||||||
|
});
|
||||||
|
|
||||||
|
const delegatedProps = reactiveOmit(props, 'class');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Label
|
||||||
|
data-slot="label"
|
||||||
|
v-bind="delegatedProps"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50',
|
||||||
|
props.class
|
||||||
|
)
|
||||||
|
">
|
||||||
|
<slot />
|
||||||
|
</Label>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as Label } from './Label.vue';
|
||||||
+1
-15
@@ -42,21 +42,7 @@ export const useAuthStore = defineStore('Auth', () => {
|
|||||||
endpoint: '',
|
endpoint: '',
|
||||||
websocket: '',
|
websocket: '',
|
||||||
saveCredentials: false,
|
saveCredentials: false,
|
||||||
lastUserLoggedIn: '',
|
lastUserLoggedIn: ''
|
||||||
rules: {
|
|
||||||
username: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
trigger: 'blur'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
password: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
trigger: 'blur'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const enablePrimaryPasswordDialog = ref({
|
const enablePrimaryPasswordDialog = ref({
|
||||||
|
|||||||
@@ -51,8 +51,10 @@
|
|||||||
<p class="mutual-graph__force-description">
|
<p class="mutual-graph__force-description">
|
||||||
{{ t('view.charts.mutual_friend.force_dialog.description') }}
|
{{ t('view.charts.mutual_friend.force_dialog.description') }}
|
||||||
</p>
|
</p>
|
||||||
<el-form label-position="top" size="small" class="mutual-graph__force-form">
|
<FieldGroup class="mutual-graph__force-form">
|
||||||
<el-form-item :label="t('view.charts.mutual_friend.force_dialog.repulsion')">
|
<Field>
|
||||||
|
<FieldLabel>{{ t('view.charts.mutual_friend.force_dialog.repulsion') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<NumberField
|
<NumberField
|
||||||
v-model="forceForm.repulsion"
|
v-model="forceForm.repulsion"
|
||||||
:step="1"
|
:step="1"
|
||||||
@@ -62,11 +64,14 @@
|
|||||||
<NumberFieldInput />
|
<NumberFieldInput />
|
||||||
</NumberFieldContent>
|
</NumberFieldContent>
|
||||||
</NumberField>
|
</NumberField>
|
||||||
<div class="mutual-graph__helper">
|
<FieldDescription class="mutual-graph__helper">
|
||||||
{{ t('view.charts.mutual_friend.force_dialog.repulsion_help') }}
|
{{ t('view.charts.mutual_friend.force_dialog.repulsion_help') }}
|
||||||
</div>
|
</FieldDescription>
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
<el-form-item :label="t('view.charts.mutual_friend.force_dialog.edge_length_min')">
|
</Field>
|
||||||
|
<Field>
|
||||||
|
<FieldLabel>{{ t('view.charts.mutual_friend.force_dialog.edge_length_min') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<NumberField
|
<NumberField
|
||||||
v-model="forceForm.edgeLengthMin"
|
v-model="forceForm.edgeLengthMin"
|
||||||
:step="1"
|
:step="1"
|
||||||
@@ -76,11 +81,14 @@
|
|||||||
<NumberFieldInput />
|
<NumberFieldInput />
|
||||||
</NumberFieldContent>
|
</NumberFieldContent>
|
||||||
</NumberField>
|
</NumberField>
|
||||||
<div class="mutual-graph__helper">
|
<FieldDescription class="mutual-graph__helper">
|
||||||
{{ t('view.charts.mutual_friend.force_dialog.edge_length_min_help') }}
|
{{ t('view.charts.mutual_friend.force_dialog.edge_length_min_help') }}
|
||||||
</div>
|
</FieldDescription>
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
<el-form-item :label="t('view.charts.mutual_friend.force_dialog.edge_length_max')">
|
</Field>
|
||||||
|
<Field>
|
||||||
|
<FieldLabel>{{ t('view.charts.mutual_friend.force_dialog.edge_length_max') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<NumberField
|
<NumberField
|
||||||
v-model="forceForm.edgeLengthMax"
|
v-model="forceForm.edgeLengthMax"
|
||||||
:step="1"
|
:step="1"
|
||||||
@@ -90,11 +98,14 @@
|
|||||||
<NumberFieldInput />
|
<NumberFieldInput />
|
||||||
</NumberFieldContent>
|
</NumberFieldContent>
|
||||||
</NumberField>
|
</NumberField>
|
||||||
<div class="mutual-graph__helper">
|
<FieldDescription class="mutual-graph__helper">
|
||||||
{{ t('view.charts.mutual_friend.force_dialog.edge_length_max_help') }}
|
{{ t('view.charts.mutual_friend.force_dialog.edge_length_max_help') }}
|
||||||
</div>
|
</FieldDescription>
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
<el-form-item :label="t('view.charts.mutual_friend.force_dialog.gravity')">
|
</Field>
|
||||||
|
<Field>
|
||||||
|
<FieldLabel>{{ t('view.charts.mutual_friend.force_dialog.gravity') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<NumberField
|
<NumberField
|
||||||
v-model="forceForm.gravity"
|
v-model="forceForm.gravity"
|
||||||
:max="1"
|
:max="1"
|
||||||
@@ -105,11 +116,12 @@
|
|||||||
<NumberFieldInput />
|
<NumberFieldInput />
|
||||||
</NumberFieldContent>
|
</NumberFieldContent>
|
||||||
</NumberField>
|
</NumberField>
|
||||||
<div class="mutual-graph__helper">
|
<FieldDescription class="mutual-graph__helper">
|
||||||
{{ t('view.charts.mutual_friend.force_dialog.gravity_help') }}
|
{{ t('view.charts.mutual_friend.force_dialog.gravity_help') }}
|
||||||
</div>
|
</FieldDescription>
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
</el-form>
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="mutual-graph__dialog-footer">
|
<div class="mutual-graph__dialog-footer">
|
||||||
@@ -127,6 +139,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue';
|
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue';
|
||||||
|
import { Field, FieldContent, FieldDescription, FieldGroup, FieldLabel } from '@/components/ui/field';
|
||||||
import { NumberField, NumberFieldContent, NumberFieldInput } from '@/components/ui/number-field';
|
import { NumberField, NumberFieldContent, NumberFieldInput } from '@/components/ui/number-field';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { ElMessageBox } from 'element-plus';
|
import { ElMessageBox } from 'element-plus';
|
||||||
|
|||||||
+134
-54
@@ -16,71 +16,101 @@
|
|||||||
<div class="x-login-form-container">
|
<div class="x-login-form-container">
|
||||||
<div>
|
<div>
|
||||||
<h2 style="font-weight: bold; text-align: center; margin: 0">{{ t('view.login.login') }}</h2>
|
<h2 style="font-weight: bold; text-align: center; margin: 0">{{ t('view.login.login') }}</h2>
|
||||||
<el-form
|
<form id="login-form" @submit.prevent="onSubmit">
|
||||||
ref="loginFormRef"
|
<FieldGroup class="gap-3">
|
||||||
:model="loginForm"
|
<VeeField v-slot="{ field, errors }" name="username">
|
||||||
:rules="loginForm.rules"
|
<Field :data-invalid="!!errors.length">
|
||||||
@submit.prevent="handleLogin()">
|
<FieldLabel for="login-form-username">
|
||||||
<el-form-item
|
{{ t('view.login.field.username') }}
|
||||||
:label="t('view.login.field.username')"
|
</FieldLabel>
|
||||||
prop="username"
|
<FieldContent>
|
||||||
required
|
|
||||||
style="display: block">
|
|
||||||
<InputGroupField
|
<InputGroupField
|
||||||
v-model="loginForm.username"
|
id="login-form-username"
|
||||||
|
:model-value="field.value"
|
||||||
name="username"
|
name="username"
|
||||||
:placeholder="t('view.login.field.username')"
|
:placeholder="t('view.login.field.username')"
|
||||||
clearable />
|
:aria-invalid="!!errors.length"
|
||||||
</el-form-item>
|
clearable
|
||||||
<el-form-item
|
@update:modelValue="field.onChange"
|
||||||
:label="t('view.login.field.password')"
|
@blur="field.onBlur" />
|
||||||
prop="password"
|
<FieldError v-if="errors.length" :errors="errors" />
|
||||||
required
|
</FieldContent>
|
||||||
style="display: block; margin-top: 10px">
|
</Field>
|
||||||
|
</VeeField>
|
||||||
|
<VeeField v-slot="{ field, errors }" name="password">
|
||||||
|
<Field :data-invalid="!!errors.length">
|
||||||
|
<FieldLabel for="login-form-password">
|
||||||
|
{{ t('view.login.field.password') }}
|
||||||
|
</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<InputGroupField
|
<InputGroupField
|
||||||
v-model="loginForm.password"
|
id="login-form-password"
|
||||||
|
:model-value="field.value"
|
||||||
type="password"
|
type="password"
|
||||||
name="password"
|
name="password"
|
||||||
:placeholder="t('view.login.field.password')"
|
:placeholder="t('view.login.field.password')"
|
||||||
|
:aria-invalid="!!errors.length"
|
||||||
clearable
|
clearable
|
||||||
show-password />
|
show-password
|
||||||
</el-form-item>
|
@update:modelValue="field.onChange"
|
||||||
|
@blur="field.onBlur" />
|
||||||
|
<FieldError v-if="errors.length" :errors="errors" />
|
||||||
|
</FieldContent>
|
||||||
|
</Field>
|
||||||
|
</VeeField>
|
||||||
|
</FieldGroup>
|
||||||
<label class="inline-flex items-center gap-2 mr-2">
|
<label class="inline-flex items-center gap-2 mr-2">
|
||||||
<Checkbox v-model="loginForm.saveCredentials" />
|
<Checkbox v-model="loginForm.saveCredentials" />
|
||||||
<span>{{ t('view.login.field.saveCredentials') }}</span>
|
<span>{{ t('view.login.field.saveCredentials') }}</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="inline-flex items-center gap-2" style="margin-top: 10px">
|
<label class="inline-flex items-center gap-2" style="margin-top: 10px">
|
||||||
<Checkbox v-model="enableCustomEndpoint" @update:modelValue="toggleCustomEndpoint" />
|
<Checkbox v-model="enableCustomEndpoint" @update:modelValue="handleCustomEndpointToggle" />
|
||||||
<span>{{ t('view.login.field.devEndpoint') }}</span>
|
<span>{{ t('view.login.field.devEndpoint') }}</span>
|
||||||
</label>
|
</label>
|
||||||
<el-form-item
|
<FieldGroup v-if="enableCustomEndpoint" class="mt-3 gap-3">
|
||||||
v-if="enableCustomEndpoint"
|
<VeeField v-slot="{ field, errors }" name="endpoint">
|
||||||
:label="t('view.login.field.endpoint')"
|
<Field :data-invalid="!!errors.length">
|
||||||
prop="endpoint"
|
<FieldLabel for="login-form-endpoint">
|
||||||
style="margin-top: 10px">
|
{{ t('view.login.field.endpoint') }}
|
||||||
|
</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<InputGroupField
|
<InputGroupField
|
||||||
v-model="loginForm.endpoint"
|
id="login-form-endpoint"
|
||||||
|
:model-value="field.value"
|
||||||
name="endpoint"
|
name="endpoint"
|
||||||
:placeholder="AppDebug.endpointDomainVrchat"
|
:placeholder="AppDebug.endpointDomainVrchat"
|
||||||
clearable />
|
:aria-invalid="!!errors.length"
|
||||||
</el-form-item>
|
clearable
|
||||||
<el-form-item
|
@update:modelValue="field.onChange"
|
||||||
v-if="enableCustomEndpoint"
|
@blur="field.onBlur" />
|
||||||
:label="t('view.login.field.websocket')"
|
<FieldError v-if="errors.length" :errors="errors" />
|
||||||
prop="websocket"
|
</FieldContent>
|
||||||
style="margin-top: 10px">
|
</Field>
|
||||||
|
</VeeField>
|
||||||
|
<VeeField v-slot="{ field, errors }" name="websocket">
|
||||||
|
<Field :data-invalid="!!errors.length">
|
||||||
|
<FieldLabel for="login-form-websocket">
|
||||||
|
{{ t('view.login.field.websocket') }}
|
||||||
|
</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<InputGroupField
|
<InputGroupField
|
||||||
v-model="loginForm.websocket"
|
id="login-form-websocket"
|
||||||
|
:model-value="field.value"
|
||||||
name="websocket"
|
name="websocket"
|
||||||
:placeholder="AppDebug.websocketDomainVrchat"
|
:placeholder="AppDebug.websocketDomainVrchat"
|
||||||
clearable />
|
:aria-invalid="!!errors.length"
|
||||||
</el-form-item>
|
clearable
|
||||||
<el-form-item>
|
@update:modelValue="field.onChange"
|
||||||
<Button class="mt-2" type="submit" size="lg" style="width: 100%">{{
|
@blur="field.onBlur" />
|
||||||
t('view.login.login')
|
<FieldError v-if="errors.length" :errors="errors" />
|
||||||
}}</Button>
|
</FieldContent>
|
||||||
</el-form-item>
|
</Field>
|
||||||
</el-form>
|
</VeeField>
|
||||||
|
</FieldGroup>
|
||||||
|
<Field class="mt-2">
|
||||||
|
<Button type="submit" size="lg" style="width: 100%">{{ t('view.login.login') }}</Button>
|
||||||
|
</Field>
|
||||||
|
</form>
|
||||||
<Button
|
<Button
|
||||||
variant="Secondary"
|
variant="Secondary"
|
||||||
size="lg"
|
size="lg"
|
||||||
@@ -147,14 +177,18 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { Field, FieldContent, FieldError, FieldGroup, FieldLabel } from '@/components/ui/field';
|
||||||
import { onBeforeMount, onBeforeUnmount, ref, watch } from 'vue';
|
import { onBeforeMount, onBeforeUnmount, ref, watch } from 'vue';
|
||||||
import { CircleArrowDown, Route } from 'lucide-vue-next';
|
import { CircleArrowDown, Route } from 'lucide-vue-next';
|
||||||
|
import { Field as VeeField, useForm } from 'vee-validate';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { InputGroupField } from '@/components/ui/input-group';
|
|
||||||
import { Checkbox } from '@/components/ui/checkbox';
|
import { Checkbox } from '@/components/ui/checkbox';
|
||||||
|
import { InputGroupField } from '@/components/ui/input-group';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { useAuthStore, useGeneralSettingsStore, useVRCXUpdaterStore } from '../../stores';
|
import { useAuthStore, useGeneralSettingsStore, useVRCXUpdaterStore } from '../../stores';
|
||||||
import { openExternalLink, userImage } from '../../shared/utils';
|
import { openExternalLink, userImage } from '../../shared/utils';
|
||||||
@@ -171,8 +205,27 @@
|
|||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const loginFormRef = ref(null);
|
|
||||||
const savedCredentials = ref({});
|
const savedCredentials = ref({});
|
||||||
|
const requiredMessage = 'Required';
|
||||||
|
|
||||||
|
const formSchema = toTypedSchema(
|
||||||
|
z.object({
|
||||||
|
username: z.string().min(1, requiredMessage),
|
||||||
|
password: z.string().min(1, requiredMessage),
|
||||||
|
endpoint: z.string().optional(),
|
||||||
|
websocket: z.string().optional()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const { handleSubmit, resetForm, setValues, values } = useForm({
|
||||||
|
validationSchema: formSchema,
|
||||||
|
initialValues: {
|
||||||
|
username: loginForm.value.username,
|
||||||
|
password: loginForm.value.password,
|
||||||
|
endpoint: loginForm.value.endpoint,
|
||||||
|
websocket: loginForm.value.websocket
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
async function clickDeleteSavedLogin(userId) {
|
async function clickDeleteSavedLogin(userId) {
|
||||||
await deleteSavedLogin(userId);
|
await deleteSavedLogin(userId);
|
||||||
@@ -184,15 +237,22 @@
|
|||||||
await updateSavedCredentials();
|
await updateSavedCredentials();
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleLogin() {
|
const onSubmit = handleSubmit(async (formValues) => {
|
||||||
if (loginFormRef.value) {
|
loginForm.value.username = formValues.username ?? '';
|
||||||
loginFormRef.value.validate(async (valid) => {
|
loginForm.value.password = formValues.password ?? '';
|
||||||
if (valid) {
|
loginForm.value.endpoint = formValues.endpoint ?? '';
|
||||||
|
loginForm.value.websocket = formValues.websocket ?? '';
|
||||||
await login();
|
await login();
|
||||||
await updateSavedCredentials();
|
await updateSavedCredentials();
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
async function handleCustomEndpointToggle() {
|
||||||
|
await toggleCustomEndpoint();
|
||||||
|
setValues({
|
||||||
|
...values,
|
||||||
|
endpoint: loginForm.value.endpoint,
|
||||||
|
websocket: loginForm.value.websocket
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateSavedCredentials() {
|
async function updateSavedCredentials() {
|
||||||
@@ -224,9 +284,29 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
if (loginFormRef.value) {
|
resetForm({
|
||||||
loginFormRef.value.resetFields();
|
values: {
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
endpoint: '',
|
||||||
|
websocket: ''
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
loginForm.value.username = '';
|
||||||
|
loginForm.value.password = '';
|
||||||
|
loginForm.value.endpoint = '';
|
||||||
|
loginForm.value.websocket = '';
|
||||||
savedCredentials.value = {};
|
savedCredentials.value = {};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
values,
|
||||||
|
(formValues) => {
|
||||||
|
loginForm.value.username = formValues.username ?? '';
|
||||||
|
loginForm.value.password = formValues.password ?? '';
|
||||||
|
loginForm.value.endpoint = formValues.endpoint ?? '';
|
||||||
|
loginForm.value.websocket = formValues.websocket ?? '';
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -21,8 +21,10 @@
|
|||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<el-form label-position="top" label-width="120px" size="small" style="margin-bottom: 12px">
|
<FieldGroup class="mb-3">
|
||||||
<el-form-item :label="t('dialog.translation_api.mode')">
|
<Field>
|
||||||
|
<FieldLabel>{{ t('dialog.translation_api.mode') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<Select :model-value="form.translationApiType" @update:modelValue="handleTranslationApiTypeChange">
|
<Select :model-value="form.translationApiType" @update:modelValue="handleTranslationApiTypeChange">
|
||||||
<SelectTrigger size="sm" style="width: 100%">
|
<SelectTrigger size="sm" style="width: 100%">
|
||||||
<SelectValue :placeholder="t('dialog.translation_api.mode')" />
|
<SelectValue :placeholder="t('dialog.translation_api.mode')" />
|
||||||
@@ -38,48 +40,64 @@
|
|||||||
</SelectGroup>
|
</SelectGroup>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
</el-form>
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
|
||||||
<template v-if="form.translationApiType === 'google'">
|
<template v-if="form.translationApiType === 'google'">
|
||||||
<el-form label-position="top" label-width="120px" size="small">
|
<FieldGroup>
|
||||||
<el-form-item :label="t('dialog.translation_api.description')">
|
<Field>
|
||||||
|
<FieldLabel>{{ t('dialog.translation_api.description') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<InputGroupField
|
<InputGroupField
|
||||||
v-model="form.translationApiKey"
|
v-model="form.translationApiKey"
|
||||||
type="password"
|
type="password"
|
||||||
show-password
|
show-password
|
||||||
placeholder="AIzaSy..."
|
placeholder="AIzaSy..."
|
||||||
clearable />
|
clearable />
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
</el-form>
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="form.translationApiType === 'openai'">
|
<template v-if="form.translationApiType === 'openai'">
|
||||||
<el-form label-position="top" label-width="120px" size="small">
|
<FieldGroup>
|
||||||
<el-form-item :label="t('dialog.translation_api.openai.endpoint')">
|
<Field>
|
||||||
|
<FieldLabel>{{ t('dialog.translation_api.openai.endpoint') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<InputGroupField
|
<InputGroupField
|
||||||
v-model="form.translationApiEndpoint"
|
v-model="form.translationApiEndpoint"
|
||||||
placeholder="https://api.openai.com/v1/chat/completions"
|
placeholder="https://api.openai.com/v1/chat/completions"
|
||||||
clearable />
|
clearable />
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
|
</Field>
|
||||||
|
|
||||||
<el-form-item :label="t('dialog.translation_api.openai.api_key')">
|
<Field>
|
||||||
|
<FieldLabel>{{ t('dialog.translation_api.openai.api_key') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<InputGroupField
|
<InputGroupField
|
||||||
v-model="form.translationApiKey"
|
v-model="form.translationApiKey"
|
||||||
type="password"
|
type="password"
|
||||||
show-password
|
show-password
|
||||||
placeholder="sk-..."
|
placeholder="sk-..."
|
||||||
clearable />
|
clearable />
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
|
</Field>
|
||||||
|
|
||||||
<el-form-item :label="t('dialog.translation_api.openai.model')">
|
<Field>
|
||||||
|
<FieldLabel>{{ t('dialog.translation_api.openai.model') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<InputGroupField v-model="form.translationApiModel" clearable />
|
<InputGroupField v-model="form.translationApiModel" clearable />
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
|
</Field>
|
||||||
|
|
||||||
<el-form-item :label="t('dialog.translation_api.openai.prompt_optional')">
|
<Field>
|
||||||
|
<FieldLabel>{{ t('dialog.translation_api.openai.prompt_optional') }}</FieldLabel>
|
||||||
|
<FieldContent>
|
||||||
<InputGroupTextareaField v-model="form.translationApiPrompt" :rows="3" clearable />
|
<InputGroupTextareaField v-model="form.translationApiPrompt" :rows="3" clearable />
|
||||||
</el-form-item>
|
</FieldContent>
|
||||||
</el-form>
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@@ -94,14 +112,14 @@
|
|||||||
">
|
">
|
||||||
{{ t('dialog.translation_api.guide') }}
|
{{ t('dialog.translation_api.guide') }}
|
||||||
</Button>
|
</Button>
|
||||||
<div>
|
|
||||||
<Button
|
<Button
|
||||||
variant="secondary"
|
variant="outline"
|
||||||
class="mr-2"
|
class="mr-2"
|
||||||
v-if="form.translationApiType === 'openai'"
|
v-if="form.translationApiType === 'openai'"
|
||||||
@click="testOpenAiTranslation">
|
@click="testOpenAiTranslation">
|
||||||
{{ t('dialog.translation_api.test') }}
|
{{ t('dialog.translation_api.test') }}
|
||||||
</Button>
|
</Button>
|
||||||
|
<div>
|
||||||
<Button style="margin-left: auto" @click="saveTranslationApiConfig">
|
<Button style="margin-left: auto" @click="saveTranslationApiConfig">
|
||||||
{{ t('dialog.translation_api.save') }}
|
{{ t('dialog.translation_api.save') }}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -113,9 +131,10 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||||
|
import { Field, FieldContent, FieldGroup, FieldLabel } from '@/components/ui/field';
|
||||||
|
import { InputGroupField, InputGroupTextareaField } from '@/components/ui/input-group';
|
||||||
import { reactive, watch } from 'vue';
|
import { reactive, watch } from 'vue';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { InputGroupField, InputGroupTextareaField } from '@/components/ui/input-group';
|
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { toast } from 'vue-sonner';
|
import { toast } from 'vue-sonner';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|||||||
@@ -312,9 +312,9 @@
|
|||||||
.x-link:hover {
|
.x-link:hover {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
.x-link:hover span {
|
/* .x-link:hover span {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
} */
|
||||||
.is-rotated {
|
.is-rotated {
|
||||||
transform: rotate(90deg);
|
transform: rotate(90deg);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user