From 3653b841c1fe7712f6b53e3a27b060c2f8070a8c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20G=C3=B6bel?= <dgoebel@techfak.uni-bielefeld.de>
Date: Tue, 13 Feb 2024 11:36:48 +0100
Subject: [PATCH] Resolve "Add help for creating clomw_info.json"

---
 .gitlab-ci.yml                                |   1 +
 package-lock.json                             | 813 +++++++++---------
 package.json                                  |   2 +
 src/App.vue                                   |   2 +-
 src/components/CopyToClipboardIcon.vue        |   1 +
 src/components/DraggableLists.vue             | 104 +++
 src/components/NavbarTop.vue                  |   2 +-
 src/components/modals/BootstrapModal.vue      |   2 +-
 .../modals/BucketDetailModal.vue              |   1 +
 .../ParameterSchemaFormComponent.vue          |  51 +-
 .../form-mode/ParameterBooleanInput.vue       |  15 +-
 .../form-mode/ParameterEnumInput.vue          |  26 +-
 .../form-mode/ParameterGroupForm.vue          |  89 +-
 .../form-mode/ParameterNumberInput.vue        |  20 +-
 .../workflows/WorkflowDocumentationTabs.vue   |   2 +-
 .../workflows/WorkflowWithVersionsCard.vue    |  73 +-
 .../workflows/modals/ParameterModal.vue       |   9 +-
 .../modals/UpdateWorkflowCredentialsModal.vue |   3 +-
 .../workflows/modals/UpdateWorkflowModal.vue  |   1 +
 src/router/workflowRoutes.ts                  |  12 +
 src/utils/DownloadJson.ts                     |   6 +
 src/views/admin/AdminResourcesView.vue        |   2 +-
 src/views/admin/AdminUsersView.vue            |   2 +-
 src/views/object-storage/BucketsView.vue      |   5 +-
 src/views/object-storage/S3KeysView.vue       |   4 +-
 src/views/resources/ListResourcesView.vue     |   2 +-
 src/views/resources/MyResourcesView.vue       |   2 +-
 src/views/resources/ReviewResourceView.vue    |   2 +-
 src/views/workflows/ArbitraryWorkflowView.vue |  16 +-
 src/views/workflows/CreateClowmInfoView.vue   | 565 ++++++++++++
 .../workflows/ListWorkflowExecutionsView.vue  |   2 +-
 src/views/workflows/ListWorkflowsView.vue     |  14 +-
 src/views/workflows/MyWorkflowsView.vue       |   2 +-
 src/views/workflows/ReviewWorkflowsView.vue   |   2 +-
 src/views/workflows/StartWorkflowView.vue     |  20 +-
 35 files changed, 1279 insertions(+), 596 deletions(-)
 create mode 100644 src/components/DraggableLists.vue
 create mode 100644 src/utils/DownloadJson.ts
 create mode 100644 src/views/workflows/CreateClowmInfoView.vue

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index dc9685d..7f2ea9f 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -13,6 +13,7 @@ default:
     - docker
   before_script:
     - npm --version  # For debugging
+    - node --version
     - npm install --no-fund
 
 lint:
diff --git a/package-lock.json b/package-lock.json
index fd24c5c..d350098 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -25,6 +25,7 @@
         "pinia": "~2.1.0",
         "semver": "~7.5.0",
         "showdown": "~2.1.0",
+        "sortablejs": "^1.15.2",
         "vue": "~3.4.0",
         "vue-router": "~4.2.0",
         "vue3-cookies": "~1.0.0"
@@ -39,6 +40,7 @@
         "@types/node": "^18.19.5",
         "@types/semver": "~7.5.1",
         "@types/showdown": "~2.0.1",
+        "@types/sortablejs": "^1.15.7",
         "@vitejs/plugin-vue": "~5.0.0",
         "@vue/eslint-config-prettier": "~8.0.0",
         "@vue/eslint-config-typescript": "~11.0.3",
