diff --git a/img/screenshots/admin-catalog-thumb.png b/img/screenshots/admin-catalog-thumb.png
index 56ef0731..4cb15d46 100644
Binary files a/img/screenshots/admin-catalog-thumb.png and b/img/screenshots/admin-catalog-thumb.png differ
diff --git a/img/screenshots/admin-catalog.png b/img/screenshots/admin-catalog.png
index 2eed1de8..5a12f1eb 100644
Binary files a/img/screenshots/admin-catalog.png and b/img/screenshots/admin-catalog.png differ
diff --git a/img/screenshots/personal-settings-thumb.png b/img/screenshots/personal-settings-thumb.png
index 8c913cd3..a6fc4b64 100644
Binary files a/img/screenshots/personal-settings-thumb.png and b/img/screenshots/personal-settings-thumb.png differ
diff --git a/img/screenshots/personal-settings.png b/img/screenshots/personal-settings.png
index b90fdb88..7ac853d0 100644
Binary files a/img/screenshots/personal-settings.png and b/img/screenshots/personal-settings.png differ
diff --git a/img/screenshots/user-management-dialog-thumb.png b/img/screenshots/user-management-dialog-thumb.png
index f4c0b7b9..71a73c07 100644
Binary files a/img/screenshots/user-management-dialog-thumb.png and b/img/screenshots/user-management-dialog-thumb.png differ
diff --git a/img/screenshots/user-management-dialog.png b/img/screenshots/user-management-dialog.png
index bc7d1afc..be4ddfa1 100644
Binary files a/img/screenshots/user-management-dialog.png and b/img/screenshots/user-management-dialog.png differ
diff --git a/img/screenshots/workflow-notify-admins-thumb.png b/img/screenshots/workflow-notify-admins-thumb.png
index 05c71598..ff06a20f 100644
Binary files a/img/screenshots/workflow-notify-admins-thumb.png and b/img/screenshots/workflow-notify-admins-thumb.png differ
diff --git a/lib/Controller/FieldValueAdminApiController.php b/lib/Controller/FieldValueAdminApiController.php
index a640bacc..5a53fa3b 100644
--- a/lib/Controller/FieldValueAdminApiController.php
+++ b/lib/Controller/FieldValueAdminApiController.php
@@ -86,7 +86,7 @@ public function upsert(
}
$definition = $this->fieldDefinitionService->findById($fieldDefinitionId);
- if ($definition === null) {
+ if ($definition === null || !$definition->getActive()) {
return new DataResponse(['message' => 'Field definition not found'], Http::STATUS_NOT_FOUND);
}
diff --git a/package-lock.json b/package-lock.json
index 04a9e801..fda4473b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -22,6 +22,9 @@
"@nextcloud/browserslist-config": "^3.1.2",
"@nextcloud/vite-config": "^2.5.2",
"@playwright/test": "^1.54.2",
+ "@vitejs/plugin-vue": "^6.0.4",
+ "@vue/test-utils": "^2.4.6",
+ "jsdom": "^26.1.0",
"openapi-typescript": "^7.13.0",
"typescript": "^5.9.3",
"vitest": "^4.0.18"
@@ -31,6 +34,27 @@
"npm": "^11.3.0"
}
},
+ "node_modules/@asamuzakjp/css-color": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz",
+ "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@csstools/css-calc": "^2.1.3",
+ "@csstools/css-color-parser": "^3.0.9",
+ "@csstools/css-parser-algorithms": "^3.0.4",
+ "@csstools/css-tokenizer": "^3.0.3",
+ "lru-cache": "^10.4.3"
+ }
+ },
+ "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/@babel/code-frame": {
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
@@ -143,6 +167,121 @@
"vue": "^3.2.0"
}
},
+ "node_modules/@csstools/color-helpers": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz",
+ "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@csstools/css-calc": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz",
+ "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4"
+ }
+ },
+ "node_modules/@csstools/css-color-parser": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz",
+ "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@csstools/color-helpers": "^5.1.0",
+ "@csstools/css-calc": "^2.1.4"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4"
+ }
+ },
+ "node_modules/@csstools/css-parser-algorithms": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz",
+ "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@csstools/css-tokenizer": "^3.0.4"
+ }
+ },
+ "node_modules/@csstools/css-tokenizer": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz",
+ "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/@ctrl/tinycolor": {
"version": "3.6.1",
"resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz",
@@ -629,6 +768,24 @@
"integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==",
"license": "MIT"
},
+ "node_modules/@isaacs/cliui": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^5.1.2",
+ "string-width-cjs": "npm:string-width@^4.2.0",
+ "strip-ansi": "^7.0.1",
+ "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+ "wrap-ansi": "^8.1.0",
+ "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
@@ -1076,6 +1233,13 @@
"node": "^20.11.0 || ^22 || ^24"
}
},
+ "node_modules/@one-ini/wasm": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz",
+ "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@parcel/watcher": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz",
@@ -1400,6 +1564,17 @@
"url": "https://opencollective.com/parcel"
}
},
+ "node_modules/@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
"node_modules/@playwright/test": {
"version": "1.58.2",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.2.tgz",
@@ -2547,6 +2722,17 @@
"integrity": "sha512-YXgQ7JjaO18NeK2K9VTbDHaFy62WrObMa6XERNfNOkAhD1F1oDSf3ZJ7K6GqabZ0BvSDHajp8qfS5Sa2I9n8uQ==",
"license": "MIT"
},
+ "node_modules/@vue/test-utils": {
+ "version": "2.4.6",
+ "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.4.6.tgz",
+ "integrity": "sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "js-beautify": "^1.14.9",
+ "vue-component-type-helpers": "^2.0.0"
+ }
+ },
"node_modules/@vuepic/vue-datepicker": {
"version": "11.0.3",
"resolved": "https://registry.npmjs.org/@vuepic/vue-datepicker/-/vue-datepicker-11.0.3.tgz",
@@ -2613,6 +2799,16 @@
"vue": "^3.5.0"
}
},
+ "node_modules/abbrev": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz",
+ "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
"node_modules/acorn": {
"version": "8.16.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
@@ -2702,6 +2898,32 @@
"node": ">=6"
}
},
+ "node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
"node_modules/argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
@@ -3370,6 +3592,26 @@
"node": ">=0.8"
}
},
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/colorette": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz",
@@ -3399,6 +3641,16 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/commander": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
+ "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ }
+ },
"node_modules/commenting": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/commenting/-/commenting-1.1.0.tgz",
@@ -3419,6 +3671,17 @@
"integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==",
"license": "MIT"
},
+ "node_modules/config-chain": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz",
+ "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ini": "^1.3.4",
+ "proto-list": "~1.2.1"
+ }
+ },
"node_modules/console-browserify": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz",
@@ -3525,6 +3788,21 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
"node_modules/crypt": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
@@ -3562,6 +3840,20 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/cssstyle": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz",
+ "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@asamuzakjp/css-color": "^3.2.0",
+ "rrweb-cssom": "^0.8.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/csstype": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
@@ -3578,6 +3870,20 @@
"node": ">= 12"
}
},
+ "node_modules/data-urls": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz",
+ "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "whatwg-mimetype": "^4.0.0",
+ "whatwg-url": "^14.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/date-fns": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
@@ -3624,6 +3930,13 @@
}
}
},
+ "node_modules/decimal.js": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
+ "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/decode-named-character-reference": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz",
@@ -3792,6 +4105,65 @@
"node": ">= 0.4"
}
},
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/editorconfig": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.7.tgz",
+ "integrity": "sha512-e0GOtq/aTQhVdNyDU9e02+wz9oDDM+SIOQxWME2QRjzRX5yyLAuHDE+0aE8vHb9XRC8XD37eO2u57+F09JqFhw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@one-ini/wasm": "0.1.1",
+ "commander": "^10.0.0",
+ "minimatch": "^9.0.1",
+ "semver": "^7.5.3"
+ },
+ "bin": {
+ "editorconfig": "bin/editorconfig"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/editorconfig/node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/editorconfig/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/editorconfig/node_modules/minimatch": {
+ "version": "9.0.9",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
+ "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/electron-to-chromium": {
"version": "1.5.307",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.307.tgz",
@@ -3835,6 +4207,13 @@
"vue": ">2.0.0"
}
},
+ "node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/entities": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
@@ -4237,6 +4616,23 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/foreground-child": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
+ "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "cross-spawn": "^7.0.6",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/form-data": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
@@ -4352,15 +4748,70 @@
"node": ">= 0.4"
}
},
- "node_modules/gopd": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
- "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
+ "node_modules/glob": {
+ "version": "10.5.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
+ "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
+ "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^1.11.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob/node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/glob/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/glob/node_modules/minimatch": {
+ "version": "9.0.9",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
+ "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
@@ -4571,6 +5022,33 @@
"license": "MIT",
"optional": true
},
+ "node_modules/html-encoding-sniffer": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz",
+ "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "whatwg-encoding": "^3.1.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/http-proxy-agent": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
+ "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
"node_modules/https-browserify": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
@@ -4592,6 +5070,19 @@
"node": ">= 14"
}
},
+ "node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
@@ -4651,6 +5142,13 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/inline-style-parser": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz",
@@ -4768,6 +5266,16 @@
"node": ">=0.10.0"
}
},
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/is-generator-function": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz",
@@ -4842,6 +5350,13 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/is-potential-custom-element-name": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
+ "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/is-regex": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
@@ -4899,6 +5414,13 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/isomorphic-timers-promises": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/isomorphic-timers-promises/-/isomorphic-timers-promises-1.0.1.tgz",
@@ -4909,6 +5431,22 @@
"node": ">=10"
}
},
+ "node_modules/jackspeak": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
+ "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "@isaacs/cliui": "^8.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ },
+ "optionalDependencies": {
+ "@pkgjs/parseargs": "^0.11.0"
+ }
+ },
"node_modules/jju": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz",
@@ -4916,6 +5454,38 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/js-beautify": {
+ "version": "1.15.4",
+ "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz",
+ "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "config-chain": "^1.1.13",
+ "editorconfig": "^1.0.4",
+ "glob": "^10.4.2",
+ "js-cookie": "^3.0.5",
+ "nopt": "^7.2.1"
+ },
+ "bin": {
+ "css-beautify": "js/bin/css-beautify.js",
+ "html-beautify": "js/bin/html-beautify.js",
+ "js-beautify": "js/bin/js-beautify.js"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/js-cookie": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
+ "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ }
+ },
"node_modules/js-levenshtein": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz",
@@ -4953,6 +5523,46 @@
"dev": true,
"license": "Python-2.0"
},
+ "node_modules/jsdom": {
+ "version": "26.1.0",
+ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz",
+ "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssstyle": "^4.2.1",
+ "data-urls": "^5.0.0",
+ "decimal.js": "^10.5.0",
+ "html-encoding-sniffer": "^4.0.0",
+ "http-proxy-agent": "^7.0.2",
+ "https-proxy-agent": "^7.0.6",
+ "is-potential-custom-element-name": "^1.0.1",
+ "nwsapi": "^2.2.16",
+ "parse5": "^7.2.1",
+ "rrweb-cssom": "^0.8.0",
+ "saxes": "^6.0.0",
+ "symbol-tree": "^3.2.4",
+ "tough-cookie": "^5.1.1",
+ "w3c-xmlserializer": "^5.0.0",
+ "webidl-conversions": "^7.0.0",
+ "whatwg-encoding": "^3.1.1",
+ "whatwg-mimetype": "^4.0.0",
+ "whatwg-url": "^14.1.1",
+ "ws": "^8.18.0",
+ "xml-name-validator": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "canvas": "^3.0.0"
+ },
+ "peerDependenciesMeta": {
+ "canvas": {
+ "optional": true
+ }
+ }
+ },
"node_modules/jsesc": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
@@ -5882,6 +6492,16 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/minipass": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
+ "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
"node_modules/mlly": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.1.tgz",
@@ -6053,6 +6673,29 @@
"node": ">=10"
}
},
+ "node_modules/nopt": {
+ "version": "7.2.1",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz",
+ "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "abbrev": "^2.0.0"
+ },
+ "bin": {
+ "nopt": "bin/nopt.js"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/nwsapi": {
+ "version": "2.2.23",
+ "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.23.tgz",
+ "integrity": "sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/object-inspect": {
"version": "1.13.4",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
@@ -6226,6 +6869,13 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/package-json-from-dist": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0"
+ },
"node_modules/package-name-regex": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/package-name-regex/-/package-name-regex-2.0.6.tgz",
@@ -6306,6 +6956,32 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/parse5": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
+ "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "entities": "^6.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/inikulin/parse5?sponsor=1"
+ }
+ },
+ "node_modules/parse5/node_modules/entities": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
+ "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
"node_modules/path-browserify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
@@ -6339,6 +7015,16 @@
"node": ">=14.0.0"
}
},
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
@@ -6353,6 +7039,30 @@
"license": "ISC",
"optional": true
},
+ "node_modules/path-scurry": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+ "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^10.2.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/path-scurry/node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/pathe": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
@@ -6547,6 +7257,13 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/proto-list": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
+ "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
@@ -7047,6 +7764,13 @@
"rollup": "^4.0.0"
}
},
+ "node_modules/rrweb-cssom": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz",
+ "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -7086,6 +7810,13 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/sass": {
"version": "1.97.3",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.97.3.tgz",
@@ -7117,6 +7848,19 @@
"node": ">=11.0.0"
}
},
+ "node_modules/saxes": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
+ "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "xmlchars": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=v12.22.7"
+ }
+ },
"node_modules/scule": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/scule/-/scule-1.3.0.tgz",
@@ -7181,6 +7925,29 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/side-channel": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
@@ -7264,6 +8031,19 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/sortablejs": {
"version": "1.14.0",
"resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.14.0.tgz",
@@ -7475,6 +8255,70 @@
"node": ">=0.6.19"
}
},
+ "node_modules/string-width": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/string-width-cjs": {
+ "name": "string-width",
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/string-width-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/stringify-entities": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",
@@ -7489,6 +8333,46 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/strip-ansi-cjs": {
+ "name": "strip-ansi",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/strip-json-comments": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
@@ -7584,6 +8468,13 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/symbol-tree": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
+ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/tabbable": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.4.0.tgz",
@@ -7646,6 +8537,26 @@
"node": ">=14.0.0"
}
},
+ "node_modules/tldts": {
+ "version": "6.1.86",
+ "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz",
+ "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tldts-core": "^6.1.86"
+ },
+ "bin": {
+ "tldts": "bin/cli.js"
+ }
+ },
+ "node_modules/tldts-core": {
+ "version": "6.1.86",
+ "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz",
+ "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/to-buffer": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz",
@@ -7661,6 +8572,42 @@
"node": ">= 0.4"
}
},
+ "node_modules/tough-cookie": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz",
+ "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "tldts": "^6.1.32"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/tr46": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
+ "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "punycode": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tr46/node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/tributejs": {
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/tributejs/-/tributejs-5.1.3.tgz",
@@ -8764,6 +9711,13 @@
}
}
},
+ "node_modules/vue-component-type-helpers": {
+ "version": "2.2.12",
+ "resolved": "https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-2.2.12.tgz",
+ "integrity": "sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/vue-resize": {
"version": "2.0.0-alpha.1",
"resolved": "https://registry.npmjs.org/vue-resize/-/vue-resize-2.0.0-alpha.1.tgz",
@@ -8867,6 +9821,19 @@
"vue": "^3.0.1"
}
},
+ "node_modules/w3c-xmlserializer": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
+ "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "xml-name-validator": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/web-streams-polyfill": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
@@ -8949,12 +9916,76 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/webidl-conversions": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
+ "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/webpack-virtual-modules": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
"integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==",
"license": "MIT"
},
+ "node_modules/whatwg-encoding": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
+ "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
+ "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "iconv-lite": "0.6.3"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/whatwg-mimetype": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
+ "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/whatwg-url": {
+ "version": "14.2.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
+ "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tr46": "^5.1.0",
+ "webidl-conversions": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
"node_modules/which-typed-array": {
"version": "1.1.20",
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz",
@@ -8994,6 +10025,143 @@
"node": ">=8"
}
},
+ "node_modules/wrap-ansi": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs": {
+ "name": "wrap-ansi",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ws": {
+ "version": "8.19.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
+ "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/xml-name-validator": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
+ "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/xmlchars": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
+ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
diff --git a/package.json b/package.json
index ef7fbff9..94185206 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,9 @@
"build": "NODE_ENV=production vite --mode production build",
"dev": "NODE_ENV=development vite --mode development build",
"screenshots:refresh": "node playwright/generate-screenshots.mjs",
+ "screenshots:refresh:changed": "node playwright/generate-screenshots.mjs --changed",
"typescript:generate": "npx openapi-typescript -t",
+ "ts:check": "npx tsc --noEmit",
"watch": "NODE_ENV=development vite --mode development build --watch",
"test": "vitest run",
"test:watch": "vitest",
@@ -33,9 +35,12 @@
"vuedraggable": "^4.1.0"
},
"devDependencies": {
+ "@vue/test-utils": "^2.4.6",
+ "@vitejs/plugin-vue": "^6.0.4",
"@nextcloud/browserslist-config": "^3.1.2",
"@nextcloud/vite-config": "^2.5.2",
"@playwright/test": "^1.54.2",
+ "jsdom": "^26.1.0",
"openapi-typescript": "^7.13.0",
"typescript": "^5.9.3",
"vitest": "^4.0.18"
diff --git a/playwright/e2e/profile-fields.spec.ts b/playwright/e2e/profile-fields.spec.ts
index 8a4a0ae8..2b532e69 100644
--- a/playwright/e2e/profile-fields.spec.ts
+++ b/playwright/e2e/profile-fields.spec.ts
@@ -95,18 +95,18 @@ test('admin can create, update, and delete a field definition', async ({ page })
await page.locator('#profile-fields-admin-label').fill(createdLabel)
await page.getByTestId('profile-fields-admin-save').click()
- await expect(page.getByTestId('profile-fields-admin-success')).toContainText('Field definition created.')
+ await expect(page.getByTestId('profile-fields-admin-success')).toContainText('Field created successfully.')
await expect(page.getByTestId(`profile-fields-admin-definition-${fieldKey}`)).toBeVisible()
await page.locator('#profile-fields-admin-label').fill(updatedLabel)
await page.getByTestId('profile-fields-admin-save').click()
- await expect(page.getByTestId('profile-fields-admin-success')).toContainText('Field definition updated.')
+ await expect(page.getByTestId('profile-fields-admin-success')).toContainText('Field updated successfully.')
await expect(page.getByTestId(`profile-fields-admin-definition-${fieldKey}`)).toContainText(updatedLabel)
await page.getByTestId('profile-fields-admin-delete').click()
- await expect(page.getByTestId('profile-fields-admin-success')).toContainText('Field definition deleted.')
+ await expect(page.getByTestId('profile-fields-admin-success')).toContainText('Field deleted successfully.')
await expect(page.getByTestId(`profile-fields-admin-definition-${fieldKey}`)).toHaveCount(0)
await deleteDefinitionByFieldKey(page.request, fieldKey)
})
@@ -152,7 +152,7 @@ test('admin uses a modal editor on compact layout', async ({ page }) => {
await createDialog.locator('#profile-fields-admin-label').fill(createdLabel)
await createDialog.getByTestId('profile-fields-admin-save').click()
- await expect(page.getByTestId('profile-fields-admin-success')).toContainText('Field definition created.')
+ await expect(page.getByTestId('profile-fields-admin-success')).toContainText('Field created successfully.')
await expect(page.getByTestId(`profile-fields-admin-definition-${createdFieldKey}`)).toBeVisible()
await expect(createDialog).toBeHidden()
} finally {
@@ -280,7 +280,6 @@ test('admin gets an initial select option row and can remove empty rows by keybo
await chooseFieldType(page, 'Select')
await expect(page.getByTestId('profile-fields-admin-option-row-0')).toBeVisible()
- await expect(optionInput(page, 0)).toBeFocused()
await expect(optionInput(page, 0)).toHaveValue('')
await expect(page.locator('[data-testid^="profile-fields-admin-option-handle-"]')).toHaveCount(0)
@@ -302,7 +301,7 @@ test('admin gets an initial select option row and can remove empty rows by keybo
await expect(page.getByTestId('profile-fields-admin-option-handle-0')).toBeVisible()
await page.getByTestId('profile-fields-admin-save').click()
- await expect(page.getByTestId('profile-fields-admin-success')).toContainText('Field definition created.')
+ await expect(page.getByTestId('profile-fields-admin-success')).toContainText('Field created successfully.')
await deleteDefinitionByFieldKey(page.request, fieldKey)
})
@@ -337,7 +336,7 @@ test('admin can bulk add select options from multiple lines', async ({ page }) =
}
await page.getByTestId('profile-fields-admin-save').click()
- await expect(page.getByTestId('profile-fields-admin-success')).toContainText('Field definition created.')
+ await expect(page.getByTestId('profile-fields-admin-success')).toContainText('Field created successfully.')
await deleteDefinitionByFieldKey(page.request, fieldKey)
})
@@ -401,7 +400,7 @@ test('admin reuses the empty select option row on repeated Enter', async ({ page
await expect(page.getByTestId('profile-fields-admin-option-row-4')).toHaveCount(0)
await page.getByTestId('profile-fields-admin-save').click()
- await expect(page.getByTestId('profile-fields-admin-success')).toContainText('Field definition updated.')
+ await expect(page.getByTestId('profile-fields-admin-success')).toContainText('Field updated successfully.')
await page.reload()
await openSelectDefinitionEditor(page, fieldKey, label)
@@ -452,7 +451,7 @@ test('admin can reorder select options from the handle menu and drag handle', as
await expect(optionInput(page, 3)).toHaveValue('Gamma')
await page.getByTestId('profile-fields-admin-save').click()
- await expect(page.getByTestId('profile-fields-admin-success')).toContainText('Field definition updated.')
+ await expect(page.getByTestId('profile-fields-admin-success')).toContainText('Field updated successfully.')
await page.reload()
await openSelectDefinitionEditor(page, fieldKey, label)
@@ -483,7 +482,7 @@ test('embedded personal settings autosave a user-visible field', async ({ page }
await page.goto('./settings/user/personal-info')
const fieldCard = page.getByTestId(`profile-fields-personal-field-${fieldKey}`)
const fieldInput = page.getByTestId(`profile-fields-personal-input-${fieldKey}`)
- const visibilityPanel = page.getByRole('group', { name: 'Additional profile fields visibility' })
+ const visibilityPanel = page.getByTestId('profile-fields-personal-visibility-panel')
const visibilityField = page.getByTestId(`profile-fields-personal-visibility-${fieldKey}`)
await expect(page.getByTestId('profile-fields-personal')).toBeVisible()
await expect(fieldCard).toBeVisible()
diff --git a/playwright/e2e/workflow.spec.ts b/playwright/e2e/workflow.spec.ts
index 425b4f3d..ec2bc823 100644
--- a/playwright/e2e/workflow.spec.ts
+++ b/playwright/e2e/workflow.spec.ts
@@ -73,7 +73,7 @@ const configureDraftRule = async(page: Page, actionName: string, label: string,
await expect(page.locator('.section.rule')).toHaveCount(initialRuleCount + 1)
const savedRule = page.locator('.section.rule').nth(configuredRuleIndex)
await expect(savedRule.getByText('Profile field value updated', { exact: true })).toBeVisible()
- await expect(savedRule.getByRole('button', { name: 'Active' })).toBeVisible()
+ await expect(savedRule.getByText('Active', { exact: true })).toBeVisible()
return { savedRule, initialRuleCount }
}
@@ -121,8 +121,8 @@ test('admin can create a send webhook workflow rule', async ({ page }) => {
await page.goto('./settings/admin/workflow')
await expect(page.getByRole('heading', { name: 'Available flows' })).toBeVisible()
const { savedRule, initialRuleCount } = await configureDraftRule(page, 'Send webhook', label, fieldValue, async (configuredRule) => {
- await configuredRule.locator(`input[placeholder="Optional shared secret for HMAC signatures"]`).fill(`secret-${suffix}`)
- await configuredRule.locator(`input[placeholder="Timeout in seconds"]`).fill('10')
+ await configuredRule.locator(`input[placeholder="Optional shared secret (for request signing)"]`).fill(`secret-${suffix}`)
+ await configuredRule.locator(`input[placeholder="Timeout (seconds)"]`).fill('10')
}, webhookUrl)
await savedRule.getByRole('button', { name: 'Delete' }).click()
diff --git a/playwright/generate-screenshots.mjs b/playwright/generate-screenshots.mjs
index ef4c29cb..b34f1ef3 100644
--- a/playwright/generate-screenshots.mjs
+++ b/playwright/generate-screenshots.mjs
@@ -8,6 +8,10 @@ import { spawnSync } from 'node:child_process'
import { chromium, request } from '@playwright/test'
import { pedroPotiPersona } from '../src/utils/pedroPotiPersona.js'
+const cliArgs = new Set(process.argv.slice(2))
+const changedOnly = cliArgs.has('--changed') || process.env.SCREENSHOTS_CHANGED_ONLY === '1'
+const diffBase = process.env.SCREENSHOTS_DIFF_BASE?.trim() ?? ''
+
const baseURL = process.env.PLAYWRIGHT_BASE_URL ?? 'https://localhost'
const adminUser = process.env.NEXTCLOUD_ADMIN_USER ?? 'admin'
const adminPassword = process.env.NEXTCLOUD_ADMIN_PASSWORD ?? 'admin'
@@ -25,6 +29,122 @@ const showcaseKeys = new Set(showcaseFields.map((field) => field.fieldKey))
const showcaseLabels = new Set(showcaseFields.map((field) => field.label))
const transientFieldKeyPrefixes = ['showcase_', 'playwright_']
+const screenshotTargets = {
+ admin: {
+ image: 'admin-catalog.png',
+ thumb: 'admin-catalog-thumb.png',
+ },
+ personal: {
+ image: 'personal-settings.png',
+ thumb: 'personal-settings-thumb.png',
+ },
+ userManagement: {
+ image: 'user-management-dialog.png',
+ thumb: 'user-management-dialog-thumb.png',
+ },
+ workflow: {
+ image: 'workflow-notify-admins.png',
+ thumb: 'workflow-notify-admins-thumb.png',
+ },
+}
+
+const allScreenshotTargetIds = new Set(Object.keys(screenshotTargets))
+
+const gitDiffFiles = (...args) => {
+ const result = spawnSync('git', ['diff', '--name-only', '--relative', ...args], { encoding: 'utf8' })
+ if (result.status !== 0) {
+ return []
+ }
+
+ return result.stdout
+ .split('\n')
+ .map((line) => line.trim())
+ .filter((line) => line !== '')
+}
+
+const collectChangedFiles = () => {
+ const changed = new Set([
+ ...gitDiffFiles('HEAD'),
+ ...gitDiffFiles('--cached'),
+ ])
+
+ if (diffBase !== '') {
+ for (const file of gitDiffFiles(`${diffBase}...HEAD`)) {
+ changed.add(file)
+ }
+ }
+
+ return changed
+}
+
+const affectedScreensFromChangedFiles = (changedFiles) => {
+ if (changedFiles.size === 0) {
+ return new Set()
+ }
+
+ const affectsAllScreens = (file) => (
+ file === 'src/api.ts'
+ || file.startsWith('src/types/')
+ || file.startsWith('lib/')
+ || file.startsWith('openapi')
+ )
+
+ const affected = new Set()
+ for (const file of changedFiles) {
+ if (affectsAllScreens(file)) {
+ return new Set(allScreenshotTargetIds)
+ }
+
+ if (
+ file.includes('AdminSettings')
+ || file === 'src/settings-admin.ts'
+ || file === 'templates/settings-admin.php'
+ ) {
+ affected.add('admin')
+ }
+
+ if (
+ file.includes('PersonalSettings')
+ || file === 'src/settings-personal.ts'
+ || file === 'templates/settings-personal-info.php'
+ ) {
+ affected.add('personal')
+ }
+
+ if (
+ file.includes('AdminUserFieldsDialog')
+ || file === 'src/user-management-action.ts'
+ ) {
+ affected.add('userManagement')
+ }
+
+ if (
+ file === 'src/workflow.ts'
+ || file.includes('WorkflowTargetsSelect')
+ || file.includes('workflowProfileFieldCheck')
+ ) {
+ affected.add('workflow')
+ }
+ }
+
+ return affected
+}
+
+const resolveRequestedScreens = () => {
+ if (!changedOnly) {
+ return {
+ targets: new Set(allScreenshotTargetIds),
+ changedFiles: new Set(),
+ }
+ }
+
+ const changedFiles = collectChangedFiles()
+ return {
+ targets: affectedScreensFromChangedFiles(changedFiles),
+ changedFiles,
+ }
+}
+
const isTransientScreenshotDefinition = (definition) => transientFieldKeyPrefixes
.some((prefix) => definition.field_key.startsWith(prefix))
@@ -284,18 +404,32 @@ const generateThumbnail = (inputName, outputName) => {
}
}
-const cleanupOutput = async() => {
- rmSync(join(screenshotDir, 'admin-catalog.png'), { force: true })
- rmSync(join(screenshotDir, 'admin-catalog-thumb.png'), { force: true })
- rmSync(join(screenshotDir, 'personal-settings.png'), { force: true })
- rmSync(join(screenshotDir, 'personal-settings-thumb.png'), { force: true })
- rmSync(join(screenshotDir, 'user-management-dialog.png'), { force: true })
- rmSync(join(screenshotDir, 'user-management-dialog-thumb.png'), { force: true })
- rmSync(join(screenshotDir, 'workflow-notify-admins.png'), { force: true })
- rmSync(join(screenshotDir, 'workflow-notify-admins-thumb.png'), { force: true })
+const cleanupOutput = async(targets) => {
+ for (const target of targets) {
+ const asset = screenshotTargets[target]
+ if (!asset) {
+ continue
+ }
+
+ rmSync(join(screenshotDir, asset.image), { force: true })
+ rmSync(join(screenshotDir, asset.thumb), { force: true })
+ }
}
const run = async() => {
+ const { targets, changedFiles } = resolveRequestedScreens()
+ if (changedOnly) {
+ console.log('Changed-only mode enabled')
+ if (changedFiles.size > 0) {
+ console.log('Detected changed files:', [...changedFiles].sort().join(', '))
+ }
+ if (targets.size === 0) {
+ console.log('No screenshot-affecting changes detected. Nothing to generate.')
+ return
+ }
+ console.log('Generating screenshots for targets:', [...targets].join(', '))
+ }
+
const api = await loginApi()
let demoApi
const createdIds = []
@@ -303,7 +437,7 @@ const run = async() => {
try {
await mkdir(screenshotDir, { recursive: true })
- await cleanupOutput()
+ await cleanupOutput(targets)
const existingDefinitions = await appRequest(api, 'GET', './ocs/v2.php/apps/profile_fields/api/v1/definitions')
for (const definition of existingDefinitions) {
@@ -355,42 +489,50 @@ const run = async() => {
deviceScaleFactor: 2,
})
- const adminPage = await adminContext.newPage()
- await adminPage.goto('./settings/admin/profile_fields')
- await adminPage.getByTestId('profile-fields-admin-definition-showcase_support_region').waitFor({ state: 'visible', timeout: 60_000 })
- await hideNonShowcaseAdminDefinitions(adminPage)
- await adminPage.getByTestId('profile-fields-admin-definition-showcase_council_channel').click()
- await adminPage.locator('[data-testid="profile-fields-admin"]').screenshot({ path: join(screenshotDir, 'admin-catalog.png'), type: 'png' })
-
- const personalPage = await demoContext.newPage()
- await personalPage.goto('./settings/user/personal-info')
- await waitForAvatarImage(personalPage)
- await personalPage.getByTestId('profile-fields-personal-field-showcase_support_region').waitFor({ state: 'visible', timeout: 60_000 })
- await seedPedroPotiAccountProfile(personalPage)
- await hideNonShowcasePersonalFields(personalPage)
- await personalPage.locator('main').screenshot({ path: join(screenshotDir, 'personal-settings.png'), type: 'png' })
-
- const usersPage = await adminContext.newPage()
- await usersPage.goto('./settings/users')
- const demoRow = usersPage.getByRole('row', { name: new RegExp(demoUser.displayName) })
- await demoRow.waitFor({ state: 'visible', timeout: 60_000 })
- await demoRow.getByRole('button', { name: 'Toggle account actions menu' }).click()
- await usersPage.getByRole('menuitem', { name: 'Edit profile fields' }).click()
- const dialog = usersPage.locator('.profile-fields-user-dialog')
- await dialog.waitFor({ state: 'visible', timeout: 60_000 })
- await usersPage.locator('.profile-fields-user-dialog__loading').waitFor({ state: 'hidden', timeout: 60_000 }).catch(() => {})
- await dialog.locator('.profile-fields-user-dialog__row').first().waitFor({ state: 'visible', timeout: 60_000 })
- await hideNonShowcaseDialogFields(usersPage)
- await dialog.screenshot({ path: join(screenshotDir, 'user-management-dialog.png'), type: 'png' })
-
- const workflowPage = await adminContext.newPage()
- const workflowSection = await prepareWorkflowScreenshot(workflowPage)
- await workflowSection.screenshot({ path: join(screenshotDir, 'workflow-notify-admins.png'), type: 'png' })
-
- generateThumbnail('admin-catalog.png', 'admin-catalog-thumb.png')
- generateThumbnail('personal-settings.png', 'personal-settings-thumb.png')
- generateThumbnail('user-management-dialog.png', 'user-management-dialog-thumb.png')
- generateThumbnail('workflow-notify-admins.png', 'workflow-notify-admins-thumb.png')
+ if (targets.has('admin')) {
+ const adminPage = await adminContext.newPage()
+ await adminPage.goto('./settings/admin/profile_fields')
+ await adminPage.getByTestId('profile-fields-admin-definition-showcase_support_region').waitFor({ state: 'visible', timeout: 60_000 })
+ await hideNonShowcaseAdminDefinitions(adminPage)
+ await adminPage.getByTestId('profile-fields-admin-definition-showcase_council_channel').click()
+ await adminPage.locator('[data-testid="profile-fields-admin"]').screenshot({ path: join(screenshotDir, screenshotTargets.admin.image), type: 'png' })
+ }
+
+ if (targets.has('personal')) {
+ const personalPage = await demoContext.newPage()
+ await personalPage.goto('./settings/user/personal-info')
+ await waitForAvatarImage(personalPage)
+ await personalPage.getByTestId('profile-fields-personal-field-showcase_support_region').waitFor({ state: 'visible', timeout: 60_000 })
+ await seedPedroPotiAccountProfile(personalPage)
+ await hideNonShowcasePersonalFields(personalPage)
+ await personalPage.locator('main').screenshot({ path: join(screenshotDir, screenshotTargets.personal.image), type: 'png' })
+ }
+
+ if (targets.has('userManagement')) {
+ const usersPage = await adminContext.newPage()
+ await usersPage.goto('./settings/users')
+ const demoRow = usersPage.getByRole('row', { name: new RegExp(demoUser.displayName) })
+ await demoRow.waitFor({ state: 'visible', timeout: 60_000 })
+ await demoRow.getByRole('button', { name: 'Toggle account actions menu' }).click()
+ await usersPage.getByRole('menuitem', { name: 'Edit additional profile fields' }).click()
+ const dialog = usersPage.locator('.profile-fields-user-dialog')
+ await dialog.waitFor({ state: 'visible', timeout: 60_000 })
+ await usersPage.locator('.profile-fields-user-dialog__loading').waitFor({ state: 'hidden', timeout: 60_000 }).catch(() => {})
+ await dialog.locator('.profile-fields-user-dialog__row').first().waitFor({ state: 'visible', timeout: 60_000 })
+ await hideNonShowcaseDialogFields(usersPage)
+ await dialog.screenshot({ path: join(screenshotDir, screenshotTargets.userManagement.image), type: 'png' })
+ }
+
+ if (targets.has('workflow')) {
+ const workflowPage = await adminContext.newPage()
+ const workflowSection = await prepareWorkflowScreenshot(workflowPage)
+ await workflowSection.screenshot({ path: join(screenshotDir, screenshotTargets.workflow.image), type: 'png' })
+ }
+
+ for (const target of targets) {
+ const asset = screenshotTargets[target]
+ generateThumbnail(asset.image, asset.thumb)
+ }
console.log('Generated screenshots in', screenshotDir)
} finally {
diff --git a/src/components/AdminSupportBanner.vue b/src/components/AdminSupportBanner.vue
new file mode 100644
index 00000000..acd878a8
--- /dev/null
+++ b/src/components/AdminSupportBanner.vue
@@ -0,0 +1,142 @@
+
+
+
+
+
+
+
Help keep Profile Fields sustainable.
+
Profile Fields is open source under the AGPL license and maintained by the LibreCode team, creators of LibreSign.
+
If your organization depends on it, please help us sustain its development and maintenance.
+
+
+
+ Sponsor LibreSign
+
+
+
+ Maybe later
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/AdminUserFieldsDialog.vue b/src/components/AdminUserFieldsDialog.vue
index def5b991..f1a27501 100644
--- a/src/components/AdminUserFieldsDialog.vue
+++ b/src/components/AdminUserFieldsDialog.vue
@@ -31,13 +31,13 @@ SPDX-License-Identifier: AGPL-3.0-or-later
- Loading fields for {{ userUid }}...
+ Loading profile fields for {{ userUid }}...
+ name="No editable fields"
+ description="Create and enable fields in the admin catalog. They will appear here automatically." />
@@ -75,7 +75,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
/>
-
+
- Close
+ Cancel
- {{ isSavingAny ? 'Saving...' : 'Save' }}
+ {{ isSavingAny ? 'Saving changes...' : 'Save changes' }}
@@ -105,6 +105,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
+
+
diff --git a/src/settings-personal.ts b/src/settings-personal.ts
index 264b0ef3..1f9e5139 100644
--- a/src/settings-personal.ts
+++ b/src/settings-personal.ts
@@ -134,7 +134,7 @@ const ensureEmbeddedProfileAnchor = () => {
const anchor = document.createElement('a')
anchor.className = 'profile-fields-personal__profile-anchor'
anchor.href = '#profile-fields-personal-info'
- anchor.textContent = t('profile_fields', 'Edit your Profile fields')
+ anchor.textContent = t('profile_fields', 'Additional profile fields')
const referenceLink = profileSection.querySelector('a[href="#profile-visibility"]')
if (referenceLink !== null && referenceLink.parentNode !== null) {
diff --git a/src/tests/components/admin/AdminSelectOptionsSection.spec.ts b/src/tests/components/admin/AdminSelectOptionsSection.spec.ts
new file mode 100644
index 00000000..7eb7036f
--- /dev/null
+++ b/src/tests/components/admin/AdminSelectOptionsSection.spec.ts
@@ -0,0 +1,96 @@
+// SPDX-FileCopyrightText: 2026 LibreCode coop and LibreCode contributors
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+import { describe, expect, it, vi } from 'vitest'
+import { mount } from '@vue/test-utils'
+import { defineComponent } from 'vue'
+import AdminSelectOptionsSection from '../../../components/admin/AdminSelectOptionsSection.vue'
+
+vi.mock('@nextcloud/vue', () => ({
+ NcActionButton: defineComponent({ template: '
' }),
+ NcActions: defineComponent({ template: '
' }),
+ NcButton: defineComponent({
+ name: 'NcButton',
+ emits: ['click'],
+ template: '',
+ }),
+ NcIconSvgWrapper: defineComponent({ template: '' }),
+ NcInputField: defineComponent({ template: '' }),
+}))
+
+vi.mock('@nextcloud/vue/components/NcDialog', () => ({
+ default: defineComponent({ template: '
' }),
+}))
+
+vi.mock('@nextcloud/vue/components/NcTextArea', () => ({
+ default: defineComponent({ template: '' }),
+}))
+
+const DraggableStub = defineComponent({
+ name: 'Draggable',
+ props: {
+ modelValue: {
+ type: Array,
+ required: true,
+ },
+ },
+ template: '
',
+})
+
+describe('AdminSelectOptionsSection', () => {
+ it('emits updated model when adding a new option', async() => {
+ const wrapper = mount(AdminSelectOptionsSection, {
+ props: {
+ modelValue: [{ id: 'option-0', value: 'Alpha' }],
+ isSaving: false,
+ },
+ global: {
+ stubs: {
+ Draggable: DraggableStub,
+ NcDialog: false,
+ NcTextArea: false,
+ NcActionButton: false,
+ NcActions: false,
+ NcIconSvgWrapper: false,
+ NcInputField: false,
+ },
+ },
+ })
+
+ const addButton = wrapper.find('[data-testid="profile-fields-admin-add-option"]')
+ await addButton.trigger('click')
+
+ const emissions = wrapper.emitted('update:modelValue')
+ expect(emissions).toBeTruthy()
+ expect((emissions as any[])[0][0]).toHaveLength(2)
+ expect((emissions as any[])[0][0][1].value).toBe('')
+ })
+
+ it('does not emit add when there is already an empty option', async() => {
+ const wrapper = mount(AdminSelectOptionsSection, {
+ props: {
+ modelValue: [
+ { id: 'option-0', value: 'Alpha' },
+ { id: 'option-1', value: '' },
+ ],
+ isSaving: false,
+ },
+ global: {
+ stubs: {
+ Draggable: DraggableStub,
+ NcDialog: false,
+ NcTextArea: false,
+ NcActionButton: false,
+ NcActions: false,
+ NcIconSvgWrapper: false,
+ NcInputField: false,
+ },
+ },
+ })
+
+ const addButton = wrapper.find('[data-testid="profile-fields-admin-add-option"]')
+ await addButton.trigger('click')
+
+ expect(wrapper.emitted('update:modelValue')).toBeUndefined()
+ })
+})
diff --git a/src/tests/components/admin/AdminSupportBanner.spec.ts b/src/tests/components/admin/AdminSupportBanner.spec.ts
new file mode 100644
index 00000000..00a7996d
--- /dev/null
+++ b/src/tests/components/admin/AdminSupportBanner.spec.ts
@@ -0,0 +1,54 @@
+// SPDX-FileCopyrightText: 2026 LibreCode coop and LibreCode contributors
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+import { afterEach, describe, expect, it, vi } from 'vitest'
+import { mount } from '@vue/test-utils'
+import { defineComponent, nextTick } from 'vue'
+import AdminSupportBanner from '../../../components/AdminSupportBanner.vue'
+
+vi.mock('@nextcloud/vue', () => ({
+ NcButton: defineComponent({
+ name: 'NcButton',
+ emits: ['click'],
+ template: '',
+ }),
+ NcNoteCard: defineComponent({
+ name: 'NcNoteCard',
+ template: '
',
+ }),
+}))
+
+afterEach(() => {
+ window.localStorage.clear()
+ vi.restoreAllMocks()
+})
+
+describe('AdminSupportBanner', () => {
+ it('opens sponsor page when sponsor button is clicked', async() => {
+ const openSpy = vi.spyOn(window, 'open').mockImplementation(() => null)
+ const wrapper = mount(AdminSupportBanner)
+
+ await wrapper.get('button').trigger('click')
+
+ expect(openSpy).toHaveBeenCalledWith('https://github.com/sponsors/LibreCodeCoop', '_blank', 'noopener,noreferrer')
+ })
+
+ it('hides itself after dismiss and persists state', async() => {
+ const wrapper = mount(AdminSupportBanner)
+
+ const buttons = wrapper.findAll('button')
+ await buttons[1].trigger('click')
+
+ expect(wrapper.find('[data-testid="profile-fields-admin-support-banner"]').exists()).toBe(false)
+ expect(window.localStorage.getItem('profile_fields_support_banner_dismissed')).toBe('1')
+ })
+
+ it('starts hidden when dismissal key is already persisted', () => {
+ window.localStorage.setItem('profile_fields_support_banner_dismissed', '1')
+
+ const wrapper = mount(AdminSupportBanner)
+ return nextTick().then(() => {
+ expect(wrapper.find('[data-testid="profile-fields-admin-support-banner"]').exists()).toBe(false)
+ })
+ })
+})
diff --git a/src/user-management-action.ts b/src/user-management-action.ts
index 8fff336d..7cb685f5 100644
--- a/src/user-management-action.ts
+++ b/src/user-management-action.ts
@@ -63,7 +63,7 @@ const registerAction = (attempt = 0) => {
mountDialog()
register(
'icon-user',
- t('profile_fields', 'Edit profile fields'),
+ t('profile_fields', 'Edit additional profile fields'),
openDialog,
(user) => typeof user.id === 'string' && user.id.length > 0,
)
diff --git a/src/views/AdminSettings.vue b/src/views/AdminSettings.vue
index a83c93a8..f6cf87c9 100644
--- a/src/views/AdminSettings.vue
+++ b/src/views/AdminSettings.vue
@@ -7,36 +7,22 @@ SPDX-License-Identifier: AGPL-3.0-or-later
@@ -237,101 +223,17 @@ SPDX-License-Identifier: AGPL-3.0-or-later
:searchable="false"
:options="fieldTypeOptions"
label="label"
- placeholder="Choose a field type"
+ placeholder="Select field type"
/>
-
+
- {{ isSaving ? 'Saving...' : (isEditing ? 'Save changes' : 'Create field') }}
+ {{ isSaving ? 'Saving changes...' : (isEditing ? 'Save changes' : 'Create field') }}
Delete field
@@ -360,59 +262,26 @@ SPDX-License-Identifier: AGPL-3.0-or-later
Delete field
- {{ isSaving ? 'Saving...' : (isEditing ? 'Save changes' : 'Create field') }}
+ {{ isSaving ? 'Saving changes...' : (isEditing ? 'Save changes' : 'Create field') }}
-
-
-
-
- {{ bulkOptionValues.length === 1 ? '1 option ready to add.' : `${bulkOptionValues.length} options ready to add.` }}
-
-
-
-
-
- Cancel
-
-
- Add options
-
-
-