diff --git a/img/screenshots/admin-catalog-thumb.png b/img/screenshots/admin-catalog-thumb.png
index 56ef073..4cb15d4 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 2eed1de..5a12f1e 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 8c913cd..a6fc4b6 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 b90fdb8..7ac853d 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 f4c0b7b..71a73c0 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 bc7d1af..be4ddfa 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 05c7159..ff06a20 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 a640bac..5a53fa3 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 04a9e80..fda4473 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 ef7fbff..9418520 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 8a4a0ae..2b532e6 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 425b4f3..ec2bc82 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 ef4c29c..b34f1ef 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 0000000..acd878a
--- /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 def5b99..f1a2750 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 264b0ef..1f9e513 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 0000000..7eb7036
--- /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 0000000..00a7996
--- /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 8fff336..7cb685f 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 a83c93a..f6cf87c 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
-
-
-