@@ -211,33 +213,33 @@
       "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
     },
     "node_modules/@aws-sdk/client-s3": {
-      "version": "3.507.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.507.0.tgz",
-      "integrity": "sha512-rRLiC5Ly3e7kZVNoRsG6JhZ8Yat5uEnDeShdWNdHchyTO88AaEnHaeyiVG9ecmKI8jYl6NbWSHB8xL0l9KIr/w==",
+      "version": "3.511.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.511.0.tgz",
+      "integrity": "sha512-IRUYev0KNKa5rQrpULE9IhJW6dhgGQWBmAJI+OyITHMu3uGvVHDqWKqnShV0IfMJWg1y37I3juFJ1KAti8jyHw==",
       "dependencies": {
         "@aws-crypto/sha1-browser": "3.0.0",
         "@aws-crypto/sha256-browser": "3.0.0",
         "@aws-crypto/sha256-js": "3.0.0",
-        "@aws-sdk/client-sts": "3.507.0",
-        "@aws-sdk/core": "3.496.0",
-        "@aws-sdk/credential-provider-node": "3.507.0",
-        "@aws-sdk/middleware-bucket-endpoint": "3.502.0",
-        "@aws-sdk/middleware-expect-continue": "3.502.0",
-        "@aws-sdk/middleware-flexible-checksums": "3.502.0",
-        "@aws-sdk/middleware-host-header": "3.502.0",
-        "@aws-sdk/middleware-location-constraint": "3.502.0",
-        "@aws-sdk/middleware-logger": "3.502.0",
-        "@aws-sdk/middleware-recursion-detection": "3.502.0",
-        "@aws-sdk/middleware-sdk-s3": "3.502.0",
-        "@aws-sdk/middleware-signing": "3.502.0",
-        "@aws-sdk/middleware-ssec": "3.502.0",
-        "@aws-sdk/middleware-user-agent": "3.502.0",
-        "@aws-sdk/region-config-resolver": "3.502.0",
-        "@aws-sdk/signature-v4-multi-region": "3.502.0",
-        "@aws-sdk/types": "3.502.0",
-        "@aws-sdk/util-endpoints": "3.502.0",
-        "@aws-sdk/util-user-agent-browser": "3.502.0",
-        "@aws-sdk/util-user-agent-node": "3.502.0",
+        "@aws-sdk/client-sts": "3.511.0",
+        "@aws-sdk/core": "3.511.0",
+        "@aws-sdk/credential-provider-node": "3.511.0",
+        "@aws-sdk/middleware-bucket-endpoint": "3.511.0",
+        "@aws-sdk/middleware-expect-continue": "3.511.0",
+        "@aws-sdk/middleware-flexible-checksums": "3.511.0",
+        "@aws-sdk/middleware-host-header": "3.511.0",
+        "@aws-sdk/middleware-location-constraint": "3.511.0",
+        "@aws-sdk/middleware-logger": "3.511.0",
+        "@aws-sdk/middleware-recursion-detection": "3.511.0",
+        "@aws-sdk/middleware-sdk-s3": "3.511.0",
+        "@aws-sdk/middleware-signing": "3.511.0",
+        "@aws-sdk/middleware-ssec": "3.511.0",
+        "@aws-sdk/middleware-user-agent": "3.511.0",
+        "@aws-sdk/region-config-resolver": "3.511.0",
+        "@aws-sdk/signature-v4-multi-region": "3.511.0",
+        "@aws-sdk/types": "3.511.0",
+        "@aws-sdk/util-endpoints": "3.511.0",
+        "@aws-sdk/util-user-agent-browser": "3.511.0",
+        "@aws-sdk/util-user-agent-node": "3.511.0",
         "@aws-sdk/xml-builder": "3.496.0",
         "@smithy/config-resolver": "^2.1.1",
         "@smithy/core": "^1.3.1",
@@ -279,22 +281,22 @@
       }
     },
     "node_modules/@aws-sdk/client-sso": {
-      "version": "3.507.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.507.0.tgz",
-      "integrity": "sha512-pFeaKwqv4tXD6QVxWC2V4N62DUoP3bPSm/mCe2SPhaNjNsmwwA53viUHz/nwxIbs8w4vV44UQsygb0AgKm+HoQ==",
+      "version": "3.511.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.511.0.tgz",
+      "integrity": "sha512-v1f5ZbuZWpad+fgTOpgFyIZT3A37wdqoSPh0hl+cKRu5kPsz96xCe9+UvLx+HdN2yJ/mV0UZcMq6ysj4xAGIEg==",
       "dependencies": {
         "@aws-crypto/sha256-browser": "3.0.0",
         "@aws-crypto/sha256-js": "3.0.0",
-        "@aws-sdk/core": "3.496.0",
-        "@aws-sdk/middleware-host-header": "3.502.0",
-        "@aws-sdk/middleware-logger": "3.502.0",
-        "@aws-sdk/middleware-recursion-detection": "3.502.0",
-        "@aws-sdk/middleware-user-agent": "3.502.0",
-        "@aws-sdk/region-config-resolver": "3.502.0",
-        "@aws-sdk/types": "3.502.0",
-        "@aws-sdk/util-endpoints": "3.502.0",
-        "@aws-sdk/util-user-agent-browser": "3.502.0",
-        "@aws-sdk/util-user-agent-node": "3.502.0",
+        "@aws-sdk/core": "3.511.0",
+        "@aws-sdk/middleware-host-header": "3.511.0",
+        "@aws-sdk/middleware-logger": "3.511.0",
+        "@aws-sdk/middleware-recursion-detection": "3.511.0",
+        "@aws-sdk/middleware-user-agent": "3.511.0",
+        "@aws-sdk/region-config-resolver": "3.511.0",
+        "@aws-sdk/types": "3.511.0",
+        "@aws-sdk/util-endpoints": "3.511.0",
+        "@aws-sdk/util-user-agent-browser": "3.511.0",
+        "@aws-sdk/util-user-agent-node": "3.511.0",
         "@smithy/config-resolver": "^2.1.1",
         "@smithy/core": "^1.3.1",
         "@smithy/fetch-http-handler": "^2.4.1",
@@ -326,24 +328,24 @@
       }
     },
     "node_modules/@aws-sdk/client-sso-oidc": {
-      "version": "3.507.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.507.0.tgz",
-      "integrity": "sha512-ms5CH2ImhqqCIbo5irxayByuPOlVAmSiqDVfjZKwgIziqng2bVgNZMeKcT6t0bmrcgScEAVnZwY7j/iZTIw73g==",
+      "version": "3.511.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.511.0.tgz",
+      "integrity": "sha512-cITRRq54eTrq7ll9li+yYnLbNHKXG2P+ovdZSDiQ6LjCYBdcD4ela30qbs87Yye9YsopdslDzBhHHtrf5oiuMw==",
       "dependencies": {
         "@aws-crypto/sha256-browser": "3.0.0",
         "@aws-crypto/sha256-js": "3.0.0",
-        "@aws-sdk/client-sts": "3.507.0",
-        "@aws-sdk/core": "3.496.0",
-        "@aws-sdk/middleware-host-header": "3.502.0",
-        "@aws-sdk/middleware-logger": "3.502.0",
-        "@aws-sdk/middleware-recursion-detection": "3.502.0",
-        "@aws-sdk/middleware-signing": "3.502.0",
-        "@aws-sdk/middleware-user-agent": "3.502.0",
-        "@aws-sdk/region-config-resolver": "3.502.0",
-        "@aws-sdk/types": "3.502.0",
-        "@aws-sdk/util-endpoints": "3.502.0",
-        "@aws-sdk/util-user-agent-browser": "3.502.0",
-        "@aws-sdk/util-user-agent-node": "3.502.0",
+        "@aws-sdk/client-sts": "3.511.0",
+        "@aws-sdk/core": "3.511.0",
+        "@aws-sdk/middleware-host-header": "3.511.0",
+        "@aws-sdk/middleware-logger": "3.511.0",
+        "@aws-sdk/middleware-recursion-detection": "3.511.0",
+        "@aws-sdk/middleware-signing": "3.511.0",
+        "@aws-sdk/middleware-user-agent": "3.511.0",
+        "@aws-sdk/region-config-resolver": "3.511.0",
+        "@aws-sdk/types": "3.511.0",
+        "@aws-sdk/util-endpoints": "3.511.0",
+        "@aws-sdk/util-user-agent-browser": "3.511.0",
+        "@aws-sdk/util-user-agent-node": "3.511.0",
         "@smithy/config-resolver": "^2.1.1",
         "@smithy/core": "^1.3.1",
         "@smithy/fetch-http-handler": "^2.4.1",
@@ -374,26 +376,26 @@
         "node": ">=14.0.0"
       },
       "peerDependencies": {
-        "@aws-sdk/credential-provider-node": "^3.507.0"
+        "@aws-sdk/credential-provider-node": "^3.511.0"
       }
     },
     "node_modules/@aws-sdk/client-sts": {
-      "version": "3.507.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.507.0.tgz",
-      "integrity": "sha512-TOWBe0ApEh32QOib0R+irWGjd1F9wnhbGV5PcB9SakyRwvqwG5MKOfYxG7ocoDqLlaRwzZMidcy/PV8/OEVNKg==",
+      "version": "3.511.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.511.0.tgz",
+      "integrity": "sha512-lwVEEXK+1auEwmBuTv35m2GvbxPthi8SjNUpU4pRetZPVbGhnhCN6H7JqeMDP6GLf81Io2eySXRsmLMt7l/fjg==",
       "dependencies": {
         "@aws-crypto/sha256-browser": "3.0.0",
         "@aws-crypto/sha256-js": "3.0.0",
-        "@aws-sdk/core": "3.496.0",
-        "@aws-sdk/middleware-host-header": "3.502.0",
-        "@aws-sdk/middleware-logger": "3.502.0",
-        "@aws-sdk/middleware-recursion-detection": "3.502.0",
-        "@aws-sdk/middleware-user-agent": "3.502.0",
-        "@aws-sdk/region-config-resolver": "3.502.0",
-        "@aws-sdk/types": "3.502.0",
-        "@aws-sdk/util-endpoints": "3.502.0",
-        "@aws-sdk/util-user-agent-browser": "3.502.0",
-        "@aws-sdk/util-user-agent-node": "3.502.0",
+        "@aws-sdk/core": "3.511.0",
+        "@aws-sdk/middleware-host-header": "3.511.0",
+        "@aws-sdk/middleware-logger": "3.511.0",
+        "@aws-sdk/middleware-recursion-detection": "3.511.0",
+        "@aws-sdk/middleware-user-agent": "3.511.0",
+        "@aws-sdk/region-config-resolver": "3.511.0",
+        "@aws-sdk/types": "3.511.0",
+        "@aws-sdk/util-endpoints": "3.511.0",
+        "@aws-sdk/util-user-agent-browser": "3.511.0",
+        "@aws-sdk/util-user-agent-node": "3.511.0",
         "@smithy/config-resolver": "^2.1.1",
         "@smithy/core": "^1.3.1",
         "@smithy/fetch-http-handler": "^2.4.1",
@@ -426,13 +428,13 @@
         "node": ">=14.0.0"
       },
       "peerDependencies": {
-        "@aws-sdk/credential-provider-node": "^3.507.0"
+        "@aws-sdk/credential-provider-node": "^3.511.0"
       }
     },
     "node_modules/@aws-sdk/core": {
-      "version": "3.496.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.496.0.tgz",
-      "integrity": "sha512-yT+ug7Cw/3eJi7x2es0+46x12+cIJm5Xv+GPWsrTFD1TKgqO/VPEgfDtHFagDNbFmjNQA65Ygc/kEdIX9ICX/A==",
+      "version": "3.511.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.511.0.tgz",
+      "integrity": "sha512-0gbDvQhToyLxPyr/7KP6uavrBYKh7exld2lju1Lp65U61XgEjTVP/thJmHTvH4BAKGSqeIz/rrwJ0KrC8nwBtw==",
       "dependencies": {
         "@smithy/core": "^1.3.1",
         "@smithy/protocol-http": "^3.1.1",
@@ -446,11 +448,11 @@
       }
     },
     "node_modules/@aws-sdk/credential-provider-env": {
-      "version": "3.502.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.502.0.tgz",
-      "integrity": "sha512-KIB8Ae1Z7domMU/jU4KiIgK4tmYgvuXlhR54ehwlVHxnEoFPoPuGHFZU7oFn79jhhSLUFQ1lRYMxP0cEwb7XeQ==",
+      "version": "3.511.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.511.0.tgz",
+      "integrity": "sha512-4VUsnLRox8YzxnZwnFrfZM4bL5KKLhsjjjX7oiuLyzFkhauI4HFYt7rTB8YNGphpqAg/Wzw5DBZfO3Bw1iR1HA==",
       "dependencies": {
-        "@aws-sdk/types": "3.502.0",
+        "@aws-sdk/types": "3.511.0",
         "@smithy/property-provider": "^2.1.1",
         "@smithy/types": "^2.9.1",
         "tslib": "^2.5.0"
@@ -460,11 +462,11 @@
       }
     },
     "node_modules/@aws-sdk/credential-provider-http": {
-      "version": "3.503.1",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.503.1.tgz",
-      "integrity": "sha512-rTdlFFGoPPFMF2YjtlfRuSgKI+XsF49u7d98255hySwhsbwd3Xp+utTTPquxP+CwDxMHbDlI7NxDzFiFdsoZug==",
+      "version": "3.511.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.511.0.tgz",
+      "integrity": "sha512-y83Gt8GPpgMe/lMFxIq+0G2rbzLTC6lhrDocHUzqcApLD6wet8Esy2iYckSRlJgYY+qsVAzpLrSMtt85DwRPTw==",
       "dependencies": {
-        "@aws-sdk/types": "3.502.0",
+        "@aws-sdk/types": "3.511.0",
         "@smithy/fetch-http-handler": "^2.4.1",
         "@smithy/node-http-handler": "^2.3.1",
         "@smithy/property-provider": "^2.1.1",
@@ -479,16 +481,16 @@
       }
     },
     "node_modules/@aws-sdk/credential-provider-ini": {
-      "version": "3.507.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.507.0.tgz",
-      "integrity": "sha512-2CnyduoR9COgd7qH1LPYK8UggGqVs8R4ASDMB5bwGxbg9ZerlStDiHpqvJNNg1k+VlejBr++utxfmHd236XgmQ==",
-      "dependencies": {
-        "@aws-sdk/client-sts": "3.507.0",
-        "@aws-sdk/credential-provider-env": "3.502.0",
-        "@aws-sdk/credential-provider-process": "3.502.0",
-        "@aws-sdk/credential-provider-sso": "3.507.0",
-        "@aws-sdk/credential-provider-web-identity": "3.507.0",
-        "@aws-sdk/types": "3.502.0",
+      "version": "3.511.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.511.0.tgz",
+      "integrity": "sha512-AgIOCtYzm61jbTQCY/2Vf/yu7DeLG0TLZa05a3VVRN9XE4ERtEnMn7TdbxM+hS24MTX8xI0HbMcWxCBkXRIg9w==",
+      "dependencies": {
+        "@aws-sdk/client-sts": "3.511.0",
+        "@aws-sdk/credential-provider-env": "3.511.0",
+        "@aws-sdk/credential-provider-process": "3.511.0",
+        "@aws-sdk/credential-provider-sso": "3.511.0",
+        "@aws-sdk/credential-provider-web-identity": "3.511.0",
+        "@aws-sdk/types": "3.511.0",
         "@smithy/credential-provider-imds": "^2.2.1",
         "@smithy/property-provider": "^2.1.1",
         "@smithy/shared-ini-file-loader": "^2.3.1",
@@ -500,17 +502,17 @@
       }
     },
     "node_modules/@aws-sdk/credential-provider-node": {
-      "version": "3.507.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.507.0.tgz",
-      "integrity": "sha512-tkQnmOLkRBXfMLgDYHzogrqTNdtl0Im0ipzJb2IV5hfM5NoTfCf795e9A9isgwjSP/g/YEU0xQWxa4lq8LRtuA==",
-      "dependencies": {
-        "@aws-sdk/credential-provider-env": "3.502.0",
-        "@aws-sdk/credential-provider-http": "3.503.1",
-        "@aws-sdk/credential-provider-ini": "3.507.0",
-        "@aws-sdk/credential-provider-process": "3.502.0",
-        "@aws-sdk/credential-provider-sso": "3.507.0",
-        "@aws-sdk/credential-provider-web-identity": "3.507.0",
-        "@aws-sdk/types": "3.502.0",
+      "version": "3.511.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.511.0.tgz",
+      "integrity": "sha512-5JDZXsSluliJmxOF+lYYFgJdSKQfVLQyic5NxScHULTERGoEwEHMgucFGwJ9MV9FoINjNTQLfAiWlJL/kGkCEQ==",
+      "dependencies": {
+        "@aws-sdk/credential-provider-env": "3.511.0",
+        "@aws-sdk/credential-provider-http": "3.511.0",
+        "@aws-sdk/credential-provider-ini": "3.511.0",
+        "@aws-sdk/credential-provider-process": "3.511.0",
+        "@aws-sdk/credential-provider-sso": "3.511.0",
+        "@aws-sdk/credential-provider-web-identity": "3.511.0",
+        "@aws-sdk/types": "3.511.0",
         "@smithy/credential-provider-imds": "^2.2.1",
         "@smithy/property-provider": "^2.1.1",
         "@smithy/shared-ini-file-loader": "^2.3.1",
@@ -522,11 +524,11 @@
       }
     },
     "node_modules/@aws-sdk/credential-provider-process": {
-      "version": "3.502.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.502.0.tgz",
-      "integrity": "sha512-fJJowOjQ4infYQX0E1J3xFVlmuwEYJAFk0Mo1qwafWmEthsBJs+6BR2RiWDELHKrSK35u4Pf3fu3RkYuCtmQFw==",
+      "version": "3.511.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.511.0.tgz",
+      "integrity": "sha512-88hLUPqcTwjSubPS+34ZfmglnKeLny8GbmZsyllk96l26PmDTAqo5RScSA8BWxL0l5pRRWGtcrFyts+oibHIuQ==",
       "dependencies": {
-        "@aws-sdk/types": "3.502.0",
+        "@aws-sdk/types": "3.511.0",
         "@smithy/property-provider": "^2.1.1",
         "@smithy/shared-ini-file-loader": "^2.3.1",
         "@smithy/types": "^2.9.1",
@@ -537,13 +539,13 @@
       }
     },
     "node_modules/@aws-sdk/credential-provider-sso": {
-      "version": "3.507.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.507.0.tgz",
-      "integrity": "sha512-6WBjou52QukFpDi4ezb19bcAx/bM8ge8qnJnRT02WVRmU6zFQ5yLD2fW1MFsbX3cwbey+wSqKd5FGE1Hukd5wQ==",
+      "version": "3.511.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.511.0.tgz",
+      "integrity": "sha512-aEei9UdXYEE2e0Htf28/IcuHcWk3VkUkpcg3KDR/AyzXA3i/kxmixtAgRmHOForC5CMqoJjzVPFUITNkAscyag==",
       "dependencies": {
-        "@aws-sdk/client-sso": "3.507.0",
-        "@aws-sdk/token-providers": "3.507.0",
-        "@aws-sdk/types": "3.502.0",
+        "@aws-sdk/client-sso": "3.511.0",
+        "@aws-sdk/token-providers": "3.511.0",
+        "@aws-sdk/types": "3.511.0",
         "@smithy/property-provider": "^2.1.1",
         "@smithy/shared-ini-file-loader": "^2.3.1",
         "@smithy/types": "^2.9.1",
@@ -554,12 +556,12 @@
       }
     },
     "node_modules/@aws-sdk/credential-provider-web-identity": {
-      "version": "3.507.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.507.0.tgz",
-      "integrity": "sha512-f+aGMfazBimX7S06224JRYzGTaMh1uIhfj23tZylPJ05KxTVi5IO1RoqeI/uHLJ+bDOx+JHBC04g/oCdO4kHvw==",
+      "version": "3.511.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.511.0.tgz",
+      "integrity": "sha512-/3XMyN7YYefAsES/sMMY5zZGRmZ5QJisJw798DdMYmYMsb1dt0Qy8kZTu+59ZzOiVIcznsjSTCEB81QmGtDKcA==",
       "dependencies": {
-        "@aws-sdk/client-sts": "3.507.0",
-        "@aws-sdk/types": "3.502.0",
+        "@aws-sdk/client-sts": "3.511.0",
+        "@aws-sdk/types": "3.511.0",
         "@smithy/property-provider": "^2.1.1",
         "@smithy/types": "^2.9.1",
         "tslib": "^2.5.0"
@@ -569,9 +571,9 @@
       }
     },
     "node_modules/@aws-sdk/lib-storage": {
-      "version": "3.507.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/lib-storage/-/lib-storage-3.507.0.tgz",
-      "integrity": "sha512-cwCBGw6W71d0PrFSRizBd7WdyzsZnRWVftnOvNkyjIE5vFifdFbpdVaFidMVUZmvUQvS3Vgkx1LeXPT98EBWUQ==",
+      "version": "3.511.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/lib-storage/-/lib-storage-3.511.0.tgz",
+      "integrity": "sha512-inEbSyqzGxiQs8aEnkGdxw9ZDn370mRHOdE1TB/GvVe9buQVyZ2hQvOY5WBVOaIGDIxGpuUzVvr4o89XreU19w==",
       "dependencies": {
         "@smithy/abort-controller": "^2.1.1",
         "@smithy/middleware-endpoint": "^2.4.1",
@@ -589,11 +591,11 @@
       }
     },
     "node_modules/@aws-sdk/middleware-bucket-endpoint": {
-      "version": "3.502.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.502.0.tgz",
-      "integrity": "sha512-mUSP2DUcjhO5zM2b21CvZ9AqwI8DaAeZA6NYHOxWGTV9BUxHcdGWXEjDkcVj9CQ0gvNwTtw6B5L/q52rVAnZbw==",
+      "version": "3.511.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.511.0.tgz",
+      "integrity": "sha512-G4dAAHPUZbpDCVBaCcAOlFoctO9lcecSs0EZYrvzQc/9d4XJvNWGd1C7GSdf204VPOCPZCjNpTkdWGm25r00wA==",
       "dependencies": {
-        "@aws-sdk/types": "3.502.0",
+        "@aws-sdk/types": "3.511.0",
         "@aws-sdk/util-arn-parser": "3.495.0",
         "@smithy/node-config-provider": "^2.2.1",
         "@smithy/protocol-http": "^3.1.1",
@@ -606,11 +608,11 @@
       }
     },
     "node_modules/@aws-sdk/middleware-expect-continue": {
-      "version": "3.502.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.502.0.tgz",
-      "integrity": "sha512-DxfAuBVuPSt8as9xP57o8ks6ySVSjwO2NNNAdpLwk4KhEAPYEpHlf2yWYorYLrS+dDmwfYgOhRNoguuBdCu6ow==",
+      "version": "3.511.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.511.0.tgz",
+      "integrity": "sha512-zjDzrJV9PFCkEqhNLKKK+9PB1vPveVZLJbcY71V3PZFvPII1bhlgwvI1e99MhEiaiH2a9I2PnS56bGwEKuNTrw==",
       "dependencies": {
-        "@aws-sdk/types": "3.502.0",
+        "@aws-sdk/types": "3.511.0",
         "@smithy/protocol-http": "^3.1.1",
         "@smithy/types": "^2.9.1",
         "tslib": "^2.5.0"
@@ -620,13 +622,13 @@
       }
     },
     "node_modules/@aws-sdk/middleware-flexible-checksums": {
-      "version": "3.502.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.502.0.tgz",
-      "integrity": "sha512-kCt2zQDFumz/LnJJJOSd2GW4dr8oT8YMJKgxC/pph3aRXoSHXRwhrMbFnQ8swEE9vjywxtcED8sym0b0tNhhoA==",
+      "version": "3.511.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.511.0.tgz",
+      "integrity": "sha512-oI8zULi6VXLXJ3zA6aCdbOoceSNOxGITosB7EKDsLllzAQFV1WlzmQCtjFY8DLLYZ521atgJNcVbzjxPQnrnJA==",
       "dependencies": {
         "@aws-crypto/crc32": "3.0.0",
         "@aws-crypto/crc32c": "3.0.0",
-        "@aws-sdk/types": "3.502.0",
+        "@aws-sdk/types": "3.511.0",
         "@smithy/is-array-buffer": "^2.1.1",
         "@smithy/protocol-http": "^3.1.1",
         "@smithy/types": "^2.9.1",
@@ -638,11 +640,11 @@
       }
     },
     "node_modules/@aws-sdk/middleware-host-header": {
-      "version": "3.502.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.502.0.tgz",
-      "integrity": "sha512-EjnG0GTYXT/wJBmm5/mTjDcAkzU8L7wQjOzd3FTXuTCNNyvAvwrszbOj5FlarEw5XJBbQiZtBs+I5u9+zy560w==",
+      "version": "3.511.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.511.0.tgz",
+      "integrity": "sha512-DbBzQP/6woSHR/+g9dHN3YiYaLIqFw9u8lQFMxi3rT3hqITFVYLzzXtEaHjDD6/is56pNT84CIKbyJ6/gY5d1Q==",
       "dependencies": {
-        "@aws-sdk/types": "3.502.0",
+        "@aws-sdk/types": "3.511.0",
         "@smithy/protocol-http": "^3.1.1",
         "@smithy/types": "^2.9.1",
         "tslib": "^2.5.0"
@@ -652,11 +654,11 @@
       }
     },
     "node_modules/@aws-sdk/middleware-location-constraint": {
-      "version": "3.502.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.502.0.tgz",
-      "integrity": "sha512-fLRwPuTZvEWQkPjys03m3D6tYN4kf7zU6+c8mJxwvEg+yfBuv2RBsbd+Vn2bTisUjXvIg1kyBzONlpHoIyFneg==",
+      "version": "3.511.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.511.0.tgz",
+      "integrity": "sha512-PKHnOT3oBo41NELq3Esz3K9JuV1l9E+SrCcfr/07yU4EbqhS4UGPb22Yf5JakQu4fGbTFlAftcc8PXcE2zLr4g==",
       "dependencies": {
-        "@aws-sdk/types": "3.502.0",
+        "@aws-sdk/types": "3.511.0",
         "@smithy/types": "^2.9.1",
         "tslib": "^2.5.0"
       },
@@ -665,11 +667,11 @@
       }
     },
     "node_modules/@aws-sdk/middleware-logger": {
-      "version": "3.502.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.502.0.tgz",
-      "integrity": "sha512-FDyv6K4nCoHxbjLGS2H8ex8I0KDIiu4FJgVRPs140ZJy6gE5Pwxzv6YTzZGLMrnqcIs9gh065Lf6DjwMelZqaw==",
+      "version": "3.511.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.511.0.tgz",
+      "integrity": "sha512-EYU9dBlJXvQcCsM2Tfgi0NQoXrqovfDv/fDy8oGJgZFrgNuHDti8tdVVxeJTUJNEAF67xlDl5o+rWEkKthkYGQ==",
       "dependencies": {
-        "@aws-sdk/types": "3.502.0",
+        "@aws-sdk/types": "3.511.0",
         "@smithy/types": "^2.9.1",
         "tslib": "^2.5.0"
       },
@@ -678,11 +680,11 @@
       }
     },
     "node_modules/@aws-sdk/middleware-recursion-detection": {
-      "version": "3.502.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.502.0.tgz",
-      "integrity": "sha512-hvbyGJbxeuezxOu8VfFmcV4ql1hKXLxHTe5FNYfEBat2KaZXVhc1Hg+4TvB06/53p+E8J99Afmumkqbxs2esUA==",
+      "version": "3.511.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.511.0.tgz",
+      "integrity": "sha512-PlNPCV/6zpDVdNx1K69xDTh/wPNU4WyP4qa6hUo2/+4/PNG5HI9xbCWtpb4RjhdTRw6qDtkBNcPICHbtWx5aHg==",
       "dependencies": {
-        "@aws-sdk/types": "3.502.0",
+        "@aws-sdk/types": "3.511.0",
         "@smithy/protocol-http": "^3.1.1",
         "@smithy/types": "^2.9.1",
         "tslib": "^2.5.0"
@@ -692,11 +694,11 @@
       }
     },
     "node_modules/@aws-sdk/middleware-sdk-s3": {
-      "version": "3.502.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.502.0.tgz",
-      "integrity": "sha512-GbGugrfyL5bNA/zw8iQll92yXBONfWSC8Ns00DtkOU1saPXp4/7WHtyyZGYdvPa73T1IsuZy9egpoYRBmRcd5Q==",
+      "version": "3.511.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.511.0.tgz",
+      "integrity": "sha512-SKJr8mKaqjcGpu0xxRPXZiKrJmyetDfgzvWuZ7QOgdnPa+6jk5fmEUTFoPb3VCarMkf8xo/l6cTZ5lei7Lbflw==",
       "dependencies": {
-        "@aws-sdk/types": "3.502.0",
+        "@aws-sdk/types": "3.511.0",
         "@aws-sdk/util-arn-parser": "3.495.0",
         "@smithy/node-config-provider": "^2.2.1",
         "@smithy/protocol-http": "^3.1.1",
@@ -711,11 +713,11 @@
       }
     },
     "node_modules/@aws-sdk/middleware-signing": {
-      "version": "3.502.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.502.0.tgz",
-      "integrity": "sha512-4hF08vSzJ7L6sB+393gOFj3s2N6nLusYS0XrMW6wYNFU10IDdbf8Z3TZ7gysDJJHEGQPmTAesPEDBsasGWcMxg==",
+      "version": "3.511.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.511.0.tgz",
+      "integrity": "sha512-IMijFLfm+QQHD6NNDX9k3op9dpBSlWKnqjcMU38Tytl2nbqV4gktkarOK1exHAmH7CdoYR5BufVtBzbASNSF/A==",
       "dependencies": {
-        "@aws-sdk/types": "3.502.0",
+        "@aws-sdk/types": "3.511.0",
         "@smithy/property-provider": "^2.1.1",
         "@smithy/protocol-http": "^3.1.1",
         "@smithy/signature-v4": "^2.1.1",
@@ -728,11 +730,11 @@
       }
     },
     "node_modules/@aws-sdk/middleware-ssec": {
-      "version": "3.502.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.502.0.tgz",
-      "integrity": "sha512-1nidVTIba6/aVjjzD/WNqWdzSyTrXOHO3Ddz2MGD8S1yGSrYz4iYaq4Bm/uosfdr8B1L0Ws0pjdRXrNfzSw/DQ==",
+      "version": "3.511.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.511.0.tgz",
+      "integrity": "sha512-8pfgBard9pj7oWJ79R6dbXHUGr7JPP/OmAsKBYZA0r/91a1XdFUDtRYZadstjcOv/X3QbeG3QqWOtNco+XgM7Q==",
       "dependencies": {
-        "@aws-sdk/types": "3.502.0",
+        "@aws-sdk/types": "3.511.0",
         "@smithy/types": "^2.9.1",
         "tslib": "^2.5.0"
       },
@@ -741,12 +743,12 @@
       }
     },
     "node_modules/@aws-sdk/middleware-user-agent": {
-      "version": "3.502.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.502.0.tgz",
-      "integrity": "sha512-TxbBZbRiXPH0AUxegqiNd9aM9zNSbfjtBs5MEfcBsweeT/B2O7K1EjP9+CkB8Xmk/5FLKhAKLr19b1TNoE27rw==",
+      "version": "3.511.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.511.0.tgz",
+      "integrity": "sha512-eLs+CxP2QCXh3tCGYCdAml3oyWj8MSIwKbH+8rKw0k/5vmY1YJDBy526whOxx61ivhz2e0muuijN4X5EZZ2Pnw==",
       "dependencies": {
-        "@aws-sdk/types": "3.502.0",
-        "@aws-sdk/util-endpoints": "3.502.0",
+        "@aws-sdk/types": "3.511.0",
+        "@aws-sdk/util-endpoints": "3.511.0",
         "@smithy/protocol-http": "^3.1.1",
         "@smithy/types": "^2.9.1",
         "tslib": "^2.5.0"
@@ -756,11 +758,11 @@
       }
     },
     "node_modules/@aws-sdk/region-config-resolver": {
-      "version": "3.502.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.502.0.tgz",
-      "integrity": "sha512-mxmsX2AGgnSM+Sah7mcQCIneOsJQNiLX0COwEttuf8eO+6cLMAZvVudH3BnWTfea4/A9nuri9DLCqBvEmPrilg==",
+      "version": "3.511.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.511.0.tgz",
+      "integrity": "sha512-RzBLSNaRd4iEkQyEGfiSNvSnWU/x23rsiFgA9tqYFA0Vqx7YmzSWC8QBUxpwybB8HkbbL9wNVKQqTbhI3mYneQ==",
       "dependencies": {
-        "@aws-sdk/types": "3.502.0",
+        "@aws-sdk/types": "3.511.0",
         "@smithy/node-config-provider": "^2.2.1",
         "@smithy/types": "^2.9.1",
         "@smithy/util-config-provider": "^2.2.1",
@@ -772,13 +774,13 @@
       }
     },
     "node_modules/@aws-sdk/s3-request-presigner": {
-      "version": "3.507.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.507.0.tgz",
-      "integrity": "sha512-A3EGvXMeOvnG+qtAsmlcQyLP7+PlCePS+PsVqLm3Pz3C16avOTxTqOZIkYCqBBX2fnASr2qUr0d3cezBfsU7PQ==",
+      "version": "3.511.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.511.0.tgz",
+      "integrity": "sha512-CZRAA5Ru67DEStvz3i3yyS79oAPCXC5bqow5YWxAm6vkTydkA/Ybvim24T3EUDye6ParZvAtFhVV72odo5bitg==",
       "dependencies": {
-        "@aws-sdk/signature-v4-multi-region": "3.502.0",
-        "@aws-sdk/types": "3.502.0",
-        "@aws-sdk/util-format-url": "3.502.0",
+        "@aws-sdk/signature-v4-multi-region": "3.511.0",
+        "@aws-sdk/types": "3.511.0",
+        "@aws-sdk/util-format-url": "3.511.0",
         "@smithy/middleware-endpoint": "^2.4.1",
         "@smithy/protocol-http": "^3.1.1",
         "@smithy/smithy-client": "^2.3.1",
@@ -790,12 +792,12 @@
       }
     },
     "node_modules/@aws-sdk/signature-v4-multi-region": {
-      "version": "3.502.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.502.0.tgz",
-      "integrity": "sha512-NpOXtUXH0ZAgnyI3Y3s2fPrgwbsWoNMwdoXdFZvH0eDzzX80tim7Yuy6dzVA5zrxSzOYs1xjcOhM+4CmM0QZiw==",
+      "version": "3.511.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.511.0.tgz",
+      "integrity": "sha512-lwbU3LX5TpYu1DHBMH2Wz+2MWGccn5G3psu1Y9WTPc+1bubVQHWf8UD2lzON5L2QirT9tQheQjTke1u5JC7FTQ==",
       "dependencies": {
-        "@aws-sdk/middleware-sdk-s3": "3.502.0",
-        "@aws-sdk/types": "3.502.0",
+        "@aws-sdk/middleware-sdk-s3": "3.511.0",
+        "@aws-sdk/types": "3.511.0",
         "@smithy/protocol-http": "^3.1.1",
         "@smithy/signature-v4": "^2.1.1",
         "@smithy/types": "^2.9.1",
@@ -806,12 +808,12 @@
       }
     },
     "node_modules/@aws-sdk/token-providers": {
-      "version": "3.507.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.507.0.tgz",
-      "integrity": "sha512-ehOINGjoGJc6Puzon7ev4bXckkaZx18WNgMTNttYJhj3vTpj5LPSQbI/5SS927bEbpGMFz1+hJ6Ra5WGfbTcEQ==",
+      "version": "3.511.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.511.0.tgz",
+      "integrity": "sha512-92dXjMHBJcRoUkJHc0Bvtsz7Sal8t6VASRJ5vfs5c2ZpTVgLpVnM4dBmwUgGUdnvHov0cZTXbbadTJ/qOWx5Zw==",
       "dependencies": {
-        "@aws-sdk/client-sso-oidc": "3.507.0",
-        "@aws-sdk/types": "3.502.0",
+        "@aws-sdk/client-sso-oidc": "3.511.0",
+        "@aws-sdk/types": "3.511.0",
         "@smithy/property-provider": "^2.1.1",
         "@smithy/shared-ini-file-loader": "^2.3.1",
         "@smithy/types": "^2.9.1",
@@ -822,9 +824,9 @@
       }
     },
     "node_modules/@aws-sdk/types": {
-      "version": "3.502.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.502.0.tgz",
-      "integrity": "sha512-M0DSPYe/gXhwD2QHgoukaZv5oDxhW3FfvYIrJptyqUq3OnPJBcDbihHjrE0PBtfh/9kgMZT60/fQ2NVFANfa2g==",
+      "version": "3.511.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.511.0.tgz",
+      "integrity": "sha512-P03ufufxmkvd7nO46oOeEqYIMPJ8qMCKxAsfJk1JBVPQ1XctVntbail4/UFnrnzij8DTl4Mk/D62uGo7+RolXA==",
       "dependencies": {
         "@smithy/types": "^2.9.1",
         "tslib": "^2.5.0"
@@ -845,11 +847,11 @@
       }
     },
     "node_modules/@aws-sdk/util-endpoints": {
-      "version": "3.502.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.502.0.tgz",
-      "integrity": "sha512-6LKFlJPp2J24r1Kpfoz5ESQn+1v5fEjDB3mtUKRdpwarhm3syu7HbKlHCF3KbcCOyahobvLvhoedT78rJFEeeg==",
+      "version": "3.511.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.511.0.tgz",
+      "integrity": "sha512-J/5hsscJkg2pAOdLx1YKlyMCk5lFRxRxEtup9xipzOxVBlqOIE72Tuu31fbxSlF8XzO/AuCJcZL4m1v098K9oA==",
       "dependencies": {
-        "@aws-sdk/types": "3.502.0",
+        "@aws-sdk/types": "3.511.0",
         "@smithy/types": "^2.9.1",
         "@smithy/util-endpoints": "^1.1.1",
         "tslib": "^2.5.0"
@@ -859,11 +861,11 @@
       }
     },
     "node_modules/@aws-sdk/util-format-url": {
-      "version": "3.502.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.502.0.tgz",
-      "integrity": "sha512-4+0zBD0ZIJqtTzSE6VRruRwUx3lG+is8Egv+LN99X5y7i6OdrS9ePYHbCJ9FxkzTThgbkUq6k2W7psEDYvn4VA==",
+      "version": "3.511.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.511.0.tgz",
+      "integrity": "sha512-2BycrBtplIGAtzjj5YYLGrDBQDHR0zTct9bWBVhSfI0w2YAWAvxfRmXG4Dd1FF5ZxTm2xB9lA2u8FKim7ZKD8Q==",
       "dependencies": {
-        "@aws-sdk/types": "3.502.0",
+        "@aws-sdk/types": "3.511.0",
         "@smithy/querystring-builder": "^2.1.1",
         "@smithy/types": "^2.9.1",
         "tslib": "^2.5.0"
@@ -884,22 +886,22 @@
       }
     },
     "node_modules/@aws-sdk/util-user-agent-browser": {
-      "version": "3.502.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.502.0.tgz",
-      "integrity": "sha512-v8gKyCs2obXoIkLETAeEQ3AM+QmhHhst9xbM1cJtKUGsRlVIak/XyyD+kVE6kmMm1cjfudHpHKABWk9apQcIZQ==",
+      "version": "3.511.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.511.0.tgz",
+      "integrity": "sha512-5LuESdwtIcA10aHcX7pde7aCIijcyTPBXFuXmFlDTgm/naAayQxelQDpvgbzuzGLgePf8eTyyhDKhzwPZ2EqiQ==",
       "dependencies": {
-        "@aws-sdk/types": "3.502.0",
+        "@aws-sdk/types": "3.511.0",
         "@smithy/types": "^2.9.1",
         "bowser": "^2.11.0",
         "tslib": "^2.5.0"
       }
     },
     "node_modules/@aws-sdk/util-user-agent-node": {
-      "version": "3.502.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.502.0.tgz",
-      "integrity": "sha512-9RjxpkGZKbTdl96tIJvAo+vZoz4P/cQh36SBUt9xfRfW0BtsaLyvSrvlR5wyUYhvRcC12Axqh/8JtnAPq//+Vw==",
+      "version": "3.511.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.511.0.tgz",
+      "integrity": "sha512-UopdlRvYY5mxlS4wwFv+QAWL6/T302wmoQj7i+RY+c/D3Ej3PKBb/mW3r2wEOgZLJmPpeeM1SYMk+rVmsW1rqw==",
       "dependencies": {
-        "@aws-sdk/types": "3.502.0",
+        "@aws-sdk/types": "3.511.0",
         "@smithy/node-config-provider": "^2.2.1",
         "@smithy/types": "^2.9.1",
         "tslib": "^2.5.0"
@@ -1553,9 +1555,9 @@
       }
     },
     "node_modules/@rollup/rollup-android-arm-eabi": {
-      "version": "4.9.6",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.6.tgz",
-      "integrity": "sha512-MVNXSSYN6QXOulbHpLMKYi60ppyO13W9my1qogeiAqtjb2yR4LSmfU2+POvDkLzhjYLXz9Rf9+9a3zFHW1Lecg==",
+      "version": "4.10.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.10.0.tgz",
+      "integrity": "sha512-/MeDQmcD96nVoRumKUljsYOLqfv1YFJps+0pTrb2Z9Nl/w5qNUysMaWQsrd1mvAlNT4yza1iVyIu4Q4AgF6V3A==",
       "cpu": [
         "arm"
       ],
@@ -1566,9 +1568,9 @@
       ]
     },
     "node_modules/@rollup/rollup-android-arm64": {
-      "version": "4.9.6",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.6.tgz",
-      "integrity": "sha512-T14aNLpqJ5wzKNf5jEDpv5zgyIqcpn1MlwCrUXLrwoADr2RkWA0vOWP4XxbO9aiO3dvMCQICZdKeDrFl7UMClw==",
+      "version": "4.10.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.10.0.tgz",
+      "integrity": "sha512-lvu0jK97mZDJdpZKDnZI93I0Om8lSDaiPx3OiCk0RXn3E8CMPJNS/wxjAvSJJzhhZpfjXsjLWL8LnS6qET4VNQ==",
       "cpu": [
         "arm64"
       ],
@@ -1579,9 +1581,9 @@
       ]
     },
     "node_modules/@rollup/rollup-darwin-arm64": {
-      "version": "4.9.6",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.6.tgz",
-      "integrity": "sha512-CqNNAyhRkTbo8VVZ5R85X73H3R5NX9ONnKbXuHisGWC0qRbTTxnF1U4V9NafzJbgGM0sHZpdO83pLPzq8uOZFw==",
+      "version": "4.10.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.10.0.tgz",
+      "integrity": "sha512-uFpayx8I8tyOvDkD7X6n0PriDRWxcqEjqgtlxnUA/G9oS93ur9aZ8c8BEpzFmsed1TH5WZNG5IONB8IiW90TQg==",
       "cpu": [
         "arm64"
       ],
@@ -1592,9 +1594,9 @@
       ]
     },
     "node_modules/@rollup/rollup-darwin-x64": {
-      "version": "4.9.6",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.6.tgz",
-      "integrity": "sha512-zRDtdJuRvA1dc9Mp6BWYqAsU5oeLixdfUvkTHuiYOHwqYuQ4YgSmi6+/lPvSsqc/I0Omw3DdICx4Tfacdzmhog==",
+      "version": "4.10.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.10.0.tgz",
+      "integrity": "sha512-nIdCX03qFKoR/MwQegQBK+qZoSpO3LESurVAC6s6jazLA1Mpmgzo3Nj3H1vydXp/JM29bkCiuF7tDuToj4+U9Q==",
       "cpu": [
         "x64"
       ],
@@ -1605,9 +1607,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
-      "version": "4.9.6",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.6.tgz",
-      "integrity": "sha512-oNk8YXDDnNyG4qlNb6is1ojTOGL/tRhbbKeE/YuccItzerEZT68Z9gHrY3ROh7axDc974+zYAPxK5SH0j/G+QQ==",
+      "version": "4.10.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.10.0.tgz",
+      "integrity": "sha512-Fz7a+y5sYhYZMQFRkOyCs4PLhICAnxRX/GnWYReaAoruUzuRtcf+Qnw+T0CoAWbHCuz2gBUwmWnUgQ67fb3FYw==",
       "cpu": [
         "arm"
       ],
@@ -1618,9 +1620,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-arm64-gnu": {
-      "version": "4.9.6",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.6.tgz",
-      "integrity": "sha512-Z3O60yxPtuCYobrtzjo0wlmvDdx2qZfeAWTyfOjEDqd08kthDKexLpV97KfAeUXPosENKd8uyJMRDfFMxcYkDQ==",
+      "version": "4.10.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.10.0.tgz",
+      "integrity": "sha512-yPtF9jIix88orwfTi0lJiqINnlWo6p93MtZEoaehZnmCzEmLL0eqjA3eGVeyQhMtxdV+Mlsgfwhh0+M/k1/V7Q==",
       "cpu": [
         "arm64"
       ],
@@ -1631,9 +1633,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-arm64-musl": {
-      "version": "4.9.6",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.6.tgz",
-      "integrity": "sha512-gpiG0qQJNdYEVad+1iAsGAbgAnZ8j07FapmnIAQgODKcOTjLEWM9sRb+MbQyVsYCnA0Im6M6QIq6ax7liws6eQ==",
+      "version": "4.10.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.10.0.tgz",
+      "integrity": "sha512-9GW9yA30ib+vfFiwjX+N7PnjTnCMiUffhWj4vkG4ukYv1kJ4T9gHNg8zw+ChsOccM27G9yXrEtMScf1LaCuoWQ==",
       "cpu": [
         "arm64"
       ],
@@ -1644,9 +1646,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-riscv64-gnu": {
-      "version": "4.9.6",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.6.tgz",
-      "integrity": "sha512-+uCOcvVmFUYvVDr27aiyun9WgZk0tXe7ThuzoUTAukZJOwS5MrGbmSlNOhx1j80GdpqbOty05XqSl5w4dQvcOA==",
+      "version": "4.10.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.10.0.tgz",
+      "integrity": "sha512-X1ES+V4bMq2ws5fF4zHornxebNxMXye0ZZjUrzOrf7UMx1d6wMQtfcchZ8SqUnQPPHdOyOLW6fTcUiFgHFadRA==",
       "cpu": [
         "riscv64"
       ],
@@ -1657,9 +1659,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-x64-gnu": {
-      "version": "4.9.6",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.6.tgz",
-      "integrity": "sha512-HUNqM32dGzfBKuaDUBqFB7tP6VMN74eLZ33Q9Y1TBqRDn+qDonkAUyKWwF9BR9unV7QUzffLnz9GrnKvMqC/fw==",
+      "version": "4.10.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.10.0.tgz",
+      "integrity": "sha512-w/5OpT2EnI/Xvypw4FIhV34jmNqU5PZjZue2l2Y3ty1Ootm3SqhI+AmfhlUYGBTd9JnpneZCDnt3uNOiOBkMyw==",
       "cpu": [
         "x64"
       ],
@@ -1670,9 +1672,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-x64-musl": {
-      "version": "4.9.6",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.6.tgz",
-      "integrity": "sha512-ch7M+9Tr5R4FK40FHQk8VnML0Szi2KRujUgHXd/HjuH9ifH72GUmw6lStZBo3c3GB82vHa0ZoUfjfcM7JiiMrQ==",
+      "version": "4.10.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.10.0.tgz",
+      "integrity": "sha512-q/meftEe3QlwQiGYxD9rWwB21DoKQ9Q8wA40of/of6yGHhZuGfZO0c3WYkN9dNlopHlNT3mf5BPsUSxoPuVQaw==",
       "cpu": [
         "x64"
       ],
@@ -1683,9 +1685,9 @@
       ]
     },
     "node_modules/@rollup/rollup-win32-arm64-msvc": {
-      "version": "4.9.6",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.6.tgz",
-      "integrity": "sha512-VD6qnR99dhmTQ1mJhIzXsRcTBvTjbfbGGwKAHcu+52cVl15AC/kplkhxzW/uT0Xl62Y/meBKDZvoJSJN+vTeGA==",
+      "version": "4.10.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.10.0.tgz",
+      "integrity": "sha512-NrR6667wlUfP0BHaEIKgYM/2va+Oj+RjZSASbBMnszM9k+1AmliRjHc3lJIiOehtSSjqYiO7R6KLNrWOX+YNSQ==",
       "cpu": [
         "arm64"
       ],
@@ -1696,9 +1698,9 @@
       ]
     },
     "node_modules/@rollup/rollup-win32-ia32-msvc": {
-      "version": "4.9.6",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.6.tgz",
-      "integrity": "sha512-J9AFDq/xiRI58eR2NIDfyVmTYGyIZmRcvcAoJ48oDld/NTR8wyiPUu2X/v1navJ+N/FGg68LEbX3Ejd6l8B7MQ==",
+      "version": "4.10.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.10.0.tgz",
+      "integrity": "sha512-FV0Tpt84LPYDduIDcXvEC7HKtyXxdvhdAOvOeWMWbQNulxViH2O07QXkT/FffX4FqEI02jEbCJbr+YcuKdyyMg==",
       "cpu": [
         "ia32"
       ],
@@ -1709,9 +1711,9 @@
       ]
     },
     "node_modules/@rollup/rollup-win32-x64-msvc": {
-      "version": "4.9.6",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.6.tgz",
-      "integrity": "sha512-jqzNLhNDvIZOrt69Ce4UjGRpXJBzhUBzawMwnaDAwyHriki3XollsewxWzOzz+4yOFDkuJHtTsZFwMxhYJWmLQ==",
+      "version": "4.10.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.10.0.tgz",
+      "integrity": "sha512-OZoJd+o5TaTSQeFFQ6WjFCiltiYVjIdsXxwu/XZ8qRpsvMQr4UsVrE5UyT9RIvsnuF47DqkJKhhVZ2Q9YW9IpQ==",
       "cpu": [
         "x64"
       ],
@@ -1772,9 +1774,9 @@
       }
     },
     "node_modules/@smithy/core": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/@smithy/core/-/core-1.3.1.tgz",
-      "integrity": "sha512-tf+NIu9FkOh312b6M9G4D68is4Xr7qptzaZGZUREELF8ysE1yLKphqt7nsomjKZVwW7WE5pDDex9idowNGRQ/Q==",
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/@smithy/core/-/core-1.3.2.tgz",
+      "integrity": "sha512-tYDmTp0f2TZVE18jAOH1PnmkngLQ+dOGUlMd1u67s87ieueNeyqhja6z/Z4MxhybEiXKOWFOmGjfTZWFxljwJw==",
       "dependencies": {
         "@smithy/middleware-endpoint": "^2.4.1",
         "@smithy/middleware-retry": "^2.1.1",
@@ -2245,9 +2247,9 @@
       }
     },
     "node_modules/@smithy/util-defaults-mode-node": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.1.1.tgz",
-      "integrity": "sha512-tYVrc+w+jSBfBd267KDnvSGOh4NMz+wVH7v4CClDbkdPfnjvImBZsOURncT5jsFwR9KCuDyPoSZq4Pa6+eCUrA==",
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.2.0.tgz",
+      "integrity": "sha512-iFJp/N4EtkanFpBUtSrrIbtOIBf69KNuve03ic1afhJ9/korDxdM0c6cCH4Ehj/smI9pDCfVv+bqT3xZjF2WaA==",
       "dependencies": {
         "@smithy/config-resolver": "^2.1.1",
         "@smithy/credential-provider-imds": "^2.2.1",
@@ -2416,18 +2418,18 @@
       }
     },
     "node_modules/@types/node": {
-      "version": "18.19.14",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.14.tgz",
-      "integrity": "sha512-EnQ4Us2rmOS64nHDWr0XqAD8DsO6f3XR6lf9UIIrZQpUzPVdN/oPuEzfDWNHSyXLvoGgjuEm/sPwFGSSs35Wtg==",
+      "version": "18.19.15",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.15.tgz",
+      "integrity": "sha512-AMZ2UWx+woHNfM11PyAEQmfSxi05jm9OlkxczuHeEqmvwPkYj6MWv44gbzDPefYOLysTOFyI3ziiy2ONmUZfpA==",
       "dev": true,
       "dependencies": {
         "undici-types": "~5.26.4"
       }
     },
     "node_modules/@types/semver": {
-      "version": "7.5.6",
-      "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz",
-      "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==",
+      "version": "7.5.7",
+      "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.7.tgz",
+      "integrity": "sha512-/wdoPq1QqkSj9/QOeKkFquEuPzQbHTWAMPH/PaUMB+JuR31lXhlWXRZ52IpfDYVlDOUBvX09uBrPwxGT1hjNBg==",
       "dev": true
     },
     "node_modules/@types/showdown": {
@@ -2436,6 +2438,12 @@
       "integrity": "sha512-pTvD/0CIeqe4x23+YJWlX2gArHa8G0J0Oh6GKaVXV7TAeickpkkZiNOgFcFcmLQ5lB/K0qBJL1FtRYltBfbGCQ==",
       "dev": true
     },
+    "node_modules/@types/sortablejs": {
+      "version": "1.15.7",
+      "resolved": "https://registry.npmjs.org/@types/sortablejs/-/sortablejs-1.15.7.tgz",
+      "integrity": "sha512-PvgWCx1Lbgm88FdQ6S7OGvLIjWS66mudKPlfdrWil0TjsO5zmoZmzoKiiwRShs1dwPgrlkr0N4ewuy0/+QUXYQ==",
+      "dev": true
+    },
     "node_modules/@types/trusted-types": {
       "version": "2.0.7",
       "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
@@ -2631,9 +2639,9 @@
       }
     },
     "node_modules/@vitejs/plugin-vue": {
-      "version": "5.0.3",
-      "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.3.tgz",
-      "integrity": "sha512-b8S5dVS40rgHdDrw+DQi/xOM9ed+kSRZzfm1T74bMmBDCd8XO87NKlFYInzCtwvtWwXZvo1QxE2OSspTATWrbA==",
+      "version": "5.0.4",
+      "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.4.tgz",
+      "integrity": "sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==",
       "dev": true,
       "engines": {
         "node": "^18.0.0 || >=20.0.0"
@@ -2672,12 +2680,12 @@
       }
     },
     "node_modules/@vue/compiler-core": {
-      "version": "3.4.15",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.15.tgz",
-      "integrity": "sha512-XcJQVOaxTKCnth1vCxEChteGuwG6wqnUHxAm1DO3gCz0+uXKaJNx8/digSz4dLALCy8n2lKq24jSUs8segoqIw==",
+      "version": "3.4.19",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.19.tgz",
+      "integrity": "sha512-gj81785z0JNzRcU0Mq98E56e4ltO1yf8k5PQ+tV/7YHnbZkrM0fyFyuttnN8ngJZjbpofWE/m4qjKBiLl8Ju4w==",
       "dependencies": {
-        "@babel/parser": "^7.23.6",
-        "@vue/shared": "3.4.15",
+        "@babel/parser": "^7.23.9",
+        "@vue/shared": "3.4.19",
         "entities": "^4.5.0",
         "estree-walker": "^2.0.2",
         "source-map-js": "^1.0.2"
@@ -2689,26 +2697,26 @@
       "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
     },
     "node_modules/@vue/compiler-dom": {
-      "version": "3.4.15",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.15.tgz",
-      "integrity": "sha512-wox0aasVV74zoXyblarOM3AZQz/Z+OunYcIHe1OsGclCHt8RsRm04DObjefaI82u6XDzv+qGWZ24tIsRAIi5MQ==",
+      "version": "3.4.19",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.19.tgz",
+      "integrity": "sha512-vm6+cogWrshjqEHTzIDCp72DKtea8Ry/QVpQRYoyTIg9k7QZDX6D8+HGURjtmatfgM8xgCFtJJaOlCaRYRK3QA==",
       "dependencies": {
-        "@vue/compiler-core": "3.4.15",
-        "@vue/shared": "3.4.15"
+        "@vue/compiler-core": "3.4.19",
+        "@vue/shared": "3.4.19"
       }
     },
     "node_modules/@vue/compiler-sfc": {
-      "version": "3.4.15",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.15.tgz",
-      "integrity": "sha512-LCn5M6QpkpFsh3GQvs2mJUOAlBQcCco8D60Bcqmf3O3w5a+KWS5GvYbrrJBkgvL1BDnTp+e8q0lXCLgHhKguBA==",
-      "dependencies": {
-        "@babel/parser": "^7.23.6",
-        "@vue/compiler-core": "3.4.15",
-        "@vue/compiler-dom": "3.4.15",
-        "@vue/compiler-ssr": "3.4.15",
-        "@vue/shared": "3.4.15",
+      "version": "3.4.19",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.19.tgz",
+      "integrity": "sha512-LQ3U4SN0DlvV0xhr1lUsgLCYlwQfUfetyPxkKYu7dkfvx7g3ojrGAkw0AERLOKYXuAGnqFsEuytkdcComei3Yg==",
+      "dependencies": {
+        "@babel/parser": "^7.23.9",
+        "@vue/compiler-core": "3.4.19",
+        "@vue/compiler-dom": "3.4.19",
+        "@vue/compiler-ssr": "3.4.19",
+        "@vue/shared": "3.4.19",
         "estree-walker": "^2.0.2",
-        "magic-string": "^0.30.5",
+        "magic-string": "^0.30.6",
         "postcss": "^8.4.33",
         "source-map-js": "^1.0.2"
       }
@@ -2730,12 +2738,12 @@
       }
     },
     "node_modules/@vue/compiler-ssr": {
-      "version": "3.4.15",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.15.tgz",
-      "integrity": "sha512-1jdeQyiGznr8gjFDadVmOJqZiLNSsMa5ZgqavkPZ8O2wjHv0tVuAEsw5hTdUoUW4232vpBbL/wJhzVW/JwY1Uw==",
+      "version": "3.4.19",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.19.tgz",
+      "integrity": "sha512-P0PLKC4+u4OMJ8sinba/5Z/iDT84uMRRlrWzadgLA69opCpI1gG4N55qDSC+dedwq2fJtzmGald05LWR5TFfLw==",
       "dependencies": {
-        "@vue/compiler-dom": "3.4.15",
-        "@vue/shared": "3.4.15"
+        "@vue/compiler-dom": "3.4.19",
+        "@vue/shared": "3.4.19"
       }
     },
     "node_modules/@vue/devtools-api": {
@@ -2831,48 +2839,48 @@
       }
     },
     "node_modules/@vue/reactivity": {
-      "version": "3.4.15",
-      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.15.tgz",
-      "integrity": "sha512-55yJh2bsff20K5O84MxSvXKPHHt17I2EomHznvFiJCAZpJTNW8IuLj1xZWMLELRhBK3kkFV/1ErZGHJfah7i7w==",
+      "version": "3.4.19",
+      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.19.tgz",
+      "integrity": "sha512-+VcwrQvLZgEclGZRHx4O2XhyEEcKaBi50WbxdVItEezUf4fqRh838Ix6amWTdX0CNb/b6t3Gkz3eOebfcSt+UA==",
       "dependencies": {
-        "@vue/shared": "3.4.15"
+        "@vue/shared": "3.4.19"
       }
     },
     "node_modules/@vue/runtime-core": {
-      "version": "3.4.15",
-      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.15.tgz",
-      "integrity": "sha512-6E3by5m6v1AkW0McCeAyhHTw+3y17YCOKG0U0HDKDscV4Hs0kgNT5G+GCHak16jKgcCDHpI9xe5NKb8sdLCLdw==",
+      "version": "3.4.19",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.19.tgz",
+      "integrity": "sha512-/Z3tFwOrerJB/oyutmJGoYbuoadphDcJAd5jOuJE86THNZji9pYjZroQ2NFsZkTxOq0GJbb+s2kxTYToDiyZzw==",
       "dependencies": {
-        "@vue/reactivity": "3.4.15",
-        "@vue/shared": "3.4.15"
+        "@vue/reactivity": "3.4.19",
+        "@vue/shared": "3.4.19"
       }
     },
     "node_modules/@vue/runtime-dom": {
-      "version": "3.4.15",
-      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.15.tgz",
-      "integrity": "sha512-EVW8D6vfFVq3V/yDKNPBFkZKGMFSvZrUQmx196o/v2tHKdwWdiZjYUBS+0Ez3+ohRyF8Njwy/6FH5gYJ75liUw==",
+      "version": "3.4.19",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.19.tgz",
+      "integrity": "sha512-IyZzIDqfNCF0OyZOauL+F4yzjMPN2rPd8nhqPP2N1lBn3kYqJpPHHru+83Rkvo2lHz5mW+rEeIMEF9qY3PB94g==",
       "dependencies": {
-        "@vue/runtime-core": "3.4.15",
-        "@vue/shared": "3.4.15",
+        "@vue/runtime-core": "3.4.19",
+        "@vue/shared": "3.4.19",
         "csstype": "^3.1.3"
       }
     },
     "node_modules/@vue/server-renderer": {
-      "version": "3.4.15",
-      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.15.tgz",
-      "integrity": "sha512-3HYzaidu9cHjrT+qGUuDhFYvF/j643bHC6uUN9BgM11DVy+pM6ATsG6uPBLnkwOgs7BpJABReLmpL3ZPAsUaqw==",
+      "version": "3.4.19",
+      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.19.tgz",
+      "integrity": "sha512-eAj2p0c429RZyyhtMRnttjcSToch+kTWxFPHlzGMkR28ZbF1PDlTcmGmlDxccBuqNd9iOQ7xPRPAGgPVj+YpQw==",
       "dependencies": {
-        "@vue/compiler-ssr": "3.4.15",
-        "@vue/shared": "3.4.15"
+        "@vue/compiler-ssr": "3.4.19",
+        "@vue/shared": "3.4.19"
       },
       "peerDependencies": {
-        "vue": "3.4.15"
+        "vue": "3.4.19"
       }
     },
     "node_modules/@vue/shared": {
-      "version": "3.4.15",
-      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.15.tgz",
-      "integrity": "sha512-KzfPTxVaWfB+eGcGdbSf4CWdaXcGDqckoeXUh7SB3fZdEtzPCK2Vq9B/lRRL3yutax/LWITz+SwvgyOxz5V75g=="
+      "version": "3.4.19",
+      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.19.tgz",
+      "integrity": "sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw=="
     },
     "node_modules/@vue/tsconfig": {
       "version": "0.5.1",
@@ -3130,15 +3138,16 @@
       }
     },
     "node_modules/call-bind": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.6.tgz",
-      "integrity": "sha512-Mj50FLHtlsoVfRfnHaZvyrooHcrlceNZdL/QBvJJVd9Ta55qCQK0gs4ss2oZDeV9zFCs6ewzYgVE5yfVmfFpVg==",
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
+      "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
       "dev": true,
       "dependencies": {
+        "es-define-property": "^1.0.0",
         "es-errors": "^1.3.0",
         "function-bind": "^1.1.2",
-        "get-intrinsic": "^1.2.3",
-        "set-function-length": "^1.2.0"
+        "get-intrinsic": "^1.2.4",
+        "set-function-length": "^1.2.1"
       },
       "engines": {
         "node": ">= 0.4"
@@ -3207,16 +3216,10 @@
       }
     },
     "node_modules/chokidar": {
-      "version": "3.5.3",
-      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
-      "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+      "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
       "dev": true,
-      "funding": [
-        {
-          "type": "individual",
-          "url": "https://paulmillr.com/funding/"
-        }
-      ],
       "dependencies": {
         "anymatch": "~3.1.2",
         "braces": "~3.0.2",
@@ -3229,6 +3232,9 @@
       "engines": {
         "node": ">= 8.10.0"
       },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      },
       "optionalDependencies": {
         "fsevents": "~2.3.2"
       }
@@ -3362,18 +3368,21 @@
       "dev": true
     },
     "node_modules/define-data-property": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.2.tgz",
-      "integrity": "sha512-SRtsSqsDbgpJBbW3pABMCOt6rQyeM8s8RiyeSN8jYG8sYmt/kGJejbydttUsnDs1tadr19tvhT4ShwMyoqAm4g==",
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.3.tgz",
+      "integrity": "sha512-h3GBouC+RPtNX2N0hHVLo2ZwPYurq8mLmXpOLTsw71gr7lHt5VaI4vVkDUNOfiWmm48JEXe3VM7PmLX45AMmmg==",
       "dev": true,
       "dependencies": {
         "es-errors": "^1.3.0",
-        "get-intrinsic": "^1.2.2",
+        "get-intrinsic": "^1.2.4",
         "gopd": "^1.0.1",
         "has-property-descriptors": "^1.0.1"
       },
       "engines": {
         "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
     "node_modules/define-properties": {
@@ -3504,6 +3513,18 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/es-define-property": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
+      "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
+      "dev": true,
+      "dependencies": {
+        "get-intrinsic": "^1.2.4"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/es-errors": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
@@ -4157,13 +4178,14 @@
       }
     },
     "node_modules/get-symbol-description": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.1.tgz",
-      "integrity": "sha512-KmuibvwbWaM4BHcBRYwJfZ1JxyJeBwB8ct9YYu67SvYdbEIlcQ2e56dHxfbobqW38GXo8/zDFqJeGtHiVbWyQw==",
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz",
+      "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==",
       "dev": true,
       "dependencies": {
         "call-bind": "^1.0.5",
-        "es-errors": "^1.3.0"
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.4"
       },
       "engines": {
         "node": ">= 0.4"
@@ -4326,12 +4348,12 @@
       }
     },
     "node_modules/has-property-descriptors": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz",
-      "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==",
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+      "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
       "dev": true,
       "dependencies": {
-        "get-intrinsic": "^1.2.2"
+        "es-define-property": "^1.0.0"
       },
       "funding": {
         "url": "https://github.com/sponsors/ljharb"
@@ -4377,9 +4399,9 @@
       }
     },
     "node_modules/hasown": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
-      "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz",
+      "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==",
       "dev": true,
       "dependencies": {
         "function-bind": "^1.1.2"
@@ -5507,9 +5529,9 @@
       }
     },
     "node_modules/postcss": {
-      "version": "8.4.34",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.34.tgz",
-      "integrity": "sha512-4eLTO36woPSocqZ1zIrFD2K1v6wH7pY1uBh0JIM2KKfrVtGvPFiAku6aNOP0W1Wr9qwnaCsF0Z+CrVnryB2A8Q==",
+      "version": "8.4.35",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz",
+      "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==",
       "funding": [
         {
           "type": "opencollective",
@@ -5668,14 +5690,15 @@
       }
     },
     "node_modules/regexp.prototype.flags": {
-      "version": "1.5.1",
-      "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz",
-      "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==",
+      "version": "1.5.2",
+      "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz",
+      "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==",
       "dev": true,
       "dependencies": {
-        "call-bind": "^1.0.2",
-        "define-properties": "^1.2.0",
-        "set-function-name": "^2.0.0"
+        "call-bind": "^1.0.6",
+        "define-properties": "^1.2.1",
+        "es-errors": "^1.3.0",
+        "set-function-name": "^2.0.1"
       },
       "engines": {
         "node": ">= 0.4"
@@ -5744,9 +5767,9 @@
       }
     },
     "node_modules/rollup": {
-      "version": "4.9.6",
-      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.6.tgz",
-      "integrity": "sha512-05lzkCS2uASX0CiLFybYfVkwNbKZG5NFQ6Go0VWyogFTXXbR039UVsegViTntkk4OglHBdF54ccApXRRuXRbsg==",
+      "version": "4.10.0",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.10.0.tgz",
+      "integrity": "sha512-t2v9G2AKxcQ8yrG+WGxctBes1AomT0M4ND7jTFBCVPXQ/WFTvNSefIrNSmLKhIKBrvN8SG+CZslimJcT3W2u2g==",
       "dev": true,
       "dependencies": {
         "@types/estree": "1.0.5"
@@ -5759,19 +5782,19 @@
         "npm": ">=8.0.0"
       },
       "optionalDependencies": {
-        "@rollup/rollup-android-arm-eabi": "4.9.6",
-        "@rollup/rollup-android-arm64": "4.9.6",
-        "@rollup/rollup-darwin-arm64": "4.9.6",
-        "@rollup/rollup-darwin-x64": "4.9.6",
-        "@rollup/rollup-linux-arm-gnueabihf": "4.9.6",
-        "@rollup/rollup-linux-arm64-gnu": "4.9.6",
-        "@rollup/rollup-linux-arm64-musl": "4.9.6",
-        "@rollup/rollup-linux-riscv64-gnu": "4.9.6",
-        "@rollup/rollup-linux-x64-gnu": "4.9.6",
-        "@rollup/rollup-linux-x64-musl": "4.9.6",
-        "@rollup/rollup-win32-arm64-msvc": "4.9.6",
-        "@rollup/rollup-win32-ia32-msvc": "4.9.6",
-        "@rollup/rollup-win32-x64-msvc": "4.9.6",
+        "@rollup/rollup-android-arm-eabi": "4.10.0",
+        "@rollup/rollup-android-arm64": "4.10.0",
+        "@rollup/rollup-darwin-arm64": "4.10.0",
+        "@rollup/rollup-darwin-x64": "4.10.0",
+        "@rollup/rollup-linux-arm-gnueabihf": "4.10.0",
+        "@rollup/rollup-linux-arm64-gnu": "4.10.0",
+        "@rollup/rollup-linux-arm64-musl": "4.10.0",
+        "@rollup/rollup-linux-riscv64-gnu": "4.10.0",
+        "@rollup/rollup-linux-x64-gnu": "4.10.0",
+        "@rollup/rollup-linux-x64-musl": "4.10.0",
+        "@rollup/rollup-win32-arm64-msvc": "4.10.0",
+        "@rollup/rollup-win32-ia32-msvc": "4.10.0",
+        "@rollup/rollup-win32-x64-msvc": "4.10.0",
         "fsevents": "~2.3.2"
       }
     },
@@ -5866,13 +5889,13 @@
       ]
     },
     "node_modules/safe-regex-test": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.2.tgz",
-      "integrity": "sha512-83S9w6eFq12BBIJYvjMux6/dkirb8+4zJRA9cxNBVb7Wq5fJBW+Xze48WqR8pxua7bDuAaaAxtVVd4Idjp1dBQ==",
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz",
+      "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==",
       "dev": true,
       "dependencies": {
-        "call-bind": "^1.0.5",
-        "get-intrinsic": "^1.2.2",
+        "call-bind": "^1.0.6",
+        "es-errors": "^1.3.0",
         "is-regex": "^1.1.4"
       },
       "engines": {
@@ -5914,14 +5937,15 @@
       }
     },
     "node_modules/set-function-length": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.0.tgz",
-      "integrity": "sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==",
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz",
+      "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==",
       "dev": true,
       "dependencies": {
-        "define-data-property": "^1.1.1",
+        "define-data-property": "^1.1.2",
+        "es-errors": "^1.3.0",
         "function-bind": "^1.1.2",
-        "get-intrinsic": "^1.2.2",
+        "get-intrinsic": "^1.2.3",
         "gopd": "^1.0.1",
         "has-property-descriptors": "^1.0.1"
       },
@@ -5997,14 +6021,18 @@
       }
     },
     "node_modules/side-channel": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
-      "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz",
+      "integrity": "sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==",
       "dev": true,
       "dependencies": {
-        "call-bind": "^1.0.0",
-        "get-intrinsic": "^1.0.2",
-        "object-inspect": "^1.9.0"
+        "call-bind": "^1.0.6",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.4",
+        "object-inspect": "^1.13.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
       },
       "funding": {
         "url": "https://github.com/sponsors/ljharb"
@@ -6019,6 +6047,11 @@
         "node": ">=8"
       }
     },
+    "node_modules/sortablejs": {
+      "version": "1.15.2",
+      "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.2.tgz",
+      "integrity": "sha512-FJF5jgdfvoKn1MAKSdGs33bIqLi3LmsgVTliuX6iITj834F+JRQZN90Z93yql8h0K2t0RwDPBmxwlbZfDcxNZA=="
+    },
     "node_modules/source-map": {
       "version": "0.6.1",
       "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -6070,9 +6103,9 @@
       }
     },
     "node_modules/spdx-license-ids": {
-      "version": "3.0.16",
-      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz",
-      "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==",
+      "version": "3.0.17",
+      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz",
+      "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==",
       "dev": true
     },
     "node_modules/stream-browserify": {
@@ -6301,14 +6334,14 @@
       }
     },
     "node_modules/typed-array-buffer": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz",
-      "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==",
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.1.tgz",
+      "integrity": "sha512-RSqu1UEuSlrBhHTWC8O9FnPjOduNs4M7rJ4pRKoEjtx1zUNOPN2sSXHLDX+Y2WPbHIxbvg4JFo2DNAEfPIKWoQ==",
       "dev": true,
       "dependencies": {
-        "call-bind": "^1.0.2",
-        "get-intrinsic": "^1.2.1",
-        "is-typed-array": "^1.1.10"
+        "call-bind": "^1.0.6",
+        "es-errors": "^1.3.0",
+        "is-typed-array": "^1.1.13"
       },
       "engines": {
         "node": ">= 0.4"
@@ -6914,15 +6947,15 @@
       }
     },
     "node_modules/vue": {
-      "version": "3.4.15",
-      "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.15.tgz",
-      "integrity": "sha512-jC0GH4KkWLWJOEQjOpkqU1bQsBwf4R1rsFtw5GQJbjHVKWDzO6P0nWWBTmjp1xSemAioDFj1jdaK1qa3DnMQoQ==",
-      "dependencies": {
-        "@vue/compiler-dom": "3.4.15",
-        "@vue/compiler-sfc": "3.4.15",
-        "@vue/runtime-dom": "3.4.15",
-        "@vue/server-renderer": "3.4.15",
-        "@vue/shared": "3.4.15"
+      "version": "3.4.19",
+      "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.19.tgz",
+      "integrity": "sha512-W/7Fc9KUkajFU8dBeDluM4sRGc/aa4YJnOYck8dkjgZoXtVsn3OeTGni66FV1l3+nvPA7VBFYtPioaGKUmEADw==",
+      "dependencies": {
+        "@vue/compiler-dom": "3.4.19",
+        "@vue/compiler-sfc": "3.4.19",
+        "@vue/runtime-dom": "3.4.19",
+        "@vue/server-renderer": "3.4.19",
+        "@vue/shared": "3.4.19"
       },
       "peerDependencies": {
         "typescript": "*"
diff --git a/package.json b/package.json
index ef34c45..0d6de8d 100644
--- a/package.json
+++ b/package.json
@@ -32,6 +32,7 @@
     "pinia": "~2.1.0",
     "semver": "~7.5.0",
     "showdown": "~2.1.0",
+    "sortablejs": "^1.15.2",
     "vue": "~3.4.0",
     "vue-router": "~4.2.0",
     "vue3-cookies": "~1.0.0"
@@ -46,6 +47,7 @@
     "@types/node": "^18.19.5",
     "@types/semver": "~7.5.1",
     "@types/showdown": "~2.0.1",
+    "@types/sortablejs": "^1.15.7",
     "@vitejs/plugin-vue": "~5.0.0",
     "@vue/eslint-config-prettier": "~8.0.0",
     "@vue/eslint-config-typescript": "~11.0.3",
diff --git a/src/App.vue b/src/App.vue
index 1ae0d2e..4497959 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -87,7 +87,7 @@ onBeforeMount(() => {
 
 <template>
   <NavbarTop />
-  <div class="container-xxl mt-4 flex-grow-1">
+  <div class="container-xxl mt-4 flex-grow-1 py-2">
     <div
       id="global-toast-container"
       class="toast-container position-fixed top-toast end-0 p-3"
diff --git a/src/components/CopyToClipboardIcon.vue b/src/components/CopyToClipboardIcon.vue
index b0d34b4..6e85078 100644
--- a/src/components/CopyToClipboardIcon.vue
+++ b/src/components/CopyToClipboardIcon.vue
@@ -47,6 +47,7 @@ onMounted(() => {
   </bootstrap-toast>
   <button v-if="props.button" @click="copyToClipboard" class="btn btn-primary">
     Copy to Clipboard
+    <font-awesome-icon icon="fa-solid fa-clipboard" class="ms-1" />
   </button>
   <span
     v-else
diff --git a/src/components/DraggableLists.vue b/src/components/DraggableLists.vue
new file mode 100644
index 0000000..bb72d1d
--- /dev/null
+++ b/src/components/DraggableLists.vue
@@ -0,0 +1,104 @@
+<script setup lang="ts">
+import { onMounted, ref } from "vue";
+import Sortable from "sortablejs";
+
+const leftList = defineModel<string[]>("leftList", { required: true });
+const rightList = defineModel<string[]>("rightList", { required: true });
+
+const leftListElement = ref<HTMLUListElement | undefined>(undefined);
+const rightListElement = ref<HTMLUListElement | undefined>(undefined);
+
+onMounted(() => {
+  if (leftListElement.value && rightListElement.value) {
+    new Sortable(leftListElement.value, {
+      group: "shared",
+      animation: 150,
+      sort: false, // To disable sorting: set sort to false
+      onRemove: (evt) => {
+        leftList.value.splice(evt.oldIndex ?? 0, 1);
+      },
+      onAdd: (evt) => {
+        leftList.value.splice(
+          evt.newIndex ?? 0,
+          0,
+          (evt.item as HTMLLIElement).innerText,
+        );
+      },
+    });
+
+    new Sortable(rightListElement.value, {
+      group: "shared",
+      animation: 150,
+      sort: false, // To disable sorting: set sort to false
+      onRemove: (evt) => {
+        rightList.value.splice(evt.oldIndex ?? 0, 1);
+      },
+      onAdd: (evt) => {
+        rightList.value.splice(
+          evt.newIndex ?? 0,
+          0,
+          (evt.item as HTMLLIElement).innerText,
+        );
+      },
+    });
+  }
+});
+</script>
+
+<template>
+  <div class="row">
+    <div class="col-6 d-flex flex-column justify-content-start">
+      <h5><slot name="leftHeader" /></h5>
+      <ul
+        id="items"
+        class="list-group flex-fill border border-dashed p-1 overflow-y-scroll"
+        ref="leftListElement"
+        style="max-height: 40vh"
+      >
+        <li
+          v-for="(left, index) in leftList"
+          class="list-group-item"
+          :key="left"
+          @click="
+            rightList.push(left);
+            leftList.splice(index, 1);
+          "
+        >
+          {{ left }}
+        </li>
+      </ul>
+    </div>
+    <div class="col-6 d-flex flex-column justify-content-start">
+      <h5><slot name="rightHeader" /></h5>
+      <ul
+        id="items"
+        class="list-group flex-fill border border-dashed p-1 overflow-y-scroll"
+        ref="rightListElement"
+        style="max-height: 40vh"
+      >
+        <li
+          v-for="(right, index) in rightList"
+          class="list-group-item"
+          :key="right"
+          @click="
+            leftList.push(right);
+            rightList.splice(index, 1);
+          "
+        >
+          {{ right }}
+        </li>
+      </ul>
+    </div>
+  </div>
+</template>
+
+<style scoped>
+li:hover {
+  cursor: grab;
+  background: var(--bs-secondary-bg-subtle);
+}
+
+.border-dashed {
+  border-style: dashed !important;
+}
+</style>
diff --git a/src/components/NavbarTop.vue b/src/components/NavbarTop.vue
index 0cb939d..4a85939 100644
--- a/src/components/NavbarTop.vue
+++ b/src/components/NavbarTop.vue
@@ -189,7 +189,7 @@ watch(
                 <router-link
                   class="dropdown-item"
                   :to="{ name: 'resource-review' }"
-                  >Review
+                  >Reviews
                 </router-link>
               </li>
             </ul>
diff --git a/src/components/modals/BootstrapModal.vue b/src/components/modals/BootstrapModal.vue
index 7f3a55e..cf6664b 100644
--- a/src/components/modals/BootstrapModal.vue
+++ b/src/components/modals/BootstrapModal.vue
@@ -36,7 +36,7 @@ const modalSizeClass = computed<string>(() => {
           <div class="modal-title fs-5" :id="modalLabel">
             <slot name="header" />
           </div>
-          <div>
+          <div class="d-flex align-items-center">
             <slot name="extra-button" />
             <button
               type="button"
diff --git a/src/components/object-storage/modals/BucketDetailModal.vue b/src/components/object-storage/modals/BucketDetailModal.vue
index 49eef59..3ceebc6 100644
--- a/src/components/object-storage/modals/BucketDetailModal.vue
+++ b/src/components/object-storage/modals/BucketDetailModal.vue
@@ -15,6 +15,7 @@ const props = defineProps<{
     :modalId="modalID"
     :static-backdrop="false"
     modal-label="Bucket Detail Modal"
+    size-modifier="lg"
   >
     <template v-slot:header>
       <h4>
diff --git a/src/components/parameter-schema/ParameterSchemaFormComponent.vue b/src/components/parameter-schema/ParameterSchemaFormComponent.vue
index 4a93a52..4048dac 100644
--- a/src/components/parameter-schema/ParameterSchemaFormComponent.vue
+++ b/src/components/parameter-schema/ParameterSchemaFormComponent.vue
@@ -15,6 +15,7 @@ import type { ClowmInfo } from "@/types/ClowmInfo";
 import UploadParameterFileModal from "@/components/parameter-schema/UploadParameterFileModal.vue";
 import type {
   TemporaryParams,
+  WorkflowMetaParameters,
   WorkflowParameters,
 } from "@/types/WorkflowParameters";
 import { useWorkflowExecutionStore } from "@/stores/workflowExecutions";
@@ -55,10 +56,7 @@ const emit = defineEmits<{
   (
     e: "start-workflow",
     parameters: WorkflowParameters,
-    notes?: string,
-    logs_s3_path?: string,
-    debug_s3_path?: string,
-    provenance_s3_path?: string,
+    metaParameters: WorkflowMetaParameters,
   ): void;
 }>();
 
@@ -90,18 +88,17 @@ const launchForm = ref<HTMLFormElement | null>(null);
 const formState = reactive<{
   formInput: WorkflowParameters;
   validated: boolean;
-  pipelineNotes: string;
-  logs_s3_path?: string;
-  debug_s3_path?: string;
-  provenance_s3_path?: string;
+  metaParameters: WorkflowMetaParameters;
   errorType?: string;
 }>({
   formInput: {},
   validated: false,
-  pipelineNotes: "",
-  logs_s3_path: undefined,
-  debug_s3_path: undefined,
-  provenance_s3_path: undefined,
+  metaParameters: {
+    logs_s3_path: undefined,
+    debug_s3_path: undefined,
+    provenance_s3_path: undefined,
+    notes: undefined,
+  },
   errorType: undefined,
 });
 
@@ -190,14 +187,7 @@ function startWorkflow() {
       console.error(validateSchema.errors);
       errorToast?.show();
     } else {
-      emit(
-        "start-workflow",
-        formState.formInput,
-        formState.pipelineNotes,
-        formState.logs_s3_path,
-        formState.debug_s3_path,
-        formState.provenance_s3_path,
-      );
+      emit("start-workflow", formState.formInput, formState.metaParameters);
     }
   } else {
     formState.errorType = "form";
@@ -212,10 +202,7 @@ function loadParameters(tempParams?: TemporaryParams) {
         formState.formInput[param] = tempParams.params[param];
       }
     }
-    formState.pipelineNotes = tempParams.metaParams.notes ?? "";
-    formState.logs_s3_path = tempParams.metaParams.logs_s3_path;
-    formState.provenance_s3_path = tempParams.metaParams.provenance_s3_path;
-    formState.debug_s3_path = tempParams.metaParams.debug_s3_path;
+    formState.metaParameters = tempParams.metaParams;
     if (Object.keys(tempParams?.params ?? {}).length > 0) {
       parameterLoadToast?.show();
     }
@@ -232,7 +219,8 @@ function scroll(selectedAnchor: string) {
 // =============================================================================
 onMounted(() => {
   if (props.schema) updateSchema(props.schema);
-  if (props.clowmInfo?.exampleParameters) Tooltip.getOrCreateInstance("#exampleDataButton");
+  if (props.clowmInfo?.exampleParameters)
+    Tooltip.getOrCreateInstance("#exampleDataButton");
   bucketRepository.fetchBuckets();
   bucketRepository.fetchOwnPermissions();
   keyRepository.fetchS3Keys();
@@ -271,7 +259,7 @@ onMounted(() => {
         })
     "
   />
-  <div class="row mb-5 align-items-start">
+  <div class="row align-items-start">
     <form
       v-if="props.schema"
       class="col-9"
@@ -283,8 +271,7 @@ onMounted(() => {
     >
       <template v-for="(group, groupName) in parameterGroups" :key="groupName">
         <parameter-group-form
-          :modelValue="formState.formInput"
-          @update:model-value="(newValue) => (formState.formInput = newValue)"
+          v-model="formState.formInput"
           v-if="formState.formInput"
           :parameter-group-name="groupName"
           :parameter-group="group"
@@ -317,7 +304,7 @@ onMounted(() => {
               <textarea
                 class="form-control border border-secondary"
                 rows="2"
-                v-model="formState.pipelineNotes"
+                v-model="formState.metaParameters.notes"
               />
             </div>
             <label class="mb-3" for="pipelineNotes"
@@ -335,7 +322,7 @@ onMounted(() => {
               </span>
               <parameter-string-input
                 parameter-name="logs_s3_path"
-                v-model="formState.logs_s3_path"
+                v-model="formState.metaParameters.logs_s3_path"
                 :parameter="{
                   format: 'directory-path',
                   type: 'string',
@@ -358,7 +345,7 @@ onMounted(() => {
               </span>
               <parameter-string-input
                 parameter-name="provenance_s3_path"
-                v-model="formState.provenance_s3_path"
+                v-model="formState.metaParameters.provenance_s3_path"
                 :parameter="{
                   format: 'directory-path',
                   type: 'string',
@@ -382,7 +369,7 @@ onMounted(() => {
               </span>
               <parameter-string-input
                 parameter-name="debug_s3_path"
-                v-model="formState.debug_s3_path"
+                v-model="formState.metaParameters.debug_s3_path"
                 :parameter="{
                   format: 'directory-path',
                   type: 'string',
diff --git a/src/components/parameter-schema/form-mode/ParameterBooleanInput.vue b/src/components/parameter-schema/form-mode/ParameterBooleanInput.vue
index a30bd4e..54e519c 100644
--- a/src/components/parameter-schema/form-mode/ParameterBooleanInput.vue
+++ b/src/components/parameter-schema/form-mode/ParameterBooleanInput.vue
@@ -1,6 +1,8 @@
 <script setup lang="ts">
 import { computed } from "vue";
 
+const model = defineModel<boolean | undefined>({ required: true });
+
 const props = defineProps({
   parameter: {
     type: Object,
@@ -14,9 +16,6 @@ const props = defineProps({
     type: String,
     required: true,
   },
-  modelValue: {
-    type: Boolean,
-  },
   helpId: {
     type: String,
   },
@@ -26,10 +25,6 @@ const randomIDSuffix = Math.random().toString(16).substring(2, 8);
 
 const helpTextPresent = computed<boolean>(() => props.parameter["help_text"]);
 const iconPresent = computed<boolean>(() => props.parameter["fa_icon"]);
-
-const emit = defineEmits<{
-  (e: "update:modelValue", value: boolean): void;
-}>();
 </script>
 
 <template>
@@ -47,8 +42,7 @@ const emit = defineEmits<{
         :name="'inlineRadioOptions' + randomIDSuffix"
         :id="'trueOption' + randomIDSuffix"
         :value="true"
-        :checked="props.modelValue"
-        @input="emit('update:modelValue', true)"
+        v-model="model"
       />
     </div>
     <div class="form-check form-check-inline">
@@ -58,8 +52,7 @@ const emit = defineEmits<{
         :name="'inlineRadioOptions' + randomIDSuffix"
         :id="'falseOption' + randomIDSuffix"
         :value="false"
-        @input="emit('update:modelValue', false)"
-        :checked="!props.modelValue"
+        v-model="model"
       />
       <label class="form-check-label" :for="'falseOption' + randomIDSuffix"
         >False</label
diff --git a/src/components/parameter-schema/form-mode/ParameterEnumInput.vue b/src/components/parameter-schema/form-mode/ParameterEnumInput.vue
index 03ec622..a92092c 100644
--- a/src/components/parameter-schema/form-mode/ParameterEnumInput.vue
+++ b/src/components/parameter-schema/form-mode/ParameterEnumInput.vue
@@ -1,5 +1,7 @@
 <script setup lang="ts">
-import { computed, ref } from "vue";
+import { computed } from "vue";
+
+const model = defineModel<string | undefined>({ required: true });
 
 const props = defineProps({
   parameter: {
@@ -14,41 +16,23 @@ const props = defineProps({
     type: String,
     required: true,
   },
-  modelValue: {
-    type: String,
-  },
   helpId: {
     type: String,
   },
 });
 
 const possibleValues = computed<string[]>(() => props.parameter["enum"]);
-
-const enumSelection = ref<HTMLSelectElement | undefined>(undefined);
-
-const emit = defineEmits<{
-  (e: "update:modelValue", value: string | undefined): void;
-}>();
-
-function updateValue() {
-  emit("update:modelValue", enumSelection.value?.value);
-}
 </script>
 
 <template>
   <select
     ref="enumSelection"
-    :value="props.modelValue"
-    @input="updateValue"
+    v-model="model"
     class="form-select border border-secondary"
     :required="required"
     :aria-describedby="props.helpId"
   >
-    <option
-      v-for="val in possibleValues"
-      :key="val"
-      :selected="props.modelValue === val"
-    >
+    <option v-for="val in possibleValues" :key="val">
       {{ val }}
     </option>
   </select>
diff --git a/src/components/parameter-schema/form-mode/ParameterGroupForm.vue b/src/components/parameter-schema/form-mode/ParameterGroupForm.vue
index be65227..9d4b0f3 100644
--- a/src/components/parameter-schema/form-mode/ParameterGroupForm.vue
+++ b/src/components/parameter-schema/form-mode/ParameterGroupForm.vue
@@ -1,11 +1,14 @@
 <script setup lang="ts">
 import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
-import { computed, type PropType, watch } from "vue";
+import { computed, type PropType } from "vue";
 import ParameterNumberInput from "@/components/parameter-schema/form-mode/ParameterNumberInput.vue";
 import MarkdownRenderer from "@/components/MarkdownRenderer.vue";
 import ParameterBooleanInput from "@/components/parameter-schema/form-mode/ParameterBooleanInput.vue";
 import ParameterEnumInput from "@/components/parameter-schema/form-mode/ParameterEnumInput.vue";
 import ParameterStringInput from "@/components/parameter-schema/form-mode/ParameterStringInput.vue";
+import type { WorkflowParameters } from "@/types/WorkflowParameters";
+
+const model = defineModel<WorkflowParameters>({ required: true });
 
 const props = defineProps({
   parameterGroup: {
@@ -19,10 +22,6 @@ const props = defineProps({
     type: String,
     required: true,
   },
-  modelValue: {
-    type: Object,
-    required: true,
-  },
   showHidden: {
     type: Boolean,
     default: false,
@@ -52,14 +51,6 @@ const parameters = computed<Record<string, never>>(
   () => props.parameterGroup["properties"],
 );
 
-const formInput = computed(() => props.modelValue);
-const emit = defineEmits<{
-  (
-    e: "update:modelValue",
-    value: Record<string, number | string | boolean | undefined>,
-  ): void;
-}>();
-
 function parameterRequired(
   // eslint-disable-next-line @typescript-eslint/no-explicit-any
   parameterGroup: Record<string, any>,
@@ -68,20 +59,10 @@ function parameterRequired(
   return (
     parameterGroup["required"]?.includes(parameterName) || // parameter is required
     parameterGroup["dependentRequired"]?.[parameterName] // parameter is required when another parameter is set
-      ?.map((param: string) => formInput.value[param])
+      ?.map((param: string) => model.value[param])
       ?.reduce((acc: boolean, val: string) => acc || val, false)
   );
 }
-
-watch(
-  formInput,
-  (newVal) => {
-    emit("update:modelValue", newVal);
-  },
-  {
-    deep: true,
-  },
-);
 </script>
 
 <template>
@@ -120,50 +101,48 @@ watch(
             >
               <font-awesome-icon :icon="parameter['fa_icon']" />
             </span>
-            <parameter-number-input
+            <template
               v-if="
                 parameter['type'] === 'number' ||
                 parameter['type'] === 'integer'
               "
-              :parameter-name="parameterName"
-              :parameter="parameter"
-              :help-id="parameterName + '-help'"
-              :required="parameterRequired(parameterGroup, parameterName)"
-              :model-value="formInput[parameterName]"
-              @update:model-value="
-                (newValue) => (formInput[parameterName] = newValue)
-              "
-            />
-            <parameter-boolean-input
-              v-else-if="parameter['type'] === 'boolean'"
-              :parameter-name="parameterName"
-              :parameter="parameter"
-              :help-id="parameterName + '-help'"
-              :model-value="formInput[parameterName]"
-              @update:model-value="
-                (newValue) => (formInput[parameterName] = newValue)
-              "
-            />
-            <template v-else-if="parameter['type'] === 'string'">
-              <parameter-enum-input
-                v-if="parameter['enum']"
+            >
+              <!-- @vue-ignore -->
+              <parameter-number-input
                 :parameter-name="parameterName"
                 :parameter="parameter"
-                :model-value="formInput[parameterName]"
+                :help-id="parameterName + '-help'"
                 :required="parameterRequired(parameterGroup, parameterName)"
-                @update:model-value="
-                  (newValue) => (formInput[parameterName] = newValue)
-                "
+                v-model="model[parameterName]"
               />
+            </template>
+            <template v-else-if="parameter['type'] === 'boolean'">
+              <!-- @vue-ignore -->
+              <parameter-boolean-input
+                :parameter-name="parameterName"
+                :parameter="parameter"
+                :help-id="parameterName + '-help'"
+                v-model="model[parameterName]"
+              />
+            </template>
+            <template v-else-if="parameter['type'] === 'string'">
+              <!-- @vue-ignore -->
+              <template v-if="parameter['enum']">
+                <!-- @vue-ignore -->
+                <parameter-enum-input
+                  :parameter-name="parameterName"
+                  :parameter="parameter"
+                  :required="parameterRequired(parameterGroup, parameterName)"
+                  v-model="model[parameterName]"
+                />
+              </template>
+              <!-- @vue-ignore -->
               <parameter-string-input
                 v-else
                 :parameter-name="parameterName"
                 :parameter="parameter"
-                :model-value="formInput[parameterName]"
                 :required="parameterRequired(parameterGroup, parameterName)"
-                @update:model-value="
-                  (newValue) => (formInput[parameterName] = newValue)
-                "
+                v-model="model[parameterName]"
                 :remove-advanced="!showOptional"
                 :clowm-resource="resourceParameters?.includes(parameterName)"
               />
diff --git a/src/components/parameter-schema/form-mode/ParameterNumberInput.vue b/src/components/parameter-schema/form-mode/ParameterNumberInput.vue
index 55963a2..0cc7ca6 100644
--- a/src/components/parameter-schema/form-mode/ParameterNumberInput.vue
+++ b/src/components/parameter-schema/form-mode/ParameterNumberInput.vue
@@ -1,5 +1,6 @@
 <script setup lang="ts">
-import { ref } from "vue";
+const model = defineModel<number | undefined>({ required: true });
+
 const props = defineProps({
   parameter: {
     type: Object,
@@ -13,37 +14,22 @@ const props = defineProps({
     type: String,
     required: true,
   },
-  modelValue: {
-    type: Number,
-  },
   helpId: {
     type: String,
   },
 });
-
-const emit = defineEmits<{
-  (e: "update:modelValue", value: number | undefined): void;
-}>();
-
-const numberInput = ref<HTMLInputElement | undefined>(undefined);
-
-function updateValue() {
-  emit("update:modelValue", Number(numberInput.value?.value));
-}
 </script>
 
 <template>
   <input
     class="form-control border border-secondary"
     type="number"
-    ref="numberInput"
     :max="props.parameter['maximum']"
     :min="props.parameter['minimum']"
     :step="props.parameter['type'] === 'integer' ? 1 : 0.0001"
-    :value="props.modelValue"
+    v-model="model"
     :required="props.required"
     :aria-describedby="props.helpId"
-    @input="updateValue"
   />
 </template>
 
diff --git a/src/components/workflows/WorkflowDocumentationTabs.vue b/src/components/workflows/WorkflowDocumentationTabs.vue
index 7ce8afc..7ebecac 100644
--- a/src/components/workflows/WorkflowDocumentationTabs.vue
+++ b/src/components/workflows/WorkflowDocumentationTabs.vue
@@ -102,7 +102,7 @@ const activeTab = computed<string>(
       ></span>
     </p>
   </div>
-  <div v-else class="px-2 pt-3 border border-top-0 mb-2">
+  <div v-else class="px-2 pt-3 border border-top-0">
     <div v-if="activeTab === 'description'">
       <markdown-renderer
         v-if="props.descriptionMarkdown"
diff --git a/src/components/workflows/WorkflowWithVersionsCard.vue b/src/components/workflows/WorkflowWithVersionsCard.vue
index defb55e..e0a960a 100644
--- a/src/components/workflows/WorkflowWithVersionsCard.vue
+++ b/src/components/workflows/WorkflowWithVersionsCard.vue
@@ -125,27 +125,7 @@ onMounted(() => {
           />
           <span>{{ props.workflow.name }}</span>
         </div>
-        <div>
-          <button
-            type="button"
-            class="btn btn-outline-info me-2"
-            @click="emit('workflow-update-credentials-click', props.workflow)"
-            :class="{ disabled: props.loading }"
-            data-bs-toggle="modal"
-            data-bs-target="#updateWorkflowCredentialsModal"
-          >
-            <font-awesome-icon icon="fa-solid fa-key" />
-          </button>
-          <button
-            type="button"
-            class="btn btn-outline-danger me-2"
-            @click="emit('workflow-delete-click', props.workflow)"
-            :class="{ disabled: props.loading }"
-            data-bs-toggle="modal"
-            data-bs-target="#deleteWorkflowModal"
-          >
-            <font-awesome-icon icon="fa-solid fa-trash" />
-          </button>
+        <div class="btn-group">
           <button
             type="button"
             class="btn btn-success"
@@ -156,6 +136,56 @@ onMounted(() => {
           >
             Update
           </button>
+          <button
+            type="button"
+            class="btn btn-success dropdown-toggle dropdown-toggle-split"
+            data-bs-toggle="dropdown"
+            aria-expanded="false"
+          >
+            <span class="visually-hidden">Toggle Dropdown</span>
+          </button>
+          <ul class="dropdown-menu z-3">
+            <li>
+              <a
+                class="dropdown-item"
+                href="#"
+                @click.prevent="
+                  emit('workflow-update-credentials-click', props.workflow)
+                "
+                :class="{ disabled: props.loading }"
+                data-bs-toggle="modal"
+                data-bs-target="#updateWorkflowCredentialsModal"
+                >Update Credentials</a
+              >
+            </li>
+            <li>
+              <router-link
+                :to="{
+                  name: 'workflows-clowminfo',
+                  query: {
+                    workflow_id: workflow.workflow_id,
+                    workflow_version_id: sortedVersions(
+                      props.workflow.versions,
+                    )[0]?.workflow_version_id,
+                  },
+                }"
+                class="btn btn-primary dropdown-item"
+                >Add Metadata
+              </router-link>
+            </li>
+            <li>
+              <a
+                class="dropdown-item text-danger"
+                href="#"
+                @click.prevent="emit('workflow-delete-click', props.workflow)"
+                :class="{ disabled: props.loading }"
+                data-bs-toggle="modal"
+                data-bs-target="#deleteWorkflowModal"
+                >Delete
+                <font-awesome-icon icon="fa-solid fa-trash" class="ms-2" />
+              </a>
+            </li>
+          </ul>
         </div>
       </div>
       <p class="card-text" :class="{ 'text-truncate': truncateDescription }">
@@ -331,7 +361,6 @@ onMounted(() => {
 }
 
 .card-hover:hover {
-  transform: translate(0, -5px);
   box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;
 }
 
diff --git a/src/components/workflows/modals/ParameterModal.vue b/src/components/workflows/modals/ParameterModal.vue
index 31c33ca..5ae588a 100644
--- a/src/components/workflows/modals/ParameterModal.vue
+++ b/src/components/workflows/modals/ParameterModal.vue
@@ -9,6 +9,7 @@ import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
 import type { WorkflowExecutionOut, WorkflowVersion } from "@/client/workflow";
 import { useNameStore } from "@/stores/names";
 import { useWorkflowStore } from "@/stores/workflows";
+import { createDownloadUrl } from "@/utils/DownloadJson";
 import type { WorkflowParameters } from "@/types/WorkflowParameters";
 
 const nameRepository = useNameStore();
@@ -80,10 +81,10 @@ const parameterDownloadUrl = computed<string | undefined>(() => {
   if (parameters.value == undefined) {
     return undefined;
   }
-  const blob = new Blob([JSON.stringify(parameters.value, undefined, 2)], {
-    type: "application/json",
-  });
-  return URL.createObjectURL(blob);
+  return createDownloadUrl(
+    JSON.stringify(parameters.value, null, 2),
+    "application/json",
+  );
 });
 
 const workflowName = computed<string>(() => {
diff --git a/src/components/workflows/modals/UpdateWorkflowCredentialsModal.vue b/src/components/workflows/modals/UpdateWorkflowCredentialsModal.vue
index 5da2ca9..43f6a61 100644
--- a/src/components/workflows/modals/UpdateWorkflowCredentialsModal.vue
+++ b/src/components/workflows/modals/UpdateWorkflowCredentialsModal.vue
@@ -139,6 +139,7 @@ onMounted(() => {
     :modalId="modalID"
     :static-backdrop="true"
     modal-label="Update Workflow Version Icon Modal"
+    size-modifier="lg"
     v-on="{ 'hidden.bs.modal': modalClosed }"
   >
     <template #header>
@@ -147,7 +148,7 @@ onMounted(() => {
     </template>
     <template #extra-button v-if="props.workflow.private">
       <button
-        class="btn delete-icon"
+        class="btn delete-icon px-1"
         data-bs-toggle="modal"
         :data-bs-target="'#delete-credentials-modal' + randomIDSuffix"
       >
diff --git a/src/components/workflows/modals/UpdateWorkflowModal.vue b/src/components/workflows/modals/UpdateWorkflowModal.vue
index f151066..e47ea0c 100644
--- a/src/components/workflows/modals/UpdateWorkflowModal.vue
+++ b/src/components/workflows/modals/UpdateWorkflowModal.vue
@@ -316,6 +316,7 @@ onMounted(() => {
     :static-backdrop="true"
     modal-label="Update Workflow Modal"
     v-on="{ 'hidden.bs.modal': modalClosed }"
+    size-modifier="lg"
   >
     <template #header>
       Update Workflow
diff --git a/src/router/workflowRoutes.ts b/src/router/workflowRoutes.ts
index f4d076c..4441fcb 100644
--- a/src/router/workflowRoutes.ts
+++ b/src/router/workflowRoutes.ts
@@ -20,6 +20,18 @@ export const workflowRoutes: RouteRecordRaw[] = [
       requiresDeveloperRole: true,
     },
   },
+  {
+    path: "developer/workflows/clowminfo",
+    name: "workflows-clowminfo",
+    component: () => import("../views/workflows/CreateClowmInfoView.vue"),
+    props: (route) => ({
+      workflow_version_id: route.query.workflow_version_id ?? undefined,
+      workflow_id: route.query.workflow_id ?? undefined,
+    }),
+    meta: {
+      requiresDeveloperRole: true,
+    },
+  },
   {
     path: "reviewer/workflows",
     name: "workflows-reviewer",
diff --git a/src/utils/DownloadJson.ts b/src/utils/DownloadJson.ts
new file mode 100644
index 0000000..9ad1f78
--- /dev/null
+++ b/src/utils/DownloadJson.ts
@@ -0,0 +1,6 @@
+export function createDownloadUrl(content: string, mimeType?: string): string {
+  const blob = new Blob([content], {
+    type: mimeType,
+  });
+  return URL.createObjectURL(blob);
+}
diff --git a/src/views/admin/AdminResourcesView.vue b/src/views/admin/AdminResourcesView.vue
index 095336b..9528652 100644
--- a/src/views/admin/AdminResourcesView.vue
+++ b/src/views/admin/AdminResourcesView.vue
@@ -147,7 +147,7 @@ function resetForm() {
     :resource="resourceState.inspectResource"
   />
   <div
-    class="row m-2 border-bottom mb-4 justify-content-between align-items-center"
+    class="row border-bottom mb-4 justify-content-between align-items-center"
   >
     <h2>Manage Resources</h2>
   </div>
diff --git a/src/views/admin/AdminUsersView.vue b/src/views/admin/AdminUsersView.vue
index a1f3e14..58eeccc 100644
--- a/src/views/admin/AdminUsersView.vue
+++ b/src/views/admin/AdminUsersView.vue
@@ -43,7 +43,7 @@ function searchUsers() {
 
 <template>
   <div
-    class="row m-2 border-bottom mb-4 justify-content-between align-items-center"
+    class="row border-bottom mb-4 justify-content-between align-items-center"
   >
     <h2>Manage Users</h2>
   </div>
diff --git a/src/views/object-storage/BucketsView.vue b/src/views/object-storage/BucketsView.vue
index b19d7d8..1797014 100644
--- a/src/views/object-storage/BucketsView.vue
+++ b/src/views/object-storage/BucketsView.vue
@@ -92,11 +92,10 @@ onMounted(() => {
     modalID="create-bucket-modal"
     v-if="!authStore.foreignUser"
   />
-  <div class="row m-2 border-bottom">
-    <div class="col-12"></div>
+  <div class="row border-bottom">
     <h2 class="mb-2">My Data Buckets</h2>
   </div>
-  <div class="row m-2 mt-4">
+  <div class="row mt-4">
     <div class="col-3">
       <div class="d-flex justify-content-between">
         <button
diff --git a/src/views/object-storage/S3KeysView.vue b/src/views/object-storage/S3KeysView.vue
index 8fce1fe..018415a 100644
--- a/src/views/object-storage/S3KeysView.vue
+++ b/src/views/object-storage/S3KeysView.vue
@@ -80,11 +80,11 @@ onMounted(() => {
   <bootstrap-toast toast-id="successKeyToast">
     Successfully deleted S3 Key {{ keyState.deletedKey }}
   </bootstrap-toast>
-  <div class="row m-2 border-bottom mt-4">
+  <div class="row border-bottom mt-4">
     <div class="col-12"></div>
     <h2 class="mb-2">S3 Keys</h2>
   </div>
-  <div class="row m-2 mt-4">
+  <div class="row mt-4">
     <div class="col-4">
       <div class="d-flex justify-content-between mb-4">
         <button
diff --git a/src/views/resources/ListResourcesView.vue b/src/views/resources/ListResourcesView.vue
index b615344..5787adc 100644
--- a/src/views/resources/ListResourcesView.vue
+++ b/src/views/resources/ListResourcesView.vue
@@ -53,7 +53,7 @@ onMounted(() => {
 </script>
 
 <template>
-  <div class="row m-2 border-bottom mb-4">
+  <div class="row border-bottom mb-4">
     <h2 class="mb-2">Available Resources</h2>
   </div>
   <div class="d-flex m-2 mb-3 align-items-center justify-content-start">
diff --git a/src/views/resources/MyResourcesView.vue b/src/views/resources/MyResourcesView.vue
index e9a3d23..5b2b365 100644
--- a/src/views/resources/MyResourcesView.vue
+++ b/src/views/resources/MyResourcesView.vue
@@ -62,7 +62,7 @@ onMounted(() => {
     modal-id="updateResourceModal"
   />
   <div
-    class="row m-2 border-bottom mb-4 justify-content-between align-items-center pb-2"
+    class="row border-bottom mb-4 justify-content-between align-items-center pb-2"
   >
     <h2 class="w-fit">My Resources</h2>
     <div class="w-fit">
diff --git a/src/views/resources/ReviewResourceView.vue b/src/views/resources/ReviewResourceView.vue
index 78f6785..e682c79 100644
--- a/src/views/resources/ReviewResourceView.vue
+++ b/src/views/resources/ReviewResourceView.vue
@@ -75,7 +75,7 @@ onMounted(() => {
     :resource="resourceState.inspectResource"
   />
   <div
-    class="row m-2 border-bottom mb-4 justify-content-between align-items-center"
+    class="row border-bottom mb-4 justify-content-between align-items-center"
   >
     <h2 class="w-fit">Resource Requests</h2>
     <span
diff --git a/src/views/workflows/ArbitraryWorkflowView.vue b/src/views/workflows/ArbitraryWorkflowView.vue
index a32fd64..ff7589e 100644
--- a/src/views/workflows/ArbitraryWorkflowView.vue
+++ b/src/views/workflows/ArbitraryWorkflowView.vue
@@ -11,7 +11,10 @@ import type { WorkflowIn } from "@/client/workflow";
 import { useWorkflowExecutionStore } from "@/stores/workflowExecutions";
 import ParameterSchemaFormComponent from "@/components/parameter-schema/ParameterSchemaFormComponent.vue";
 import BootstrapToast from "@/components/BootstrapToast.vue";
-import type { WorkflowParameters } from "@/types/WorkflowParameters";
+import type {
+  WorkflowMetaParameters,
+  WorkflowParameters,
+} from "@/types/WorkflowParameters";
 
 const props = defineProps<{
   wid: string;
@@ -94,10 +97,7 @@ watch(
 
 function startWorkflow(
   parameters: WorkflowParameters,
-  notes?: string,
-  logs_s3_path?: string,
-  debug_s3_path?: string,
-  provenance_s3_path?: string,
+  metaParameters: WorkflowMetaParameters,
 ) {
   if (workflowState.workflow) {
     errorToast?.hide();
@@ -106,9 +106,9 @@ function startWorkflow(
       .startDevExecution({
         git_commit_hash: workflowState.workflow.git_commit_hash,
         parameters: parameters,
-        logs_s3_path: logs_s3_path ? logs_s3_path : undefined,
-        debug_s3_path: debug_s3_path ? debug_s3_path : undefined,
-        provenance_s3_path: provenance_s3_path ? provenance_s3_path : undefined,
+        logs_s3_path: metaParameters.logs_s3_path,
+        debug_s3_path: metaParameters.debug_s3_path,
+        provenance_s3_path: metaParameters.provenance_s3_path,
         repository_url: workflowState.workflow.repository_url,
         token: workflowState.workflow.token ?? undefined,
         mode:
diff --git a/src/views/workflows/CreateClowmInfoView.vue b/src/views/workflows/CreateClowmInfoView.vue
new file mode 100644
index 0000000..8137d54
--- /dev/null
+++ b/src/views/workflows/CreateClowmInfoView.vue
@@ -0,0 +1,565 @@
+<script setup lang="ts">
+import CopyToClipboardIcon from "@/components/CopyToClipboardIcon.vue";
+import { computed, onMounted, reactive, watch } from "vue";
+import { createDownloadUrl } from "@/utils/DownloadJson";
+import type { ClowmInfo } from "@/types/ClowmInfo";
+import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
+import { useWorkflowStore } from "@/stores/workflows";
+import { DocumentationEnum } from "@/client/workflow";
+import DraggableLists from "@/components/DraggableLists.vue";
+
+const props = defineProps<{
+  workflow_id?: string;
+  workflow_version_id?: string;
+}>();
+
+const workflowRepository = useWorkflowStore();
+
+const infoState = reactive<ClowmInfo>({
+  inputParameters: [],
+  outputParameters: [],
+  exampleParameters: undefined,
+  dois: undefined,
+  resourceParameters: undefined,
+});
+
+const parameterPools = reactive<{
+  input: string[];
+  output: string[];
+  resources: string[];
+  examples: string[];
+}>({
+  examples: [],
+  input: [],
+  output: [],
+  resources: [],
+});
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+const parameterSchema = computed<Record<string, Record<string, any>>>(() => {
+  const schema =
+    workflowRepository.documentationFiles[props.workflow_version_id ?? ""]
+      ?.parameter_schema;
+  const a = schema?.["properties"] ?? {};
+  for (const group in schema?.["definitions"] ?? {}) {
+    for (const param in schema?.["definitions"]?.[group]?.["properties"] ??
+      {}) {
+      a[param] = schema["definitions"][group]["properties"][param];
+    }
+  }
+  return a;
+});
+
+function getParameterType(param: string): string | undefined {
+  return parameterSchema.value[param]?.["type"];
+}
+
+const infoStateString = computed<string>(() =>
+  JSON.stringify(infoState, null, 2),
+);
+const downloadUrl = computed<string>(() =>
+  createDownloadUrl(infoStateString.value, "application/json"),
+);
+
+watch(
+  () => workflowRepository.documentationFiles[props.workflow_version_id ?? ""],
+  (newVal, old) => {
+    if (newVal != old && newVal?.parameter_schema != undefined) {
+      updateParameterPools(newVal);
+    }
+  },
+  {
+    deep: true,
+  },
+);
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+function updateParameterPools(newVal?: Record<DocumentationEnum, any>) {
+  if (newVal?.parameter_schema) {
+    const parameters = extractParameterList(newVal.parameter_schema);
+    parameterPools.input = parameters.slice();
+    parameterPools.output = parameters.slice();
+    parameterPools.resources = parameters.slice();
+    parameterPools.examples = parameters.slice();
+    infoState.inputParameters = [];
+    infoState.outputParameters = [];
+    infoState.exampleParameters = undefined;
+    infoState.resourceParameters = undefined;
+  }
+  if (newVal?.clowm_info && Object.keys(newVal.clowm_info).length > 0) {
+    Object.assign(infoState, JSON.parse(JSON.stringify(newVal.clowm_info)));
+    parameterPools.input = parameterPools.input.filter(
+      (param) => !infoState.inputParameters.includes(param),
+    );
+    parameterPools.resources = parameterPools.resources?.filter(
+      (param) => !infoState.resourceParameters?.includes(param),
+    );
+    parameterPools.output = parameterPools.output.filter(
+      (param) => !infoState.outputParameters.includes(param),
+    );
+    parameterPools.examples = parameterPools.examples?.filter(
+      (param) =>
+        !Object.keys(infoState.exampleParameters ?? {}).includes(param),
+    );
+  }
+}
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+function extractParameterList(schema: Record<string, any>): string[] {
+  const groupedParameters = Object.keys(schema["definitions"] ?? {}).reduce(
+    (acc: string[], val) => [
+      ...acc,
+      ...Object.keys(schema["definitions"][val]["properties"]),
+    ],
+    [],
+  );
+  const singleParameters = Object.keys(schema["properties"] ?? {});
+  return [...groupedParameters, ...singleParameters];
+}
+
+function addDoi() {
+  if (infoState.dois == undefined) {
+    infoState.dois = [""];
+  } else {
+    infoState.dois.push("");
+  }
+}
+
+function removeDoi(index: number) {
+  if (infoState.dois?.length === 1) {
+    infoState.dois = undefined;
+  }
+  infoState.dois?.splice(index, 1);
+}
+
+function addExampleParameter(param: string, index: number) {
+  if (infoState.exampleParameters == undefined) {
+    infoState.exampleParameters = {};
+  }
+  parameterPools.examples.splice(index, 1);
+  switch (getParameterType(param)) {
+    case "integer": {
+      infoState.exampleParameters[param] = 0;
+      break;
+    }
+    case "number": {
+      infoState.exampleParameters[param] = 0;
+      break;
+    }
+    case "boolean": {
+      infoState.exampleParameters[param] = true;
+      break;
+    }
+    case "string": {
+      infoState.exampleParameters[param] =
+        parameterSchema.value[param]?.["enum"]?.[0] ?? "";
+      break;
+    }
+    default: {
+      infoState.exampleParameters[param] = "";
+      break;
+    }
+  }
+}
+
+function deleteExampleParameter(param: string) {
+  delete infoState.exampleParameters?.[param];
+  parameterPools.examples.push(param);
+  if (Object.keys(infoState.exampleParameters ?? {}).length === 0) {
+    infoState.exampleParameters = undefined;
+  }
+}
+
+onMounted(() => {
+  if (props.workflow_id && props.workflow_version_id) {
+    Promise.all([
+      workflowRepository.fetchWorkflowDocumentation(
+        props.workflow_id,
+        props.workflow_version_id,
+        DocumentationEnum.PARAMETER_SCHEMA,
+      ),
+      workflowRepository.fetchWorkflowDocumentation(
+        props.workflow_id,
+        props.workflow_version_id,
+        DocumentationEnum.CLOWM_INFO,
+      ),
+    ]).finally(() => {
+      updateParameterPools(
+        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+        workflowRepository.documentationFiles[props.workflow_version_id!],
+      );
+    });
+  }
+});
+</script>
+
+<template>
+  <div class="row border-bottom mb-4">
+    <h2 class="mb-2">Enhance your Workflow</h2>
+  </div>
+  <div>
+    Enhance your Workflow with CloWM:
+    <ul>
+      <li>
+        Maximize the potential of your workflow within CloWM by incorporating a
+        <code>clowm_info.json</code> file directly into your repository's root
+        directory. This simple addition empowers you to provide comprehensive
+        details about your workflow's parameters and overall structure,
+        enhancing its usability and user experience.
+      </li>
+      <li>
+        Customize Parameters: Infuse semantic meaning into various parameters,
+        enriching the understanding and functionality of your workflow.
+      </li>
+      <li>
+        Publication Linking: Seamlessly integrate a DOI link to your
+        publication, facilitating easy access to additional resources and
+        information.
+      </li>
+      <li>
+        Experience Enhanced Workflow: Elevate your workflow's presentation on
+        the CloWM platform by ensuring essential information is readily
+        available to users.
+      </li>
+    </ul>
+  </div>
+  <p>
+    Unlock the full potential of your workflow in CloWM by exploring the tabs
+    below for guidance on creating and implementing the
+    <code>clowm_info.json</code> file.
+  </p>
+  <div class="accordion mb-4" id="clowmInfoAccordion">
+    <div class="accordion-item">
+      <h2 class="accordion-header">
+        <button
+          class="accordion-button"
+          type="button"
+          data-bs-toggle="collapse"
+          data-bs-target="#clowmInfoAccordion-input"
+          aria-expanded="true"
+          aria-controls="clowmInfoAccordion-input"
+        >
+          Input Parameters
+        </button>
+      </h2>
+      <div
+        id="clowmInfoAccordion-input"
+        class="accordion-collapse collapse show"
+        data-bs-parent="#clowmInfoAccordion"
+      >
+        <div class="accordion-body">
+          <p>
+            <b>Designate Parameters as Input Files</b>: Assign semantic meaning
+            to parameters by dragging them from the left side to the right side
+            to designate them as input files.
+          </p>
+          <draggable-lists
+            :left-list="parameterPools.input"
+            :right-list="infoState.inputParameters"
+          >
+            <template #leftHeader>Workflow Parameters</template>
+            <template #rightHeader>Input Parameters</template>
+          </draggable-lists>
+        </div>
+      </div>
+    </div>
+    <div class="accordion-item">
+      <h2 class="accordion-header">
+        <button
+          class="accordion-button collapsed"
+          type="button"
+          data-bs-toggle="collapse"
+          data-bs-target="#clowmInfoAccordion-output"
+          aria-expanded="false"
+          aria-controls="clowmInfoAccordion-output"
+        >
+          Output Parameters
+        </button>
+      </h2>
+      <div
+        id="clowmInfoAccordion-output"
+        class="accordion-collapse collapse"
+        data-bs-parent="#clowmInfoAccordion"
+      >
+        <div class="accordion-body">
+          <p>
+            <b>Designate Parameters as Output</b>: Assign semantic meaning to
+            parameters by dragging them from the left side to the right side to
+            designate them as output files.
+          </p>
+          <draggable-lists
+            :left-list="parameterPools.output"
+            :right-list="infoState.outputParameters"
+          >
+            <template #leftHeader>Workflow Parameters</template>
+            <template #rightHeader>Output Parameters</template>
+          </draggable-lists>
+        </div>
+      </div>
+    </div>
+    <div class="accordion-item">
+      <h2 class="accordion-header">
+        <button
+          class="accordion-button collapsed"
+          type="button"
+          data-bs-toggle="collapse"
+          data-bs-target="#clowmInfoAccordion-resource"
+          aria-expanded="false"
+          aria-controls="clowmInfoAccordion-resource"
+        >
+          Resource Parameters
+        </button>
+      </h2>
+      <div
+        id="clowmInfoAccordion-resource"
+        class="accordion-collapse collapse"
+        data-bs-parent="#clowmInfoAccordion"
+      >
+        <div class="accordion-body">
+          <p>
+            <b>Designate Parameters as Resources</b>: Drag parameters from the
+            left side to the right side to imbue them with semantic meaning as
+            resource parameters. CloWM automatically generates a specialized
+            form input for users to configure these parameters.
+          </p>
+          <button
+            v-if="infoState.resourceParameters == undefined"
+            type="button"
+            class="btn btn-primary"
+            @click="infoState.resourceParameters = []"
+          >
+            This workflow needs resources
+          </button>
+          <draggable-lists
+            :left-list="parameterPools.resources"
+            :right-list="infoState.resourceParameters"
+            v-else
+          >
+            <template #leftHeader>Workflow Parameters</template>
+            <template #rightHeader>Resources Parameters</template>
+          </draggable-lists>
+        </div>
+      </div>
+    </div>
+    <div class="accordion-item">
+      <h2 class="accordion-header">
+        <button
+          class="accordion-button collapsed"
+          type="button"
+          data-bs-toggle="collapse"
+          data-bs-target="#clowmInfoAccordion-example"
+          aria-expanded="false"
+          aria-controls="clowmInfoAccordion-example"
+        >
+          Example Parameters
+        </button>
+      </h2>
+      <div
+        id="clowmInfoAccordion-example"
+        class="accordion-collapse collapse"
+        data-bs-parent="#clowmInfoAccordion"
+      >
+        <div class="accordion-body">
+          <p>
+            <b>Enhance Workflow Testing</b>: Incorporate example parameters
+            using publicly available test data. Easily achieve this by including
+            a sample dataset in your Git repository and linking its URL as the
+            input parameter.
+          </p>
+          <p>
+            <b>Guidance on Parameter Setting</b>: Avoid specifying parameters
+            that define output locations or resource paths, as these are heavily
+            dependent on the user or execution environment.
+          </p>
+          <div
+            class="d-flex flex-wrap overflow-y-scroll p-1 border rounded border-dashed mb-2"
+            style="max-height: 30vh"
+          >
+            <div
+              class="w-fit border px-2 rounded cursor-pointer m-1 parameter-container"
+              v-for="(param, index) in parameterPools.examples"
+              :key="param"
+              @click="addExampleParameter(param, index)"
+            >
+              {{ param }}
+            </div>
+          </div>
+          <table class="table table-bordered">
+            <thead>
+              <tr>
+                <th scope="col"><b>Parameter</b></th>
+                <th scope="col"><b>Value</b></th>
+              </tr>
+            </thead>
+            <tbody v-if="infoState.exampleParameters">
+              <tr
+                v-for="param in Object.keys(infoState.exampleParameters)"
+                :key="param"
+              >
+                <td style="width: 10%">{{ param }}</td>
+                <td class="d-flex justify-content-between align-items-center">
+                  <input
+                    v-if="
+                      getParameterType(param) === 'number' ||
+                      getParameterType(param) === 'integer'
+                    "
+                    type="number"
+                    class="form-control form-control-sm flex-grow"
+                    v-model="infoState.exampleParameters[param]"
+                    :step="getParameterType(param) === 'integer' ? 1 : 0.0001"
+                    :min="parameterSchema[param]['minimum']"
+                    :max="parameterSchema[param]['maximum']"
+                  />
+                  <div
+                    v-else-if="getParameterType(param) === 'boolean'"
+                    class="flex-grow"
+                  >
+                    <div class="form-check form-check-inline">
+                      <label
+                        class="form-check-label"
+                        :for="'trueOption' + param.replace(/\./g, '')"
+                        >True</label
+                      >
+                      <input
+                        class="form-check-input"
+                        type="radio"
+                        :name="'inlineRadioOptions' + param.replace(/\./g, '')"
+                        :id="'trueOption' + param.replace(/\./g, '')"
+                        :value="true"
+                        v-model="infoState.exampleParameters[param]"
+                      />
+                    </div>
+                    <div class="form-check form-check-inline">
+                      <input
+                        class="form-check-input"
+                        type="radio"
+                        :name="'inlineRadioOptions' + param.replace(/\./g, '')"
+                        :id="'falseOption' + param.replace(/\./g, '')"
+                        :value="false"
+                        v-model="infoState.exampleParameters[param]"
+                      />
+                      <label
+                        class="form-check-label"
+                        :for="'falseOption' + param.replace(/\./g, '')"
+                        >False</label
+                      >
+                    </div>
+                  </div>
+                  <select
+                    v-else-if="parameterSchema[param]?.['enum']"
+                    class="form-select form-select-sm flex-grow"
+                    v-model="infoState.exampleParameters[param]"
+                  >
+                    <option
+                      v-for="option in parameterSchema[param]?.['enum']"
+                      :key="option"
+                      :value="option"
+                    >
+                      {{ option }}
+                    </option>
+                  </select>
+                  <input
+                    v-else
+                    type="text"
+                    class="form-control form-control-sm flex-grow"
+                    v-model="infoState.exampleParameters[param]"
+                    :pattern="parameterSchema[param]?.['pattern']"
+                  />
+                  <button
+                    type="button"
+                    class="btn btn-outline-danger btn-sm ms-2"
+                    @click="deleteExampleParameter(param)"
+                  >
+                    Remove
+                  </button>
+                </td>
+              </tr>
+            </tbody>
+          </table>
+        </div>
+      </div>
+    </div>
+    <div class="accordion-item">
+      <h2 class="accordion-header">
+        <button
+          class="accordion-button collapsed"
+          type="button"
+          data-bs-toggle="collapse"
+          data-bs-target="#clowmInfoAccordion-dois"
+          aria-expanded="false"
+          aria-controls="clowmInfoAccordion-dois"
+        >
+          DOIs
+        </button>
+      </h2>
+      <div
+        id="clowmInfoAccordion-dois"
+        class="accordion-collapse collapse"
+        data-bs-parent="#clowmInfoAccordion"
+      >
+        <div class="accordion-body">
+          <p>
+            <b>Share Publications Easily</b>: Include the DOI for your
+            workflow's related publications and watch as the generated links
+            seamlessly appear on your workflow's dedicated page.
+          </p>
+          <button type="button" class="btn btn-primary" @click="addDoi">
+            Add DOI
+          </button>
+          <div v-if="infoState.dois">
+            <div
+              class="d-flex my-2"
+              v-for="(doi, index) in infoState.dois"
+              :key="index"
+            >
+              <button
+                type="button"
+                class="btn btn-outline-danger btn-sm"
+                @click="removeDoi(index)"
+              >
+                Remove
+              </button>
+              <input
+                type="text"
+                class="form-control mx-2"
+                v-model="infoState.dois[index]"
+                maxlength="48"
+                style="max-width: 400px"
+              />
+              <div class="align-self-center">
+                DOI Link:
+                <a
+                  :href="'https://doi.org/' + doi"
+                  target="_blank"
+                  class="align-self-center"
+                  >https://doi.org/{{ doi }}
+                  <font-awesome-icon
+                    icon="fa-solid fa-arrow-up-right-from-square"
+                    class="ms-1"
+                  />
+                </a>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+  <h5>Generated <code>clowm_info.json</code></h5>
+  <pre class="rounded w-100"><code>{{ infoState }}</code></pre>
+  <p>
+    Add this file to your Git repository, commit and push your changes and
+    register the new workflow version in CloWM
+  </p>
+  <copy-to-clipboard-icon button :text="infoStateString" />
+  <a class="btn btn-primary ms-2" :href="downloadUrl" download="clowm_info.json"
+    >Download to file
+    <font-awesome-icon icon="fa-solid fa-download" class="ms-1" />
+  </a>
+</template>
+
+<style scoped>
+.parameter-container:hover {
+  background: var(--bs-secondary-bg-subtle);
+}
+</style>
diff --git a/src/views/workflows/ListWorkflowExecutionsView.vue b/src/views/workflows/ListWorkflowExecutionsView.vue
index c3d87aa..5b96f03 100644
--- a/src/views/workflows/ListWorkflowExecutionsView.vue
+++ b/src/views/workflows/ListWorkflowExecutionsView.vue
@@ -165,7 +165,7 @@ onUnmounted(() => {
     :execution-id="executionsState.executionParameters"
   />
   <div
-    class="row m-2 border-bottom mb-4 justify-content-between align-items-center"
+    class="row border-bottom mb-4 justify-content-between align-items-center"
   >
     <h2 class="mb-2 w-fit">My Workflow Executions</h2>
     <div class="w-fit">
diff --git a/src/views/workflows/ListWorkflowsView.vue b/src/views/workflows/ListWorkflowsView.vue
index 56ccb5a..4c54cf1 100644
--- a/src/views/workflows/ListWorkflowsView.vue
+++ b/src/views/workflows/ListWorkflowsView.vue
@@ -51,15 +51,15 @@ function filterWorkflowWithoutVersion(workflow: WorkflowOut): boolean {
 }
 
 const processedWorkflows = computed<WorkflowOut[]>(() => {
-  return [
-    ...workflowRepository.workflows.filter(
-      (workflow) =>
-        filterWorkflowByString(workflow) &&
-        filterWorkflowWithoutVersion(workflow),
-    ),
-  ].sort((a, b) =>
+  const temp = workflowRepository.workflows.filter(
+    (workflow) =>
+      filterWorkflowByString(workflow) &&
+      filterWorkflowWithoutVersion(workflow),
+  );
+  temp.sort((a, b) =>
     filterFunctionMapping[workflowsState.sortByAttribute](a, b) ? 1 : -1,
   );
+  return temp;
 });
 
 onMounted(() => {
diff --git a/src/views/workflows/MyWorkflowsView.vue b/src/views/workflows/MyWorkflowsView.vue
index 48d1c28..8b5b935 100644
--- a/src/views/workflows/MyWorkflowsView.vue
+++ b/src/views/workflows/MyWorkflowsView.vue
@@ -114,7 +114,7 @@ onMounted(() => {
     modal-i-d="updateWorkflowCredentialsModal"
   />
   <div
-    class="row m-2 border-bottom mb-4 justify-content-between align-items-center pb-2"
+    class="row border-bottom mb-4 justify-content-between align-items-center pb-2"
   >
     <h2 class="w-fit">My Workflows</h2>
     <button
diff --git a/src/views/workflows/ReviewWorkflowsView.vue b/src/views/workflows/ReviewWorkflowsView.vue
index 8507574..ad15851 100644
--- a/src/views/workflows/ReviewWorkflowsView.vue
+++ b/src/views/workflows/ReviewWorkflowsView.vue
@@ -63,7 +63,7 @@ onMounted(() => {
 </script>
 
 <template>
-  <div class="row m-2 border-bottom mb-4">
+  <div class="row border-bottom mb-4">
     <h2 class="mb-2">Workflow Reviews</h2>
   </div>
   <div v-if="workflowsState.loading" class="text-center mt-5">
diff --git a/src/views/workflows/StartWorkflowView.vue b/src/views/workflows/StartWorkflowView.vue
index df2f89e..e6fde26 100644
--- a/src/views/workflows/StartWorkflowView.vue
+++ b/src/views/workflows/StartWorkflowView.vue
@@ -8,7 +8,10 @@ import { Toast } from "bootstrap";
 import { useWorkflowExecutionStore } from "@/stores/workflowExecutions";
 import BootstrapToast from "@/components/BootstrapToast.vue";
 import { useWorkflowStore } from "@/stores/workflows";
-import type { WorkflowParameters } from "@/types/WorkflowParameters";
+import type {
+  WorkflowMetaParameters,
+  WorkflowParameters,
+} from "@/types/WorkflowParameters";
 
 const executionRepository = useWorkflowExecutionStore();
 const workflowRepository = useWorkflowStore();
@@ -69,10 +72,7 @@ function downloadParameterSchema() {
 
 function startWorkflow(
   parameters: WorkflowParameters,
-  notes?: string,
-  logs_s3_path?: string,
-  debug_s3_path?: string,
-  provenance_s3_path?: string,
+  metaParameters: WorkflowMetaParameters,
 ) {
   if (props.versionId) {
     versionState.workflowExecutionError = undefined;
@@ -81,12 +81,10 @@ function startWorkflow(
       .startExecution({
         workflow_version_id: props.versionId,
         parameters: parameters,
-        notes: notes,
-        logs_s3_path: logs_s3_path?.length ?? 0 > 0 ? logs_s3_path : undefined,
-        debug_s3_path:
-          debug_s3_path?.length ?? 0 > 0 ? debug_s3_path : undefined,
-        provenance_s3_path:
-          provenance_s3_path?.length ?? 0 > 0 ? provenance_s3_path : undefined,
+        notes: metaParameters.notes,
+        logs_s3_path: metaParameters.logs_s3_path,
+        debug_s3_path: metaParameters.debug_s3_path,
+        provenance_s3_path: metaParameters.provenance_s3_path,
         mode_id: props.workflowModeId,
       })
       .then(() => {
-- 
GitLab