From de253f5acca34f20017895a2d8469b5ebd6032bf Mon Sep 17 00:00:00 2001
From: aidahosa1 <aisosa_idahosa@brown.edu>
Date: Tue, 30 Jul 2024 14:31:00 -0400
Subject: fight w css

---
 src/client/views/StyleProvider.tsx | 7 +++++++
 1 file changed, 7 insertions(+)

(limited to 'src/client/views/StyleProvider.tsx')

diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 40d0358d2..55e6217ff 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -30,6 +30,7 @@ import { DocumentView, DocumentViewProps } from './nodes/DocumentView';
 import { FieldViewProps } from './nodes/FieldView';
 import { StyleProp } from './StyleProp';
 import './StyleProvider.scss';
+import { IconTagBox } from './nodes/IconTagBox';
 
 function toggleLockedPosition(doc: Doc) {
     UndoManager.RunInBatch(() => Doc.toggleLockedPosition(doc), 'toggleBackground');
@@ -377,6 +378,11 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
                     return (<KeywordBox isEditing={false} doc={doc}></KeywordBox>)
                 }
             }
+
+            const iconTags = () => {
+                if (doc && doc![DocData].showIconTags)
+                    {return (<IconTagBox doc= {doc}></IconTagBox>)}
+            }
             return (
                 <>
                     {paint()}
@@ -384,6 +390,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
                     {filter()}
                     {audio()}
                     {keywords()}
+                    {iconTags()}
                 </>
             );
         }
-- 
cgit v1.2.3-70-g09d2


From 313b3d3e67689b175cdc85426ff6af809d476622 Mon Sep 17 00:00:00 2001
From: aidahosa1 <aisosa_idahosa@brown.edu>
Date: Tue, 17 Sep 2024 12:55:38 -0400
Subject: done with comments-- pull request

---
 eslint.config.mjs                                  |  66 +-----
 package-lock.json                                  | 176 +++++++++------
 package.json                                       |  12 +-
 src/client/apis/gpt/GPT.ts                         |   8 +-
 src/client/documents/Documents.ts                  |   2 -
 src/client/util/CurrentUserUtils.ts                |  79 +------
 src/client/util/Scripting.ts                       |   4 +-
 src/client/views/FilterPanel.scss                  |  12 --
 src/client/views/FilterPanel.tsx                   | 145 +++++--------
 src/client/views/PropertiesView.tsx                |   2 +-
 src/client/views/StyleProvider.tsx                 |   6 +
 .../views/collections/CollectionCardDeckView.tsx   |  41 ++--
 src/client/views/global/globalScripts.ts           | 240 ++-------------------
 src/client/views/nodes/IconTagBox.tsx              | 141 ++----------
 src/client/views/pdf/GPTPopup/GPTPopup.tsx         |   5 +-
 src/typings/index.d.ts                             |   2 +-
 16 files changed, 255 insertions(+), 686 deletions(-)

(limited to 'src/client/views/StyleProvider.tsx')

diff --git a/eslint.config.mjs b/eslint.config.mjs
index 619966f20..c69209327 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -1,57 +1,13 @@
-import pluginJs from '@eslint/js';
-import pluginReactConfig from 'eslint-plugin-react/configs/recommended.js';
-import globals from 'globals';
-import tseslint from 'typescript-eslint';
+import globals from "globals";
+import pluginJs from "@eslint/js";
+import tseslint from "typescript-eslint";
+import pluginReact from "eslint-plugin-react";
 
-export default [
-    {
-        languageOptions: { globals: { ...globals.browser, ...globals.node } },
-    },
-    pluginJs.configs.recommended,
-    ...tseslint.configs.recommended,
-    {
-        rules: {
-            'node/no-missing-import': 0,
-            'no-console': 'off',
-            'func-names': 'off',
-            'no-process-exit': 'off',
-            'object-shorthand': 'off',
-            'class-methods-use-this': 'off',
-            'single-quote': 'off',
-            'max-classes-per-file': 0,
-
-            'react/jsx-filename-extension': [
-                2,
-                {
-                    extensions: ['.js', '.jsx', '.ts', '.tsx'],
-                },
-            ],
 
-            'import/prefer-default-export': 'off',
-            'no-unused-expressions': 'off',
-            '@typescript-eslint/no-unused-expressions': 'off',
-            'prefer-template': 'off',
-            'no-inner-declarations': 'off',
-            'no-plusplus': 'off',
-            'no-multi-assign': 'off',
-            'no-underscore-dangle': 'off',
-            'no-nested-ternary': 'off',
-            'lines-between-class-members': 'off',
-            'no-shadow': 'off',
-            '@typescript-eslint/no-shadow': 'warn',
-            'no-unused-vars': 'off',
-            '@typescript-eslint/no-unused-vars': 'error',
-            '@typescript-eslint/no-namespace': 'off',
-            'react/destructuring-assignment': 0,
-            'prefer-arrow-callback': 'error',
-            'no-return-assign': 'error',
-            'no-await-in-loop': 'error',
-            'no-loop-func': 'error',
-            'no-cond-assign': 'error',
-            'no-use-before-define': 'error',
-            'no-explicit-any': 'error',
-            'no-restricted-globals': ['error', 'event'],
-        },
-    },
-    pluginReactConfig,
-];
+export default [
+  {files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"]},
+  {languageOptions: { globals: globals.browser }},
+  pluginJs.configs.recommended,
+  ...tseslint.configs.recommended,
+  pluginReact.configs.flat.recommended,
+];
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 1e738fcb1..3c9c5c81b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -92,7 +92,7 @@
                 "D": "^1.0.0",
                 "d3": "^7.8.5",
                 "depcheck": "^1.4.7",
-                "dotenv": "^16.3.1",
+                "dotenv": "^16.4.5",
                 "eslint-webpack-plugin": "^4.1.0",
                 "exif": "^0.6.0",
                 "exifr": "^7.1.3",
@@ -130,6 +130,7 @@
                 "image-data-uri": "^2.0.1",
                 "image-size": "^1.0.2",
                 "image-size-stream": "^1.1.0",
+                "install": "^0.13.0",
                 "is-plain-obj": "^4.1.0",
                 "jimp": "^0.22.10",
                 "jpeg-autorotate": "^9.0.0",
@@ -154,7 +155,7 @@
                 "node-stream-zip": "^1.15.0",
                 "nodemailer": "^6.9.7",
                 "nodemon": "^3.0.2",
-                "npm": "^10.2.5",
+                "npm": "^10.8.1",
                 "openai": "^4.26.0",
                 "p-limit": "^6.1.0",
                 "passport": "^0.7.0",
@@ -188,6 +189,7 @@
                 "react-compound-slider": "^3.4.0",
                 "react-datepicker": "^7.3.0",
                 "react-dom": "^18.2.0",
+                "react-draggable": "^4.4.6",
                 "react-grid-layout": "^1.4.4",
                 "react-icons": "^5.0.1",
                 "react-jsx-parser": "^2.0.0",
@@ -246,7 +248,7 @@
                 "xregexp": "^5.1.1"
             },
             "devDependencies": {
-                "@eslint/js": "^9.1.1",
+                "@eslint/js": "^9.10.0",
                 "@types/adm-zip": "^0.5.5",
                 "@types/animejs": "^3.1.12",
                 "@types/archiver": "^6.0.2",
@@ -295,9 +297,9 @@
                 "@types/youtube": "^0.1.0",
                 "chai": "^5.0.0",
                 "cross-env": "^7.0.3",
-                "eslint": "^9.9.0",
-                "eslint-plugin-react": "^7.34.1",
-                "globals": "^15.1.0",
+                "eslint": "^9.10.0",
+                "eslint-plugin-react": "^7.36.1",
+                "globals": "^15.9.0",
                 "jsdom": "^25.0.0",
                 "mocha": "^10.2.0",
                 "prettier": "^3.1.0",
@@ -306,7 +308,7 @@
                 "ts-loader": "^9.5.1",
                 "ts-node": "^10.9.1",
                 "ts-node-dev": "^2.0.0",
-                "typescript-eslint": "^8.2.0",
+                "typescript-eslint": "^8.6.0",
                 "webpack-dev-server": "^5.0.4"
             },
             "engines": {
@@ -2675,9 +2677,10 @@
             }
         },
         "node_modules/@eslint/js": {
-            "version": "9.9.1",
-            "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.1.tgz",
-            "integrity": "sha512-xIDQRsfg5hNBqHz04H1R3scSVwmI+KUbqjsQKHKQ1DAUSaUjYPReZZmS/5PNiKu1fUvzDd6H7DEDKACSEhu+TQ==",
+            "version": "9.10.0",
+            "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.10.0.tgz",
+            "integrity": "sha512-fuXtbiP5GWIn8Fz+LWoOMVf/Jxm+aajZYkhi6CuEm4SxymFM+eUWzbO9qXT+L0iCkL5+KGYMCSGxo686H19S1g==",
+            "license": "MIT",
             "engines": {
                 "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
             }
@@ -2690,6 +2693,17 @@
                 "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
             }
         },
+        "node_modules/@eslint/plugin-kit": {
+            "version": "0.1.0",
+            "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.1.0.tgz",
+            "integrity": "sha512-autAXT203ixhqei9xt+qkYOvY8l6LAFIdT2UXc/RPNeUVfqRF1BV94GTJyVPFKT8nFM6MyVJhjLj9E8JWvf5zQ==",
+            "dependencies": {
+                "levn": "^0.4.1"
+            },
+            "engines": {
+                "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+            }
+        },
         "node_modules/@ffmpeg/core": {
             "version": "0.12.6",
             "resolved": "https://registry.npmjs.org/@ffmpeg/core/-/core-0.12.6.tgz",
@@ -9993,16 +10007,17 @@
             "dev": true
         },
         "node_modules/@typescript-eslint/eslint-plugin": {
-            "version": "8.3.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.3.0.tgz",
-            "integrity": "sha512-FLAIn63G5KH+adZosDYiutqkOkYEx0nvcwNNfJAf+c7Ae/H35qWwTYvPZUKFj5AS+WfHG/WJJfWnDnyNUlp8UA==",
+            "version": "8.6.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.6.0.tgz",
+            "integrity": "sha512-UOaz/wFowmoh2G6Mr9gw60B1mm0MzUtm6Ic8G2yM1Le6gyj5Loi/N+O5mocugRGY+8OeeKmkMmbxNqUCq3B4Sg==",
             "dev": true,
+            "license": "MIT",
             "dependencies": {
                 "@eslint-community/regexpp": "^4.10.0",
-                "@typescript-eslint/scope-manager": "8.3.0",
-                "@typescript-eslint/type-utils": "8.3.0",
-                "@typescript-eslint/utils": "8.3.0",
-                "@typescript-eslint/visitor-keys": "8.3.0",
+                "@typescript-eslint/scope-manager": "8.6.0",
+                "@typescript-eslint/type-utils": "8.6.0",
+                "@typescript-eslint/utils": "8.6.0",
+                "@typescript-eslint/visitor-keys": "8.6.0",
                 "graphemer": "^1.4.0",
                 "ignore": "^5.3.1",
                 "natural-compare": "^1.4.0",
@@ -10026,15 +10041,16 @@
             }
         },
         "node_modules/@typescript-eslint/parser": {
-            "version": "8.3.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.3.0.tgz",
-            "integrity": "sha512-h53RhVyLu6AtpUzVCYLPhZGL5jzTD9fZL+SYf/+hYOx2bDkyQXztXSc4tbvKYHzfMXExMLiL9CWqJmVz6+78IQ==",
+            "version": "8.6.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.6.0.tgz",
+            "integrity": "sha512-eQcbCuA2Vmw45iGfcyG4y6rS7BhWfz9MQuk409WD47qMM+bKCGQWXxvoOs1DUp+T7UBMTtRTVT+kXr7Sh4O9Ow==",
             "dev": true,
+            "license": "BSD-2-Clause",
             "dependencies": {
-                "@typescript-eslint/scope-manager": "8.3.0",
-                "@typescript-eslint/types": "8.3.0",
-                "@typescript-eslint/typescript-estree": "8.3.0",
-                "@typescript-eslint/visitor-keys": "8.3.0",
+                "@typescript-eslint/scope-manager": "8.6.0",
+                "@typescript-eslint/types": "8.6.0",
+                "@typescript-eslint/typescript-estree": "8.6.0",
+                "@typescript-eslint/visitor-keys": "8.6.0",
                 "debug": "^4.3.4"
             },
             "engines": {
@@ -10054,13 +10070,14 @@
             }
         },
         "node_modules/@typescript-eslint/scope-manager": {
-            "version": "8.3.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.3.0.tgz",
-            "integrity": "sha512-mz2X8WcN2nVu5Hodku+IR8GgCOl4C0G/Z1ruaWN4dgec64kDBabuXyPAr+/RgJtumv8EEkqIzf3X2U5DUKB2eg==",
+            "version": "8.6.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.6.0.tgz",
+            "integrity": "sha512-ZuoutoS5y9UOxKvpc/GkvF4cuEmpokda4wRg64JEia27wX+PysIE9q+lzDtlHHgblwUWwo5/Qn+/WyTUvDwBHw==",
             "dev": true,
+            "license": "MIT",
             "dependencies": {
-                "@typescript-eslint/types": "8.3.0",
-                "@typescript-eslint/visitor-keys": "8.3.0"
+                "@typescript-eslint/types": "8.6.0",
+                "@typescript-eslint/visitor-keys": "8.6.0"
             },
             "engines": {
                 "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -10071,13 +10088,14 @@
             }
         },
         "node_modules/@typescript-eslint/type-utils": {
-            "version": "8.3.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.3.0.tgz",
-            "integrity": "sha512-wrV6qh//nLbfXZQoj32EXKmwHf4b7L+xXLrP3FZ0GOUU72gSvLjeWUl5J5Ue5IwRxIV1TfF73j/eaBapxx99Lg==",
+            "version": "8.6.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.6.0.tgz",
+            "integrity": "sha512-dtePl4gsuenXVwC7dVNlb4mGDcKjDT/Ropsk4za/ouMBPplCLyznIaR+W65mvCvsyS97dymoBRrioEXI7k0XIg==",
             "dev": true,
+            "license": "MIT",
             "dependencies": {
-                "@typescript-eslint/typescript-estree": "8.3.0",
-                "@typescript-eslint/utils": "8.3.0",
+                "@typescript-eslint/typescript-estree": "8.6.0",
+                "@typescript-eslint/utils": "8.6.0",
                 "debug": "^4.3.4",
                 "ts-api-utils": "^1.3.0"
             },
@@ -10095,10 +10113,11 @@
             }
         },
         "node_modules/@typescript-eslint/types": {
-            "version": "8.3.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.3.0.tgz",
-            "integrity": "sha512-y6sSEeK+facMaAyixM36dQ5NVXTnKWunfD1Ft4xraYqxP0lC0POJmIaL/mw72CUMqjY9qfyVfXafMeaUj0noWw==",
+            "version": "8.6.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.6.0.tgz",
+            "integrity": "sha512-rojqFZGd4MQxw33SrOy09qIDS8WEldM8JWtKQLAjf/X5mGSeEFh5ixQlxssMNyPslVIk9yzWqXCsV2eFhYrYUw==",
             "dev": true,
+            "license": "MIT",
             "engines": {
                 "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
             },
@@ -10108,13 +10127,14 @@
             }
         },
         "node_modules/@typescript-eslint/typescript-estree": {
-            "version": "8.3.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.3.0.tgz",
-            "integrity": "sha512-Mq7FTHl0R36EmWlCJWojIC1qn/ZWo2YiWYc1XVtasJ7FIgjo0MVv9rZWXEE7IK2CGrtwe1dVOxWwqXUdNgfRCA==",
+            "version": "8.6.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.6.0.tgz",
+            "integrity": "sha512-MOVAzsKJIPIlLK239l5s06YXjNqpKTVhBVDnqUumQJja5+Y94V3+4VUFRA0G60y2jNnTVwRCkhyGQpavfsbq/g==",
             "dev": true,
+            "license": "BSD-2-Clause",
             "dependencies": {
-                "@typescript-eslint/types": "8.3.0",
-                "@typescript-eslint/visitor-keys": "8.3.0",
+                "@typescript-eslint/types": "8.6.0",
+                "@typescript-eslint/visitor-keys": "8.6.0",
                 "debug": "^4.3.4",
                 "fast-glob": "^3.3.2",
                 "is-glob": "^4.0.3",
@@ -10140,6 +10160,7 @@
             "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
             "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
             "dev": true,
+            "license": "ISC",
             "dependencies": {
                 "brace-expansion": "^2.0.1"
             },
@@ -10155,6 +10176,7 @@
             "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
             "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
             "dev": true,
+            "license": "ISC",
             "bin": {
                 "semver": "bin/semver.js"
             },
@@ -10163,15 +10185,16 @@
             }
         },
         "node_modules/@typescript-eslint/utils": {
-            "version": "8.3.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.3.0.tgz",
-            "integrity": "sha512-F77WwqxIi/qGkIGOGXNBLV7nykwfjLsdauRB/DOFPdv6LTF3BHHkBpq81/b5iMPSF055oO2BiivDJV4ChvNtXA==",
+            "version": "8.6.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.6.0.tgz",
+            "integrity": "sha512-eNp9cWnYf36NaOVjkEUznf6fEgVy1TWpE0o52e4wtojjBx7D1UV2WAWGzR+8Y5lVFtpMLPwNbC67T83DWSph4A==",
             "dev": true,
+            "license": "MIT",
             "dependencies": {
                 "@eslint-community/eslint-utils": "^4.4.0",
-                "@typescript-eslint/scope-manager": "8.3.0",
-                "@typescript-eslint/types": "8.3.0",
-                "@typescript-eslint/typescript-estree": "8.3.0"
+                "@typescript-eslint/scope-manager": "8.6.0",
+                "@typescript-eslint/types": "8.6.0",
+                "@typescript-eslint/typescript-estree": "8.6.0"
             },
             "engines": {
                 "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -10185,12 +10208,13 @@
             }
         },
         "node_modules/@typescript-eslint/visitor-keys": {
-            "version": "8.3.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.3.0.tgz",
-            "integrity": "sha512-RmZwrTbQ9QveF15m/Cl28n0LXD6ea2CjkhH5rQ55ewz3H24w+AMCJHPVYaZ8/0HoG8Z3cLLFFycRXxeO2tz9FA==",
+            "version": "8.6.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.6.0.tgz",
+            "integrity": "sha512-wapVFfZg9H0qOYh4grNVQiMklJGluQrOUiOhYRrQWhx7BY/+I1IYb8BczWNbbUpO+pqy0rDciv3lQH5E1bCLrg==",
             "dev": true,
+            "license": "MIT",
             "dependencies": {
-                "@typescript-eslint/types": "8.3.0",
+                "@typescript-eslint/types": "8.6.0",
                 "eslint-visitor-keys": "^3.4.3"
             },
             "engines": {
@@ -10206,6 +10230,7 @@
             "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
             "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
             "dev": true,
+            "license": "Apache-2.0",
             "engines": {
                 "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
             },
@@ -17448,15 +17473,17 @@
             }
         },
         "node_modules/eslint": {
-            "version": "9.9.1",
-            "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.9.1.tgz",
-            "integrity": "sha512-dHvhrbfr4xFQ9/dq+jcVneZMyRYLjggWjk6RVsIiHsP8Rz6yZ8LvZ//iU4TrZF+SXWG+JkNF2OyiZRvzgRDqMg==",
+            "version": "9.10.0",
+            "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.10.0.tgz",
+            "integrity": "sha512-Y4D0IgtBZfOcOUAIQTSXBKoNGfY0REGqHJG6+Q81vNippW5YlKjHFj4soMxamKK1NXHUWuBZTLdU3Km+L/pcHw==",
+            "license": "MIT",
             "dependencies": {
                 "@eslint-community/eslint-utils": "^4.2.0",
                 "@eslint-community/regexpp": "^4.11.0",
                 "@eslint/config-array": "^0.18.0",
                 "@eslint/eslintrc": "^3.1.0",
-                "@eslint/js": "9.9.1",
+                "@eslint/js": "9.10.0",
+                "@eslint/plugin-kit": "^0.1.0",
                 "@humanwhocodes/module-importer": "^1.0.1",
                 "@humanwhocodes/retry": "^0.3.0",
                 "@nodelib/fs.walk": "^1.2.8",
@@ -17479,7 +17506,6 @@
                 "is-glob": "^4.0.0",
                 "is-path-inside": "^3.0.3",
                 "json-stable-stringify-without-jsonify": "^1.0.1",
-                "levn": "^0.4.1",
                 "lodash.merge": "^4.6.2",
                 "minimatch": "^3.1.2",
                 "natural-compare": "^1.4.0",
@@ -17506,10 +17532,11 @@
             }
         },
         "node_modules/eslint-plugin-react": {
-            "version": "7.35.0",
-            "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.35.0.tgz",
-            "integrity": "sha512-v501SSMOWv8gerHkk+IIQBkcGRGrO2nfybfj5pLxuJNFTPxxA3PSryhXTK+9pNbtkggheDdsC0E9Q8CuPk6JKA==",
+            "version": "7.36.1",
+            "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.36.1.tgz",
+            "integrity": "sha512-/qwbqNXZoq+VP30s1d4Nc1C5GTxjJQjk4Jzs4Wq2qzxFM7dSmuG2UkIjg2USMLh3A/aVcUNrK7v0J5U1XEGGwA==",
             "dev": true,
+            "license": "MIT",
             "dependencies": {
                 "array-includes": "^3.1.8",
                 "array.prototype.findlast": "^1.2.5",
@@ -18101,6 +18128,7 @@
             "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
             "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
             "dev": true,
+            "license": "MIT",
             "dependencies": {
                 "@nodelib/fs.stat": "^2.0.2",
                 "@nodelib/fs.walk": "^1.2.3",
@@ -18117,6 +18145,7 @@
             "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
             "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
             "dev": true,
+            "license": "ISC",
             "dependencies": {
                 "is-glob": "^4.0.1"
             },
@@ -19305,6 +19334,7 @@
             "resolved": "https://registry.npmjs.org/globals/-/globals-15.9.0.tgz",
             "integrity": "sha512-SmSKyLLKFbSr6rptvP8izbyxJL4ILwqO9Jg23UA0sDlGlu58V59D1//I3vlc0KJphVdUR7vMjHIplYnzBxorQA==",
             "dev": true,
+            "license": "MIT",
             "engines": {
                 "node": ">=18"
             },
@@ -19458,7 +19488,8 @@
             "version": "1.4.0",
             "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
             "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
-            "dev": true
+            "dev": true,
+            "license": "MIT"
         },
         "node_modules/grid-index": {
             "version": "1.1.0",
@@ -20727,6 +20758,14 @@
                 "inspect-function": "^0.3.1"
             }
         },
+        "node_modules/install": {
+            "version": "0.13.0",
+            "resolved": "https://registry.npmjs.org/install/-/install-0.13.0.tgz",
+            "integrity": "sha512-zDml/jzr2PKU9I8J/xyZBQn8rPCAY//UOYNmR01XwNwyfhEWObo2SWfSl1+0tm1u6PhxLwDnfsT/6jB7OUxqFA==",
+            "engines": {
+                "node": ">= 0.10"
+            }
+        },
         "node_modules/internal-slot": {
             "version": "1.0.7",
             "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz",
@@ -27554,6 +27593,7 @@
             "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
             "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
             "dev": true,
+            "license": "MIT",
             "engines": {
                 "node": ">= 8"
             }
@@ -37377,6 +37417,7 @@
             "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
             "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==",
             "dev": true,
+            "license": "MIT",
             "engines": {
                 "node": ">=16"
             },
@@ -38075,14 +38116,15 @@
             "integrity": "sha512-7sI4e/bZijOzyURng88oOFZCISQPTHozfE2sUu5AviFYk5QV7fYGb6YiDl+vKjF/pICA354JImBImL9XJWUvdQ=="
         },
         "node_modules/typescript-eslint": {
-            "version": "8.3.0",
-            "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.3.0.tgz",
-            "integrity": "sha512-EvWjwWLwwKDIJuBjk2I6UkV8KEQcwZ0VM10nR1rIunRDIP67QJTZAHBXTX0HW/oI1H10YESF8yWie8fRQxjvFA==",
+            "version": "8.6.0",
+            "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.6.0.tgz",
+            "integrity": "sha512-eEhhlxCEpCd4helh3AO1hk0UP2MvbRi9CtIAJTVPQjuSXOOO2jsEacNi4UdcJzZJbeuVg1gMhtZ8UYb+NFYPrA==",
             "dev": true,
+            "license": "MIT",
             "dependencies": {
-                "@typescript-eslint/eslint-plugin": "8.3.0",
-                "@typescript-eslint/parser": "8.3.0",
-                "@typescript-eslint/utils": "8.3.0"
+                "@typescript-eslint/eslint-plugin": "8.6.0",
+                "@typescript-eslint/parser": "8.6.0",
+                "@typescript-eslint/utils": "8.6.0"
             },
             "engines": {
                 "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
diff --git a/package.json b/package.json
index cb4633c6d..f92a533a9 100644
--- a/package.json
+++ b/package.json
@@ -23,7 +23,7 @@
         "tsc": "tsc -t es5"
     },
     "devDependencies": {
-        "@eslint/js": "^9.1.1",
+        "@eslint/js": "^9.10.0",
         "@types/adm-zip": "^0.5.5",
         "@types/animejs": "^3.1.12",
         "@types/archiver": "^6.0.2",
@@ -72,9 +72,9 @@
         "@types/youtube": "^0.1.0",
         "chai": "^5.0.0",
         "cross-env": "^7.0.3",
-        "eslint": "^9.9.0",
-        "eslint-plugin-react": "^7.34.1",
-        "globals": "^15.1.0",
+        "eslint": "^9.10.0",
+        "eslint-plugin-react": "^7.36.1",
+        "globals": "^15.9.0",
         "jsdom": "^25.0.0",
         "mocha": "^10.2.0",
         "prettier": "^3.1.0",
@@ -83,7 +83,7 @@
         "ts-loader": "^9.5.1",
         "ts-node": "^10.9.1",
         "ts-node-dev": "^2.0.0",
-        "typescript-eslint": "^8.2.0",
+        "typescript-eslint": "^8.6.0",
         "webpack-dev-server": "^5.0.4"
     },
     "dependencies": {
@@ -132,7 +132,6 @@
         "@types/web": "^0.0.157",
         "@types/webpack-hot-middleware": "^2.25.9",
         "@webscopeio/react-textarea-autocomplete": "^4.9.2",
-        "D": "^1.0.0",
         "adm-zip": "^0.5.10",
         "archiver": "^7.0.1",
         "async": "^3.2.5",
@@ -169,6 +168,7 @@
         "csv-parser": "^3.0.0",
         "csv-stringify": "^6.4.4",
         "csvtojson": "^2.0.10",
+        "D": "^1.0.0",
         "d3": "^7.8.5",
         "depcheck": "^1.4.7",
         "dotenv": "^16.4.5",
diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts
index 0b1a5160c..2657bf12e 100644
--- a/src/client/apis/gpt/GPT.ts
+++ b/src/client/apis/gpt/GPT.ts
@@ -49,7 +49,6 @@ const callTypeMap: { [type: string]: GPTCallOpts } = {
         maxTokens: 2048,
         temp: 0.25,
         prompt: "The user is going to give you a list of descriptions. Each one is separated by `======` on either side. Descriptions will vary in length, so make sure to only separate when you see `======`. Sort them by the user's specifications. Make sure each description is only in the list once. Each item should be separated by `======`. Immediately afterward, surrounded by `------` on BOTH SIDES, provide some insight into your reasoning for the way you sorted (and mention nothing about the formatting details given in this description). It is VERY important that you format it exactly as described, ensuring the proper number of `=` and `-` (6 of each) and NO commas"
-        // prompt: "I'm going to give you a list of descriptions. Each one is separated by `======` on either side. Descriptions will vary in length, so make sure to only separate when you see `======`. Sort them into lists by shared content. Make sure each description is in only one list. Each list should be separated by `======` with the elements within it separated by `~~~~~~`. Immediately afterward, surrounded by `------` on BOTH SIDES, provide some insight into your reasoning for the way you sorted. It is VERY important that you format it exactly as described, ensuring the proper number of `=` `~` and `-` (6 of each) and no commas.Try to create around 4 groups, but a little more or less is ok. Also, I may provide some more insight after this colon:"
     },
     describe: { model: 'gpt-4-vision-preview', maxTokens: 2048, temp: 0, prompt: 'Describe these images in 3-5 words' },
     chatcard: { model: 'gpt-4-turbo', maxTokens: 512, temp: 0.5, prompt: 'Answer the following question as a short flashcard response. Do not include a label.' },
@@ -64,7 +63,7 @@ const callTypeMap: { [type: string]: GPTCallOpts } = {
         model: 'gpt-4-turbo',
         maxTokens: 1024,
         temp: 0,
-        prompt: "BRIEFLY (<75 words) provide a definition for the following term. It will be used as a rubric to evaluate the user's understanding of the topic",
+        prompt: "BRIEFLY (<25 words) provide a definition for the following term. It will be used as a rubric to evaluate the user's understanding of the topic",
     },
 
     type: {
@@ -106,11 +105,6 @@ const gptAPICall = async (inputTextIn: string, callType: GPTCallType, prompt?: a
     try {
         lastCall = inputText;
 
-        // const configuration: ClientOptions = {
-        //     apiKey: process.env.OPENAI_KEY,
-        //     dangerouslyAllowBrowser: true,
-        // };
-
         const usePrompt = prompt ? prompt + opts.prompt : opts.prompt;
         const messages: ChatCompletionMessageParam[] = [
             { role: 'system', content: usePrompt },
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 188f07991..83e236cba 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -493,8 +493,6 @@ export class DocumentOptions {
     // cardSort_visibleSortGroups?: List<number>; // which sorting values are being filtered (shown)
     cardSort_isDesc?: BOOLt = new BoolInfo('whether the cards are sorted ascending or descending');
     // test?: STRt = new StrInfo('testing for filtering')
-    keywords?: MAPt = new MapInfo('keywords', true);
-
 }
 
 export const DocOptions = new DocumentOptions();
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 702d6e6e5..997d5c5a9 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -669,15 +669,10 @@ pie title Minerals in my tap water
     }
     static cardTools(): Button[] {
         return [
-            // { btnList: new List<string>(["Time", "Type", "Color", "Chat GPT", "Custom 1", "Custom 2", "Custom 3" ]),
-            //     title: "Sort Type",  toolTip: "Card Sort Type",   btnType: ButtonType.DropdownList, ignoreClick: true, width: 100, scripts: { script:  '{ return setCardSort(value, _readOnly_); }'}},
-            // { title: "Font",     toolTip: "Font", width: 100,     btnType: ButtonType.DropdownList,                      toolType:"font",     ignoreClick: true,   scripts: {script:  '{ return setFontAttr(this.toolType, value, _readOnly_);}'},
-            //   btnList: new List<string>(["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]) },
             { title: "Time",      icon:"hourglass-half", toolTip:"Sort by most recent document creation",      btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"time",   funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
             { title: "Type",      icon:"eye",            toolTip:"Sort by document type",                      btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"docType",funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
             { title: "Color",     icon:"palette",        toolTip:"Sort by document color",                     btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"color",  funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
             { title: "Tags",     icon:"bolt",        toolTip:"Sort by document's tags",                     btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"tag",  funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
-            // { title: "AI Sort",     icon:"robot",        toolTip:"Have Chat GPT sort your cards for you !",   btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"chat",  funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
             { title: "Pile",     icon:"layer-group",        toolTip:"View the cards as a pile in the free form view !",   btnType: ButtonType.ClickButton,  expertMode: false, toolType:"pile",  funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
             { title: "Chat Popup", icon:"lightbulb",     toolTip:"Toggle the chat popup's visibility!", width: 45 ,btnType: ButtonType.ToggleButton, expertMode: false, toolType:"toggle-chat",  funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'} },
 
@@ -686,62 +681,23 @@ pie title Minerals in my tap water
                 { title: "Ascending",     toolTip: "Sort the cards in ascending order",   btnType: ButtonType.ToggleButton, icon: "sort-up",  toolType:"up",  ignoreClick: true,  scripts: {onClick: '{ return showFreeform(this.toolType, _readOnly_);}' }},
                 { title: "Descending",   toolTip: "Sort the cards in descending order",btnType: ButtonType.ToggleButton, icon: "sort-down",toolType:"down",ignoreClick: true,  scripts: {onClick: '{ return showFreeform(this.toolType, _readOnly_);}'} },
             ]},
-
-            // { title: "Filter", icon:"Filter",     toolTip:"Filter cards by tags", width: 150, subMenu: this.cardGroupTools(), expertMode: false, toolType:CollectionViewType.Card,  funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} },
-            
-            
-
-
-            
-
-            // // { title: "AIs",   icon:"Visibility",   toolTip:"Filter AI labels",        subMenu: this.cardGroupTools("robot"),      expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden:`!showFreeform("chat", true)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} },
-            // // { title: "Custom",  icon:"heart",     toolTip:"Add Like labels",         btnType: ButtonType.ToggleButton,          expertMode: false, toolType:"like",                  funcs: {hidden:`showFreeform ("like", true)`},scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
-            // { title: "1st", icon:"Visibility",     toolTip:"Filter likes", width: 150, subMenu: this.cardGroupTools("heart"),     expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden:`!showFreeform("like", true)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} },
-            // // { title: "Custom",  icon:"star",      toolTip:"Add Star labels",         btnType: ButtonType.ToggleButton,          expertMode: false, toolType:"star",                  funcs: {hidden:`showFreeform ("star", true)`},scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
-            // { title: "2nd", icon:"Visibility",     toolTip:"Filter stars", width: 150, subMenu: this.cardGroupTools("star"),      expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden:`!showFreeform("star", true)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, 
-            // // { title: "Custom",  icon:"cloud", toolTip:"Add Idea labels",         btnType: ButtonType.ToggleButton,          expertMode: false, toolType:"idea",                  funcs: {hidden:`showFreeform ("idea", true)`},scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
-            // { title: "3rd", icon:"Visibility",     toolTip:"Filter ideas", width: 150, subMenu: this.cardGroupTools("cloud"), expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden:`!showFreeform("idea", true)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} },
          ]
     }
-    // static labelTools(): Button[] {
-    //     return [
-    //         // { title: "Smart",    icon:"robot",     toolTip:"Have ChatGPT Label and sort your cards for you!",           btnType: ButtonType.ToggleButton,          expertMode: false, toolType:"chat",                  funcs: {hidden:`showFreeform ("chat", true)`},scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
-    //         { title: "AIs",   icon:"AI Sort",   toolTip:"Filter AI labels",        subMenu: this.cardGroupTools("robot"),      expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden:`!showFreeform("chat", true)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} },
-    //         // { title: "Custom",  icon:"heart",     toolTip:"Add Like labels",         btnType: ButtonType.ToggleButton,          expertMode: false, toolType:"like",                  funcs: {hidden:`showFreeform ("like", true)`},scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
-    //         { title: "1st", icon:"1st",     toolTip:"Filter likes", width: 10, subMenu: this.cardGroupTools("heart"),     expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden:`!showFreeform("like", true)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} },
-    //         // { title: "Custom",  icon:"star",      toolTip:"Add Star labels",         btnType: ButtonType.ToggleButton,          expertMode: false, toolType:"star",                  funcs: {hidden:`showFreeform ("star", true)`},scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
-    //         { title: "2nd", icon:"2nd",     toolTip:"Filter stars", width: 80, subMenu: this.cardGroupTools("star"),      expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden:`!showFreeform("star", true)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, 
-    //         // { title: "Custom",  icon:"cloud", toolTip:"Add Idea labels",         btnType: ButtonType.ToggleButton,          expertMode: false, toolType:"idea",                  funcs: {hidden:`showFreeform ("idea", true)`},scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
-    //         { title: "3rd", icon:"3rd",     toolTip:"Filter ideas", width: 80, subMenu: this.cardGroupTools("cloud"), expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden:`!showFreeform("idea", true)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} },
-    //     ]
-    // }
+  
     static tagGroupTools(): Button[] {
-        if (!Doc.UserDoc().activeDashboard){
-            Doc.UserDoc().myFilterHotKeyTitles = new List<string>(['Star', 'Heart', 'Bolt', 'Cloud' ])
-
-            Doc.UserDoc()['Star'] = 'star'
-            Doc.UserDoc()['Heart'] = 'heart'
-            Doc.UserDoc()['Bolt'] = 'bolt'
-            Doc.UserDoc()['Cloud'] = 'cloud'
+        if (!Doc.UserDoc().activeDashboard) {
+        Doc.UserDoc().myFilterHotKeyTitles = new List<string>(['Star', 'Heart', 'Bolt', 'Cloud']);
         
-        }
-
-
+        ['Star', 'Heart', 'Bolt', 'Cloud'].forEach(key => {
+            Doc.UserDoc()[key] = key.toLowerCase();
+        });
+}
         // hack: if there's no dashboard, create default filters.  otherwise, just make sure that the Options button is preserved
         return (Doc.UserDoc().activeDashboard ? [] : [
             { title: "Star", isSystem: false, icon: "star", toolTip:"Click to toggle the star group's visibility",  btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"star", funcs: {}, scripts: { onClick: '{ return handleTags(this.toolType, _readOnly_);}'}},
             { title: "Heart", isSystem: false,icon: "heart", toolTip:"Click to toggle the heart group's visibility",  btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"heart", funcs: {}, scripts: { onClick: '{ return handleTags(this.toolType, _readOnly_);}'}},
             { title: "Bolt", isSystem: false,icon: "bolt", toolTip:"Click to toggle the bolt group's visibility",  btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"bolt", funcs: {}, scripts: { onClick: '{ return handleTags(this.toolType, _readOnly_);}'}},
             { title: "Cloud", isSystem: false,icon: "cloud", toolTip:"Click to toggle the cloud group's visibility",  btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"cloud", funcs: {}, scripts: { onClick: '{ return handleTags(this.toolType, _readOnly_);}'}},
-            
-            
-            // { title: "Group 1", icon, toolTip:"Click to toggle group 1's visibility",  btnType: ButtonType.ToggleButton, width: 40,  expertMode: false, toolType:"1", funcs: {hidden:`!cardHasLabel(this.toolType)`}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
-            // { title: "Group 2", icon, toolTip:"Click to toggle group 2's visibility",  btnType: ButtonType.ToggleButton, width: 40, expertMode: false, toolType:"2", funcs: {hidden:`!cardHasLabel(this.toolType)`}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
-            // { title: "Group 3", icon, toolTip:"Click to toggle group 3's visibility",  btnType: ButtonType.ToggleButton, width: 40, expertMode: false, toolType:"3", funcs: {hidden:`!cardHasLabel(this.toolType)`}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
-            // { title: "Group 4", icon, toolTip:"Click to toggle group 4's visibility",  btnType: ButtonType.ToggleButton,  width: 40, expertMode: false, toolType:"4", funcs: {hidden:`!cardHasLabel(this.toolType)`}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
-            // { title: "", icon, toolTip:"Click to toggle group 5's visibility",  btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"5", funcs: {hidden:`!cardHasLabel(this.toolType)`}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
-            // { title: "", icon, toolTip:"Click to toggle group 6's visibility",  btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"6", funcs: {hidden:`!cardHasLabel(this.toolType)`}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
-            // { title: "", icon, toolTip:"Click to toggle group 7's visibility",  btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"7", funcs: {hidden:`!cardHasLabel(this.toolType)`}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
         ]).concat([
             { title: "Options", isSystem: true,icon: "gear", toolTip:"Click to customize your filter panel", btnType: ButtonType.ClickButton,  expertMode: false, toolType:"opts", funcs: {}, scripts: { onClick: '{ return handleTags(this.toolType, _readOnly_);}'}}
         ])
@@ -856,19 +812,6 @@ pie title Minerals in my tap water
             { title: "View",    icon: "View",          toolTip: "View tools",           subMenu: CurrentUserUtils.viewTools(),   expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Always available
             { title: "Stack",   icon: "View",          toolTip: "Stacking tools",       subMenu: CurrentUserUtils.stackTools(),  expertMode: false, toolType:CollectionViewType.Stacking, funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Always available
             
-            
-            // { title: "SortType", btnList: new List<string>(["Time", "Type", "Color", "ChatGPT", "Custom 1", "Custom 2", "Custom 3" ]),
-            //       toolTip: "Card Sort Type",   btnType: ButtonType.DropdownList, ignoreClick: true, width: 100, scripts: { script:  '{ return showFreeform(value, _readOnly_); }'  },  
-            //     //   funcs: {hidden: `!SelectedDocType("card", this.expertMode)`}
-            //     },
-            // { title: "Visibility",   icon:"Visibility",   toolTip:"Filter AI labels",        subMenu: this.cardGroupTools("robot"),      expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden:`!showFreeform("chat", true)`, width: 100, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} },
-            // // { title: "Custom",  icon:"heart",     toolTip:"Add Like labels",         btnType: ButtonType.ToggleButton,          expertMode: false, toolType:"like",                  funcs: {hidden:`showFreeform ("like", true)`},scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
-            // { title: "Visibility", icon:"Visibility",     toolTip:"Filter likes", width: 10, subMenu: this.cardGroupTools("heart"),     expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden:`!showFreeform("like", true)`, width: 100, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} },
-            // // { title: "Custom",  icon:"star",      toolTip:"Add Star labels",         btnType: ButtonType.ToggleButton,          expertMode: false, toolType:"star",                  funcs: {hidden:`showFreeform ("star", true)`},scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
-            // { title: "Visibility", icon:"Visibility",     toolTip:"Filter stars", width: 80, subMenu: this.cardGroupTools("star"),      expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden:`!showFreeform("star", true)`, width: 100, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, 
-            // // { title: "Custom",  icon:"cloud", toolTip:"Add Idea labels",         btnType: ButtonType.ToggleButton,          expertMode: false, toolType:"idea",                  funcs: {hidden:`showFreeform ("idea", true)`},scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
-            // { title: "Visibility", icon:"Visibility",     toolTip:"Filter ideas", width: 80, subMenu: this.cardGroupTools("cloud"), expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden:`!showFreeform("idea", true)`,width: 100, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} },
-            
             { title: "Card",    icon: "Card",          toolTip: "Card View Tools",            subMenu: CurrentUserUtils.cardTools(),   expertMode: false, toolType:CollectionViewType.Card,     funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Always available
             // { title: "Create",   icon: "Create",         toolTip: "Assign card labels",   subMenu: CurrentUserUtils.labelTools(),  expertMode: false, toolType:CollectionViewType.Card,     funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Always available
             { title: "Web",     icon: "Web",           toolTip: "Web functions",        subMenu: CurrentUserUtils.webTools(),    expertMode: false, toolType:DocumentType.WEB,            funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Only when Web is selected
@@ -877,13 +820,7 @@ pie title Minerals in my tap water
             { title: "Schema",  icon: "Schema",linearBtnWidth:58,toolTip: "Schema functions",subMenu: CurrentUserUtils.schemaTools(),expertMode: false,toolType:CollectionViewType.Schema,funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Only when Schema is selected
             { title: "Filter", icon:"=",     toolTip:"Filter cards by tags", btnType: ButtonType.MultiToggleButton, width: 150, ignoreClick: true,toolType:DocumentType.COL, 
             subMenu: this.tagGroupTools(), funcs: {hidden: '!SelectedDocType(this.toolType, this.expertMode)'}, 
-            // [
-            //     { title: "Star", icon: "star", toolTip:"Click to toggle the star group's visibility",  btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"star", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
-            //     { title: "Heart", icon: "heart", toolTip:"Click to toggle the heart group's visibility",  btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"heart", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
-            //     { title: "Bolt", icon: "bolt", toolTip:"Click to toggle the bolt group's visibility",  btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"bolt", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
-            //     { title: "Cloud", icon: "cloud", toolTip:"Click to toggle the cloud group's visibility",  btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"cloud", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
-
-            // ]
+            
         },
            ];
            
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index c63d3d7cb..cb314e3f1 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -1,7 +1,7 @@
 // export const ts = (window as any).ts;
 // import * as typescriptlib from '!!raw-loader!../../../node_modules/typescript/lib/lib.d.ts'
 // import * as typescriptes5 from '!!raw-loader!../../../node_modules/typescript/lib/lib.es5.d.ts'
-import typescriptlib from 'type_decls.d';
+// import typescriptlib from 'type_decls.d';
 import * as ts from 'typescript';
 import { Doc, FieldType } from '../../fields/Doc';
 import { RefField } from '../../fields/RefField';
@@ -248,7 +248,7 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp
     const funcScript = `(function(${paramString})${reqTypes} { ${body} })`;
     host.writeFile('file.ts', funcScript);
 
-    if (typecheck) host.writeFile('node_modules/typescript/lib/lib.d.ts', typescriptlib);
+    // if (typecheck) host.writeFile('node_modules/typescript/lib/lib.d.ts', typescriptlib);
     const program = ts.createProgram(['file.ts'], {}, host);
     const testResult = program.emit();
     const outputText = host.readFile('file.js');
diff --git a/src/client/views/FilterPanel.scss b/src/client/views/FilterPanel.scss
index 34c3d2fc1..fbdebde0d 100644
--- a/src/client/views/FilterPanel.scss
+++ b/src/client/views/FilterPanel.scss
@@ -350,19 +350,7 @@
 
 }
 
-.drawing-box{
-    position: absolute;
-    z-index: 10000;
-    border-color: black;
-    border-style: solid;
-    border-width: medium;
-    border-radius: 10%;
-    background-color: #323232;
-
-
 
-
-}
 // .sliderBox-outerDiv {
 //     width: 30%;// width: calc(100% - 14px);   // 14px accounts for handles that are at the max value of the slider that would extend outside the box
 //     height: 40; // height: 100%;
diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
index e15285007..e73d9c2c9 100644
--- a/src/client/views/FilterPanel.tsx
+++ b/src/client/views/FilterPanel.tsx
@@ -11,7 +11,7 @@ import { List } from '../../fields/List';
 import { RichTextField } from '../../fields/RichTextField';
 import { SearchUtil } from '../util/SearchUtil';
 import { SnappingManager } from '../util/SnappingManager';
-import { undoable } from '../util/UndoManager';
+import { undoable, undoBatch } from '../util/UndoManager';
 import { FieldsDropdown } from './FieldsDropdown';
 import './FilterPanel.scss';
 import { DocumentView } from './nodes/DocumentView';
@@ -20,26 +20,14 @@ import { ObservableReactComponent } from './ObservableReactComponent';
 import { Button } from '../util/CurrentUserUtils';
 import { ButtonType } from './nodes/FontIconBox/FontIconBox';
 import { DocCast } from '../../fields/Types';
-// import { Docs } from '../../documents/Documents';
-import { Docs } from '../documents/Documents';
 import { CurrentUserUtils } from '../util/CurrentUserUtils';
-import { DocumentOptions } from '../documents/Documents';
-import { DocUtils } from '../documents/DocUtils';
-import { dropActionType } from '../util/DropActionTypes';
-import { Toggle } from 'browndash-components';
-import { SettingsManager } from '../util/SettingsManager';
 import { StrCast } from '../../fields/Types';
-import { ToggleType } from 'browndash-components';
-import { MultiToggle } from 'browndash-components';
-import { Type } from 'browndash-components';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { DocData } from '../../fields/DocSymbols';
-import { DocumentType } from '../documents/DocumentTypes';
 import { Tooltip } from '@mui/material';
 import { useLocalObservable } from 'mobx-react';
 import { useRef } from 'react';
 import { useEffect } from 'react';
-import { useState } from 'react';
 
 
 interface filterProps {
@@ -242,12 +230,14 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
         return nonNumbers / facetValues.length > 0.1 ? facetValues.sort() : facetValues.sort((n1: string, n2: string) => Number(n1) - Number(n2));
     };
 
-    addHotkey(hotKey: string) {
+    /**
+     * Allows users to add a filter hotkey to the properties panel. Will also update the multitoggle at the top menu and the 
+     * icontags tht are displayed on the documents themselves
+     * @param hotKey tite of the new hotkey
+     */
+    addHotkey = (hotKey: string) => {
         const buttons = DocCast(Doc.UserDoc().myContextMenuBtns);
         const filter = DocCast(buttons.Filter);
-        const filter2 = DocCast(filter);
-        const but2 = Doc.UserDoc().myContextMenuBtns;
-
 
         const newKey: Button = {
             title: hotKey,
@@ -260,15 +250,11 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
             scripts: { onClick: '{ return handleTags(this.toolType, _readOnly_);}' },
         };
 
-        //         const heyy = [...hi, newKey]
-
         const currHotKeys = StrListCast(Doc.UserDoc().myFilterHotKeyTitles)
 
         Doc.UserDoc().myFilterHotKeyTitles = new List<string>(currHotKeys.concat(hotKey))
 
         Doc.UserDoc()[hotKey] = 'bolt'
-        // Doc.UserDoc()['supppp'] = 'star'
-
 
         const newBtn = CurrentUserUtils.setupContextMenuBtn(newKey, filter);
         newBtn.isSystem = newBtn[DocData].isSystem = undefined;
@@ -276,32 +262,16 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
         const subDocs = DocListCast(filter.data)
         const opts = subDocs[subDocs.length-1]
         Doc.AddDocToList(filter, 'data', newBtn, opts, true);
-
-        
-
-        // console.log(filter[DocData].data + 'ok')
-        // // console.log(filter[DocData][0] + 'help')
-        // console.log(filter[DocData] + 'good grief')
-
-        // console.log(DocCast(DocCast(filter.data))[0])
-        // this.removeHotKey()
-
-        // console.log(DocCast(filter.data) + 'HI')
-        // console.log(DocListCast(filter.data) + 'WOOOOO')
-        // console.log(DocCast(filter.data)[0] + 'hm :(')
-
     }
 
     
-
-    hotKeyButtons() {
+    /**
+     * Renders the newly formed hotkey icon buttons
+     * @returns the buttons to be rendered
+     */
+    hotKeyButtons = () => {
         const selected = DocumentView.SelectedDocs().lastElement();
-
-        // console.log(StrListCast(Doc.UserDoc().myFilterHotKeyTitles) + "hiii")
-
         const hotKeys = StrListCast(Doc.UserDoc().myFilterHotKeyTitles);
-
-        // hotKeys.forEach(l => console.log(l + "render"))
     
         // Selecting a button should make it so that the icon on the top filter panel becomes said icon
         const buttons = hotKeys.map((hotKey, i) => (
@@ -506,6 +476,10 @@ interface HotKeyButtonProps {
     selected?: Doc
 }
 
+/**
+ * Renders the buttons that correspond to each icon tag in the properties view. Allows users to change the icon, 
+ * title, and delete. 
+ */
 const HotKeyIconButton: React.FC<HotKeyButtonProps> = observer(({ hotKey, selected}) => {
     const state = useLocalObservable(() => ({
         isActive: false,
@@ -534,36 +508,34 @@ const HotKeyIconButton: React.FC<HotKeyButtonProps> = observer(({ hotKey, select
 
     const handleClick = () => {
         state.toggleActive();
-        // console.log(state.isActive + "hmmm")
     };
 
     const hotKeys = StrListCast(Doc.UserDoc().myFilterHotKeyTitles)
+    const buttons = DocCast(Doc.UserDoc().myContextMenuBtns);
+    const filter = DocCast(buttons.Filter);
 
+    /**
+     * The doc of the button in the context menu that corresponds to the current hotkey
+     * @returns 
+     */
     const myHotKeyDoc = () => {
-        const buttons = DocCast(Doc.UserDoc().myContextMenuBtns);
-        const filter = DocCast(buttons.Filter);
         const hotKeyDocs = DocListCast(filter.data)
         return hotKeyDocs.filter(k => StrCast(k.title) === hotKey)[0]
 
     }
 
+    /**
+     * Removes a hotkey from list 
+     */
     const removeHotKey = () => {
-        const buttons = DocCast(Doc.UserDoc().myContextMenuBtns);
-        const filter = DocCast(buttons.Filter);
-
         Doc.RemoveDocFromList(filter, 'data', myHotKeyDoc());
 
-
-
-
-
-        // console.log((DocListCast(filter.data)[0].title) + "emmanuel")
-
-
-        // console.log(DocCast(filter.data) + 'HI')
-        // console.log(DocCast(filter.data)[0] + 'hm :(')
-
     }
+
+    /**
+     * Deselects if the user clicks outside the button
+     * @param event 
+     */
     const handleClickOutside = (event: MouseEvent) => {
         if (panelRef.current && !panelRef.current.contains(event.target as Node)) {
             state.deactivate();
@@ -571,23 +543,22 @@ const HotKeyIconButton: React.FC<HotKeyButtonProps> = observer(({ hotKey, select
                 state.stopEditing();
 
                 updateFromInput()
-
-
-
-                // Doc.UserDoc().myFilterHotKeyTitles = new List<string>(hotKeys.map(k => k === hotKey ? state.myHotKey : k));
-                // Doc.UserDoc()[state.myHotKey] = StrCast(Doc.UserDoc()[hotKey])
-
             }
         }
     };
 
-    const updateFromInput = () => {
-        const hi = myHotKeyDoc()
+    /**
+     * Updates the list of hotkeys based on the users input. replaces the old title with the new one and then assigns this new 
+     * hotkey with the current icon
+     */
+    const updateFromInput = undoable (() => {
+        const myDoc = myHotKeyDoc()
         Doc.UserDoc().myFilterHotKeyTitles = new List<string>(hotKeys.map(k => k === hotKey ? state.myHotKey : k));
         Doc.UserDoc()[state.myHotKey] = StrCast(Doc.UserDoc()[hotKey])
-        hi.title = state.myHotKey
-        hi.toolTip = `Click to toggle the ${state.myHotKey}'s group's visibility`     
-    }
+        Doc.UserDoc()[hotKey] = ''
+        myDoc.title = state.myHotKey
+        myDoc.toolTip = `Click to toggle the ${state.myHotKey}'s group's visibility`     
+    }, '')
 
 
     useEffect(() => {
@@ -599,31 +570,25 @@ const HotKeyIconButton: React.FC<HotKeyButtonProps> = observer(({ hotKey, select
 
     const iconOpts = ['star', 'heart', 'bolt', 'satellite', 'palette', 'robot', 'lightbulb', 'highlighter', 'book', 'chalkboard' ];
 
+    /**
+     * Panel of icons the user can choose from to represent their tag
+     */
     const iconPanel = iconOpts.map((icon, i) => (
-        <button key={i} onClick={(e: React.MouseEvent) => {
+        <button key={i} onClick={undoable((e: React.MouseEvent) => {
             e.stopPropagation;
             Doc.UserDoc()[hotKey] = icon;
-            const hi = myHotKeyDoc()
-            hi.icon = icon
-
-
-            
-            }} className='icon-panel-button'>
+            myHotKeyDoc().icon = icon 
+            }, '')} className='icon-panel-button'>
             <FontAwesomeIcon icon={icon as any} color = {SnappingManager.userColor}/>
         </button>
     ));
 
-    function isAttrFiltered(attr: string) {
-        if (selected && selected._childFilters !== undefined && selected.type === DocumentType.COL) {
-            return StrListCast(selected._childFilters).some(filter => filter.includes(attr));
-        } else {
-            return false;
-        }
-    }
-
+    /**
+     * Actually renders the buttons
+     */
 
     return (
-        <div className={`filterHotKey-button ${isAttrFiltered(hotKey) ? 'active' : ''}`} 
+        <div className={`filterHotKey-button`} 
             onClick={(e) => {
                 e.stopPropagation();
                 state.startEditing();
@@ -673,20 +638,12 @@ const HotKeyIconButton: React.FC<HotKeyButtonProps> = observer(({ hotKey, select
             )}
             <button className='hotKey-close' onClick={(e: React.MouseEvent) => { 
                 e.stopPropagation(); 
-                const hi = StrListCast(Doc.UserDoc().myFilterHotKeyTitles)
-                // hi.forEach((str) => {
-                //     console.log(str + "before");
-                // });
                 Doc.UserDoc().myFilterHotKeyTitles = new List<string>(hotKeys.filter(k => k !== hotKey)); 
                 removeHotKey()
 
-                // hi.forEach((str) => {
-                //     console.log(str + "after");
-                // });
-
             }}>
                 <FontAwesomeIcon icon={'x' as any} color={SnappingManager.userColor}/>
             </button>
         </div>
     );
-})
+})
\ No newline at end of file
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index daa8e1720..e940ba6f9 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -54,7 +54,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
     private _widthUndo?: UndoManager.Batch;
 
     // eslint-disable-next-line no-use-before-define
-    public static Instance: PropertiesView | undefined;
+    public static Instance: PropertiesView;
     constructor(props: PropertiesViewProps) {
         super(props);
         makeObservable(this);
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 12aaf0b39..cda39b474 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -26,6 +26,7 @@ import { FieldViewProps } from './nodes/FieldView';
 import { StyleProp } from './StyleProp';
 import './StyleProvider.scss';
 import { IconTagBox } from './nodes/IconTagBox';
+import { DocData } from '../../fields/DocSymbols';
 
 function toggleLockedPosition(doc: Doc) {
     UndoManager.RunInBatch(() => Doc.toggleLockedPosition(doc), 'toggleBackground');
@@ -368,6 +369,11 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
             };
             const tags = () => props?.DocumentView?.() && CollectionFreeFormDocumentView.from(props.DocumentView()) ? <TagsView View={props.DocumentView()}/> :  null;
 
+            const iconTags = () => {
+                if (doc && doc![DocData].showIconTags)
+                    {return (<IconTagBox doc= {doc}></IconTagBox>)}
+            }
+
             return (
                 <>
                     {paint()}
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index 2390d162c..3d9f28a49 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -23,6 +23,8 @@ import { dropActionType } from '../../util/DropActionTypes';
 import { DocCast } from '../../../fields/Types';
 import { SelectionManager } from '../../util/SelectionManager';
 
+
+
 enum cardSortings {
     Time = 'time',
     Type = 'type',
@@ -32,6 +34,14 @@ enum cardSortings {
     Tag = 'tag',
     None = '',
 }
+
+/**
+ * New view type specifically for studying more dynamically. Allows you to reorder docs however you see fit, easily 
+ * sort and filter using presets, and customize your experience with chat gpt.
+ * 
+ * This file contains code as to how the docs are to be rendered (there place geographically and also in regards to sorting),
+ * and callback functions for the gpt popup
+ */
 @observer
 export class CollectionCardView extends CollectionSubView() {
     private _dropDisposer?: DragManager.DragDropDisposer;
@@ -40,10 +50,8 @@ export class CollectionCardView extends CollectionSubView() {
     private _textToDoc = new Map<string, Doc>();
 
     @observable _forceChildXf = false;
-    // @observable _isLoading = false;
     @observable _hoveredNodeIndex = -1;
     @observable _docRefs = new ObservableMap<Doc, DocumentView>();
-    _draggerRef = React.createRef<HTMLDivElement>();
     @observable _maxRowCount = 10;
     @observable _docDraggedIndex: number = -1;
     @observable _isACardBeingDragged: boolean = false;
@@ -99,12 +107,12 @@ export class CollectionCardView extends CollectionSubView() {
 
         // Reaction to cardSort changes
         this._disposers.sort = reaction(
-            () => this.cardSort,
-            cardSort => {
-                if (cardSort === cardSortings.Chat) {
+            () => GPTPopup.Instance.visible,
+            isVis => {
+                if (isVis) {
                     this.openChatPopup();
                 } else {
-                    GPTPopup.Instance.setVisible(false);
+                    this.Document.cardSort = (this.cardSort === cardSortings.Chat) ? '' : this.Document.cardSort;
                 }
             }
         );
@@ -296,7 +304,7 @@ export class CollectionCardView extends CollectionSubView() {
      * @param de drop event
      * @returns true if a card has been dropped, falls if not
      */
-    onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
+    onInternalDrop = undoable((e: Event, de: DragManager.DropEvent) => {
         if (de.complete.docDragData) {
             this._isACardBeingDragged = false;
             this._docDraggedIndex = -1;
@@ -304,7 +312,7 @@ export class CollectionCardView extends CollectionSubView() {
             return true;
         }
         return false;
-    };
+    }, '' );
 
     get sortedDocs() {
         return this.sort(this.childDocsWithoutLinks, this.cardSort, BoolCast(this.Document.cardSort_isDesc), this._docDraggedIndex);
@@ -511,8 +519,6 @@ export class CollectionCardView extends CollectionSubView() {
             image[DocData].description = response.trim();
             return response; // Return the response from gptImageLabel
         } catch (error) {
-            console.log('bad things have happened');
-
             console.log(error);
         }
         return '';
@@ -523,18 +529,22 @@ export class CollectionCardView extends CollectionSubView() {
      * usable code
      * @param gptOutput
      */
-    @action processGptOutput = (gptOutput: string, questionType: string, tag?: string) => {
-        console.log('HIIII');
-        console.log(StrCast(this.Document.cardSort) + 'cardSort');
+    @action 
+    
+    processGptOutput = undoable((gptOutput: string, questionType: string, tag?: string) => {
         // Split the string into individual list items
         const listItems = gptOutput.split('======').filter(item => item.trim() !== '');
 
-        if (questionType == '2' || questionType == '4') {
+        if (questionType === '2' || questionType === '4') {
             this.childDocs.forEach(d => {
                 d['chatFilter'] = false;
             });
         }
 
+        if (questionType ==='6'){
+            this.Document.cardSort = 'chat'
+        }
+
 
         listItems.forEach((item, index) => {
             const normalizedItem = item.trim();
@@ -545,7 +555,6 @@ export class CollectionCardView extends CollectionSubView() {
                 switch (questionType) {
                     case '6':
                         doc.chatIndex = index;
-                        console.log(index);
                         break;
                     case '1':
                         const allHotKeys = StrListCast(Doc.UserDoc().myFilterHotKeyTitles);
@@ -578,7 +587,7 @@ export class CollectionCardView extends CollectionSubView() {
                 console.warn(`No matching document found for item: ${normalizedItem}`);
             }
         });
-    };
+    }, '');
 
 
     /**
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index 8b9f128e0..9e56d313e 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -198,44 +198,6 @@ ScriptingGlobals.add(function showFreeform(attr: 'center' | 'grid' | 'snaplines'
             checkResult: (doc: Doc) => StrCast(doc?.cardSort) === "tag",
             setDoc: (doc: Doc, dv: DocumentView) => doc.cardSort === "tag" ? doc.cardSort = '' : doc.cardSort = 'tag',
         }],
-        // ['heart', {
-        //     checkResult: (doc: Doc) => isAttrFiltered('heart'),
-        //     setDoc: (doc: Doc, dv: DocumentView) => {
-        //         isAttrFiltered('heart') ? Doc.setDocFilter(doc, 'heart', true, 'remove') : Doc.setDocFilter(doc, 'heart', true, 'match');
-
-        //     }
-        // }],
-        // ['star', {
-        //     checkResult: (doc: Doc) => isAttrFiltered('star'),
-
-        //     setDoc: (doc: Doc, dv: DocumentView) => {
-        //         isAttrFiltered('star') ? Doc.setDocFilter(doc, 'star', true, 'remove') : Doc.setDocFilter(doc, 'star', true, 'match');
-        //     }
-        // }],
-        // ['bolt', {
-        //     checkResult: (doc: Doc) => isAttrFiltered('bolt'),
-        //     setDoc: (doc: Doc, dv: DocumentView) => {
-        //         isAttrFiltered('bolt') ? Doc.setDocFilter(doc, 'bolt', true, 'remove') : Doc.setDocFilter(doc, 'bolt', true, 'match');
-
-        //     }
-        // }],
-        // ['cloud', {
-        //     checkResult: (doc: Doc) => isAttrFiltered('cloud'),
-        //     setDoc: (doc: Doc, dv: DocumentView) => {
-        //         isAttrFiltered('cloud') ? Doc.setDocFilter(doc, 'cloud', true, 'remove') : Doc.setDocFilter(doc, 'cloud', true, 'match');
-
-        //     }
-        // }],
-        // ['chat', {
-        //     checkResult: (doc: Doc) => {
-                
-        //         if (StrCast(doc?.cardSort) === "chat"){
-        //             return true
-        //         }} ,
-        //     setDoc: (doc: Doc, dv: DocumentView) => {
-        //         doc.cardSort === "chat" ? doc.cardSort = '' : doc.cardSort = 'chat';
-        //     },
-        // }],
         ['up', {
             checkResult: (doc: Doc) => BoolCast(!doc?.cardSort_isDesc),
             setDoc: (doc: Doc, dv: DocumentView) => {
@@ -251,10 +213,17 @@ ScriptingGlobals.add(function showFreeform(attr: 'center' | 'grid' | 'snaplines'
         ['toggle-chat', {
             checkResult: (doc: Doc) => GPTPopup.Instance.visible,
             setDoc: (doc: Doc, dv: DocumentView) => {
-                GPTPopup.Instance.setVisible(!GPTPopup.Instance.visible);
-                GPTPopup.Instance.setMode(GPTPopupMode.SORT);
-                doc.cardSort === "chat" ? doc.cardSort = '' : doc.cardSort = 'chat';
+                if (GPTPopup.Instance.visible){
+                    doc.cardSort = ''
+                    GPTPopup.Instance.setVisible(false);
+   
+                } else {
+                    GPTPopup.Instance.setVisible(true);
+                    GPTPopup.Instance.setMode(GPTPopupMode.CARD);
+                    GPTPopup.Instance.setCardsDoneLoading(true);
 
+                }
+                
 
             },
         }],
@@ -293,9 +262,6 @@ ScriptingGlobals.add(function showFreeform(attr: 'center' | 'grid' | 'snaplines'
     if (checkResult) {
         return map.get(attr)?.checkResult(selected);
     }
-    const filters = StrListCast(selected._childFilters).concat(StrListCast(selected?._childFiltersByRanges).filter((filter, i) => !(i % 3)));
-
-    // console.log(filters.some(filter => filter.includes('star'))+ "SUOOOOPPP")
 
     const batch = map.get(attr)?.waitForRender ? UndoManager.StartBatch('set freeform attribute') : { end: () => {} };
     DocumentView.Selected().map(dv => map.get(attr)?.setDoc(dv.layoutDoc, dv));
@@ -303,7 +269,9 @@ ScriptingGlobals.add(function showFreeform(attr: 'center' | 'grid' | 'snaplines'
     return undefined;
 });
 
-
+/**
+ * Applies a filter to the selected document (or, if the settins button is pressed, opens the filter panel)
+ */
 ScriptingGlobals.add(function handleTags(value?: any, checkResult?: boolean) {
     const selected = DocumentView.SelectedDocs().lastElement();
 
@@ -312,7 +280,7 @@ ScriptingGlobals.add(function handleTags(value?: any, checkResult?: boolean) {
     }
 
         if (checkResult) {
-            return value=== 'opts' ? PropertiesView.Instance.openFilters : isAttrFiltered(value)
+            return value=== 'opts' ? PropertiesView.Instance?.openFilters : isAttrFiltered(value)
         }
 
         if (value != 'opts'){
@@ -321,8 +289,7 @@ ScriptingGlobals.add(function handleTags(value?: any, checkResult?: boolean) {
         else {
             SnappingManager.PropertiesWidth < 5 && SnappingManager.SetPropertiesWidth(0);
             SnappingManager.SetPropertiesWidth(MainView.Instance.propertiesWidth() < 15 ? Math.min(MainView.Instance._dashUIWidth - 50, 250) : 0);
-
-            PropertiesView.Instance.CloseAll()
+            PropertiesView.Instance?.CloseAll()
             PropertiesView.Instance.openFilters = true
         }
 
@@ -330,183 +297,6 @@ ScriptingGlobals.add(function handleTags(value?: any, checkResult?: boolean) {
         return undefined;
     }, '');
 
-
-
-// ScriptingGlobals.add(function cardHasLabel(label: string) {
-//     const selected = DocumentView.SelectedDocs().lastElement();
-//     const labelNum = Number(label) - 1;
-//     return labelNum < 4 || (selected && DocListCast(selected[Doc.LayoutFieldKey(selected)]).some(doc => doc[StrCast(selected.cardSort_customField)] == labelNum));
-// }, '');
-
-// ScriptingGlobals.add(function setCardSort(attr: "Time" | "Type"| "Color"| "ChatGPT"| "Custom 1"| "Custom 2"| "Custom 3", value?: any, checkResult?: boolean) {
-//     // const editorView = RichTextMenu.Instance?.TextView?.EditorView;
-//     const selected = DocumentView.SelectedDocs().lastElement();
-
-//     // prettier-ignore
-//     const map: Map<"Time" | "Type"| "Color"| "ChatGPT"| "Custom 1"| "Custom 2"| "Custom 3", { checkResult: (doc: Doc) => any; setDoc: (doc: Doc) => void;}> = new Map([
-//         ['Time', {
-
-//             checkResult: (doc: Doc) => {StrCast(doc?.cardSort);
-//             console.log(StrCast(doc?.cardSort + "card sort"))},
-//             setDoc: (doc: Doc) => {doc.cardSort = "time"
-//             console.log("hewwo")}
-
-//         ,
-//         }],
-//         ['Type', {
-//             checkResult: (doc: Doc) => StrCast(doc?.cardSort),
-//             setDoc: (doc: Doc) => doc.cardSort = "type",
-//         }],
-//         ['Color', {
-//             checkResult: (doc: Doc) => StrCast(doc?.cardSort),
-//             setDoc: (doc: Doc) => doc.cardSort = "color",
-//         }],
-//         // ['links', {
-//         //     checkResult: (doc: Doc) => StrCast(doc?.cardSort) === "links",
-//         //     setDoc: (doc: Doc) => doc.cardSort = "links",
-//         // }],
-//         ['Custom 1', {
-//             checkResult: (doc: Doc) => StrCast(doc?.cardSort) + " 1",
-//             setDoc: (doc: Doc) => {
-//                 doc.cardSort = "custom";
-//                 doc.cardSort_customField = "like";
-//                 doc.cardSort_visibleSortGroups = new List<number>();
-//             }
-//         }],
-//         ['Custom 2', {
-//             checkResult: (doc: Doc) => StrCast(doc?.cardSort) + " 2",
-//             setDoc: (doc: Doc) => {
-//                 doc.cardSort = "custom";
-//                 doc.cardSort_customField = "star";
-//                 doc.cardSort_visibleSortGroups = new List<number>();
-//             }
-//         }],
-//         ['Custom 3', {
-//             checkResult: (doc: Doc) => StrCast(doc?.cardSort) + " 3",
-//             setDoc: (doc: Doc) => {
-//                 doc.cardSort = "custom";
-//                 doc.cardSort_customField = "idea";
-//                 doc.cardSort_visibleSortGroups = new List<number>();
-//             }
-//         }],
-//         ['ChatGPT', {
-//             checkResult: (doc: Doc) => StrCast(doc?.cardSort_customField),
-//             setDoc: (doc: Doc) => {
-//                 doc.cardSort = "custom";
-//                 doc.cardSort_customField = "chat";
-//                 doc.cardSort_visibleSortGroups = new List<number>();
-//             },
-//         }],
-//     ]);
-
-//     for (let i = 0; i < 8; i++) {
-//         map.set((i + 1 + '') as any, {
-//             checkResult: (doc: Doc) => NumListCast(doc?.cardSort_visibleSortGroups).includes(i),
-//             setDoc: (doc: Doc) => {
-//                 const list = NumListCast(doc.cardSort_visibleSortGroups);
-//                 doc.cardSort_visibleSortGroups = new List<number>(list.includes(i) ? list.filter(d => d !== i) : [...list, i]);
-//             },
-//         });
-//     }
-
-//     if (checkResult) {
-//         console.log(attr + "attricute")
-//         console.log(map.get(attr)?.checkResult(selected) + "check result")
-//         return map.get(attr)?.checkResult(selected);
-//     }
-
-//     console.log(attr + "attricute lol")
-
-//     // const batch = map.get(attr)?.waitForRender ? UndoManager.StartBatch('set freeform attribute') : { end: () => {} };
-//     DocumentView.Selected().map(dv => map.get(attr)?.setDoc(dv.layoutDoc));
-//     // setTimeout(() => batch.end(), 100);
-//     return undefined;
-
-//     // map.get(attr)?.setDoc?.();
-//     // return undefined;
-// });
-
-// ScriptingGlobals.add(function setCardSort(value?: any, checkResult?: boolean) {
-//     // const editorView = RichTextMenu.Instance?.TextView?.EditorView;
-//     const selected = DocumentView.SelectedDocs().lastElement();
-//     if (checkResult) {
-//         // console.log(attr + "attricute")
-//         // console.log(map.get(attr)?.checkResult(selected) + "check result")
-//         console.log(StrCast(selected?.cardSort) + 'check');
-//         const hi = StrCast(selected?.cardSort);
-//         return StrCast(selected?.cardSortForDropDown) ?? 'Time';
-//     }
-//     function docFields(doc: Doc): void {
-//         switch (value) {
-//             case 'Custom 1':
-//                 doc.cardSort_customField = 'like';
-//                 break;
-//             case 'Custom 2':
-//                 doc.cardSort_customField = 'star';
-//                 break;
-//             case 'Custom 3':
-//                 doc.cardSort_customField = 'idea';
-//                 break;
-//             case 'Chat GPT':
-//                 doc.cardSort = 'custom';
-//                 doc.cardSort_customField = 'chat';
-//                 break;
-//             default:
-//                 break;
-//         }
-
-//         doc.cardSort_visibleSortGroups = new List<number>();
-//     }
-
-//     // const batch = map.get(attr)?.waitForRender ? UndoManager.StartBatch('set freeform attribute') : { end: () => {} };
-//     DocumentView.Selected().map(dv => {
-//         dv.Document.cardSortForDropDown = value;
-
-//         if (value != 'Chat GPT') {
-//             dv.Document.cardSort = value.trim().split(/\s+/)[0].toLowerCase();
-//         }
-//         docFields(dv.Document);
-//     });
-
-//     return undefined;
-
-//     // map.get(attr)?.setDoc?.();
-//     // return undefined;
-// });
-
-// ScriptingGlobals.add(function setCardSortAttr(attr: 'time' | 'docType' | 'color', value: any, checkResult?: boolean) {
-//     // const editorView = RichTextMenu.Instance?.TextView?.EditorView;
-//     const selected = SelectionManager.Docs.lastElement();
-//     // prettier-ignore
-//     const map: Map<'time' | 'docType' | 'color', { waitForRender?: boolean, checkResult: (doc:Doc) => any; setDoc: (doc:Doc, dv:DocumentView) => void;}> = new Map([
-//         ['time', {
-//             checkResult: (doc:Doc) => StrCast(doc?.cardSort),
-//             setDoc: (doc:Doc,dv:DocumentView) => doc.cardSort = "time",
-//         }],
-//         ['docType', {
-//             checkResult: (doc:Doc) => StrCast(doc?.cardSort),
-//             setDoc: (doc:Doc,dv:DocumentView) => doc.cardSort = "type",
-//         }],
-//         ['color', {
-//             checkResult: (doc:Doc) => StrCast(doc?.cardSort),
-//             setDoc: (doc:Doc,dv:DocumentView) => doc.cardSort = "color",
-//         }],
-//         // ['custom', {
-//         //     checkResult: () => RichTextMenu.Instance.textAlign,
-//         //     setDoc: () => value && editorView?.state ? RichTextMenu.Instance.align(editorView, editorView.dispatch, value):(Doc.UserDoc().textAlign = value),
-//         // }]
-//         // ,
-//     ]);
-
-//     if (checkResult) {
-//         return map.get(attr)?.checkResult(selected);
-//     }
-
-//     console.log('hey')
-//     SelectionManager.Views.map(dv => map.get(attr)?.setDoc(dv.layoutDoc, dv));
-//     console.log('success')
-// });
-
 // eslint-disable-next-line prefer-arrow-callback
 ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highlight' | 'fontSize' | 'alignment', value: string | number, checkResult?: boolean) {
     const editorView = RichTextMenu.Instance?.TextView?.EditorView;
diff --git a/src/client/views/nodes/IconTagBox.tsx b/src/client/views/nodes/IconTagBox.tsx
index 8aa6bff2b..370ce51d1 100644
--- a/src/client/views/nodes/IconTagBox.tsx
+++ b/src/client/views/nodes/IconTagBox.tsx
@@ -1,13 +1,9 @@
 import React from "react";
 import { observer } from "mobx-react";
 import { computed } from "mobx";
-
 import { ObservableReactComponent } from "../ObservableReactComponent";
 import { NumCast } from "../../../fields/Types";
-import { makeObservable } from "mobx";
 import { Doc } from "../../../fields/Doc";
-import { Reaction } from "mobx";
-import { reaction } from "mobx";
 import { numberRange } from "../../../Utils";
 import { Tooltip } from "@mui/material";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
@@ -15,16 +11,9 @@ import { undoable } from "../../util/UndoManager";
 import { BoolCast } from "../../../fields/Types";
 import { DocCast } from "../../../fields/Types";
 import './IconTagBox.scss';
-import { AclAdmin, AclAugment, AclEdit, DocData } from '../../../fields/DocSymbols';
+import {DocData } from '../../../fields/DocSymbols';
 import { StrListCast } from "../../../fields/Doc";
 import { StrCast } from "../../../fields/Types";
-import { DocListCast } from "../../../fields/Doc";
-import { List } from "../../../fields/List";
-import { action } from "mobx";
-import { DragManager } from "../../util/DragManager";
-import { setupMoveUpEvents } from "../../../ClientUtils";
-import { returnFalse } from "../../../ClientUtils";
-import { emptyFunction } from "../../../Utils";
 import { CollectionViewType } from "../../documents/DocumentTypes";
 import { SnappingManager } from "../../util/SnappingManager";
 import { MainView } from "../MainView";
@@ -33,95 +22,28 @@ import { PropertiesView } from "../PropertiesView";
 
 export interface IconTagProps {
     doc: Doc;
-
-
 }
 
+/**
+ * Renders the icon tags that rest under the document. The icons rendered are determined by the values of 
+ * each icon in the userdoc.
+ */
 @observer
 export class IconTagBox extends ObservableReactComponent<IconTagProps> {
-    private ref: React.RefObject<HTMLDivElement>;
-    private height: number = 0;
-
-
     @computed
     get currentScale() {
-        // console.log(NumCast((this._props.doc.embedContainer as Doc)?._freeform_scale, 1))
         return NumCast((this._props.doc.embedContainer as Doc)?._freeform_scale, 1);
         
     }
 
     constructor(props: any) {
         super(props);
-        makeObservable(this);
-        this.ref = React.createRef();
     }
 
-    // componentDidMount(): void {
-    //     this.height = this.ref.current?.getBoundingClientRect().height ? this.ref.current?.getBoundingClientRect().height : 0;
-    //     this._props.doc._keywordHeight = this.height;
-
-    //     reaction(
-    //         () => this.currentScale,
-    //         () => {
-    //             if (this.currentScale < 1) {
-    //                 this.height = this.ref.current?.getBoundingClientRect().height ? this.ref.current?.getBoundingClientRect().height : 0;
-    //                 this._props.doc._keywordHeight = this.height;
-    //             }
-    //         }
-    //     );
-    // }
-
     componentDidUpdate(prevProps: Readonly<IconTagProps>): void {
-        // this.height = this.ref.current?.getBoundingClientRect().height ? this.ref.current?.getBoundingClientRect().height : 0;
         this._props.doc[DocData].tagHeight = 36*this.currentScale;
     }
 
-    // createCollection = () => {
-    //     // Get the documents that contain the keyword.
-    //     const selected = DocListCast(this.getKeywordCollectionDocs()!);
-    //     const newEmbeddings = selected.map(doc => Doc.MakeEmbedding(doc));
-
-    //     // Create a new collection and set up configurations.
-    //     const newCollection = ((doc: Doc) => {
-    //         const docData = doc[DocData];
-    //         docData.data = new List<Doc>(newEmbeddings);
-    //         docData.title = this._props.keyword;
-    //         doc._freeform_panX = doc._freeform_panY = 0;
-    //         return doc;
-    //     })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
-    //     newEmbeddings.forEach(embed => (embed.embedContainer = newCollection));
-    //     newCollection._width = 900;
-    //     newCollection._height = 900;
-    //     newCollection.layout_fitWidth = true;
-
-    //     // Add the collection to the keyword document's list of associated smart collections.
-    //     this._props.keywordDoc.collections = new List<Doc>([...DocListCast(this._props.keywordDoc.collections), newCollection]);
-    //     newCollection[DocData].data_labels = new List<string>([this._props.keyword]);
-    //     newCollection[DocData][`${this._props.keyword}`] = true;
-    //     newCollection[DocData].showLabels = true;
-    //     return newCollection;
-    // };
-
-    // @action
-    // handleDragStart = (e: React.PointerEvent) => {
-    //     if (this._props.isEditing) {
-    //         const clone = this.ref.current?.cloneNode(true) as HTMLElement;
-    //         if (!clone) return;
-
-    //         setupMoveUpEvents(
-    //             this,
-    //             e,
-    //             () => {
-    //                 const dragData = new DragManager.DocumentDragData([this.createCollection()]);
-    //                 DragManager.StartDocumentDrag([this.ref.current!], dragData, e.clientX, e.clientY, {});
-    //                 return true;
-    //             },
-    //             returnFalse,
-    //             emptyFunction
-    //         );
-    //         e.preventDefault();
-    //     }
-    // };
 
     /**
      * Renders the buttons to customize sorting depending on which group the card belongs to and the amount of total groups
@@ -130,25 +52,10 @@ export class IconTagBox extends ObservableReactComponent<IconTagProps> {
      * @returns
      */
     renderButtons = (doc: Doc): JSX.Element | null => {
-        // if (cardSort !== cardSortings.Custom) return null;
-
         const amButtons = (StrListCast(Doc.UserDoc().myFilterHotKeyTitles).length) + 1
 
         const keys = StrListCast(Doc.UserDoc().myFilterHotKeyTitles)
-
-
-        // const amButtons = Math.max(
-        //     4,
-        //     this.childDocs?.reduce((set, d) => {
-        //         if (this.cardSort_customField) {
-        //             set.add(NumCast(d[this.cardSort_customField]));
-        //         }
-        //         return set;
-        //     }, new Set<number>()).size ?? 0
-        // );
-
-        // const activeButtonIndex = CollectionCardView.getButtonGroup(this.cardSort_customField, doc);
-
+        
         const totalWidth = (amButtons -1) * 35 + (amButtons -1) * 2 * 5 + 6;
 
         const iconMap =  (buttonID: number)  => {
@@ -179,16 +86,14 @@ export class IconTagBox extends ObservableReactComponent<IconTagProps> {
                         </button>
                     </Tooltip>
                 ))}
-
-                {/* <Tooltip title={<div className="dash-tooltip">Click to customize these icons</div>}>
-                        <button type="button" onClick={() => this.openHotKeyMenu() }>
-                            <FontAwesomeIcon icon='gear' style={{ color: '#17438a', height: '30px', width: '30px'}} />
-                        </button>
-                </Tooltip> */}
             </div>
         );
     };
 
+    /**
+     * Opens the filter panel in the properties menu
+     */
+
     openHotKeyMenu = () => {
         SnappingManager.PropertiesWidth < 5 && SnappingManager.SetPropertiesWidth(0);
         SnappingManager.SetPropertiesWidth(MainView.Instance.propertiesWidth() < 15 ? Math.min(MainView.Instance._dashUIWidth - 50, 250) : 0);
@@ -204,31 +109,19 @@ export class IconTagBox extends ObservableReactComponent<IconTagProps> {
      * @param doc
      */
       toggleButton = undoable((doc: Doc, icon: string) => {
-
-
-
-        // this.cardSort_customField && (doc[this.cardSort_customField] = buttonID);
-
-        // doc.cardSort_activeIcons = new List<string>()
-
-
-        // const list = StrListCast(doc.cardSort_activeIcons);
-        // doc.cardSort_activeIcons = new List<string>(list.includes(icon) ? list.filter(d => d !== icon) : [...list, icon]);
-
         BoolCast(doc[icon]) ? doc[icon] = false : doc[icon] = true
 
-
-
-        // StrListCast(doc.cardSort_activeIcons).push(iconMap[buttonID])
     }, 'toggle card tag');
 
 
+    /**
+     * Determines whether or not the given icon is active depending on the doc's data
+     * @param doc 
+     * @param icon 
+     * @returns 
+     */
     getButtonIcon = (doc: Doc, icon: any): JSX.Element => {
-
-        // const isActive = StrListCast(doc.cardSort_activeIcons).includes(icon)
         const isActive = doc[icon]
-
-        // console.log(StrListCast(doc.cardSort_activeIcons))
         const color = isActive ? '#4476f7' : '#323232';
 
         return <FontAwesomeIcon icon={icon} style={{ color , height: '30px', width: '30px'}} />;
@@ -244,8 +137,6 @@ export class IconTagBox extends ObservableReactComponent<IconTagProps> {
 
     }
 
-
-
 }
 
 
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index 002e82332..53bedbb65 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -279,7 +279,7 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
     }
 
     /**
-     * Generates a response to the user's questoin depending on the type of their question
+     * Generates a response to the user's question depending on the type of their question
      */
     generateCard = async () => {
         console.log(this.chatSortPrompt + "USER PROMPT")
@@ -798,7 +798,8 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
                         color={StrCast(SettingsManager.userVariantColor)}
                         tooltip="close"
                         icon={<CgClose size="16px" />}
-                        onClick={() => this.setVisible(false)}
+                        onClick={() => {
+                            this.setVisible(false)}}
                     />
                     
                 </>
diff --git a/src/typings/index.d.ts b/src/typings/index.d.ts
index bee79a38d..fea88cdcb 100644
--- a/src/typings/index.d.ts
+++ b/src/typings/index.d.ts
@@ -12,7 +12,7 @@ declare module 'fit-curve';
 declare module 'iink-js';
 declare module 'pdfjs-dist/web/pdf_viewer';
 declare module 'react-jsx-parser';
-declare module 'type_decls.d';
+// declare module 'type_decls.d';
 
 declare module '@react-pdf/renderer' {
     import * as React from 'react';
-- 
cgit v1.2.3-70-g09d2


From 64635a0a0e07a107c4019e366feb2fe5b01c8b04 Mon Sep 17 00:00:00 2001
From: aidahosa1 <aisosa_idahosa@brown.edu>
Date: Tue, 17 Sep 2024 13:43:40 -0400
Subject: ready to pull

---
 package-lock.json                                  | 153 ++++++++++++---------
 src/client/documents/Documents.ts                  |   7 -
 src/client/views/DocumentButtonBar.tsx             |  54 +++++---
 src/client/views/DocumentDecorations.tsx           |   4 -
 src/client/views/StyleProvider.tsx                 |   3 +-
 .../views/collections/CollectionCardDeckView.scss  |  58 --------
 .../views/collections/CollectionCardDeckView.tsx   |  19 +--
 src/client/views/global/globalScripts.ts           |  12 +-
 src/client/views/nodes/IconTagBox.scss             |  20 +--
 src/client/views/pdf/GPTPopup/GPTPopup.scss        |  12 +-
 10 files changed, 145 insertions(+), 197 deletions(-)

(limited to 'src/client/views/StyleProvider.tsx')

diff --git a/package-lock.json b/package-lock.json
index fabc6d90a..d20aac0c5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -94,7 +94,7 @@
                 "D": "^1.0.0",
                 "d3": "^7.8.5",
                 "depcheck": "^1.4.7",
-                "dotenv": "^16.3.1",
+                "dotenv": "^16.4.5",
                 "eslint-webpack-plugin": "^4.1.0",
                 "exif": "^0.6.0",
                 "exifr": "^7.1.3",
@@ -133,6 +133,7 @@
                 "image-data-uri": "^2.0.1",
                 "image-size": "^1.0.2",
                 "image-size-stream": "^1.1.0",
+                "install": "^0.13.0",
                 "is-plain-obj": "^4.1.0",
                 "jimp": "^1.0.4",
                 "jpeg-autorotate": "^9.0.0",
@@ -157,7 +158,7 @@
                 "node-stream-zip": "^1.15.0",
                 "nodemailer": "^6.9.7",
                 "nodemon": "^3.0.2",
-                "npm": "^10.2.5",
+                "npm": "^10.8.1",
                 "openai": "^4.26.0",
                 "p-limit": "^6.1.0",
                 "passport": "^0.7.0",
@@ -191,6 +192,7 @@
                 "react-compound-slider": "^3.4.0",
                 "react-datepicker": "^7.3.0",
                 "react-dom": "^18.2.0",
+                "react-draggable": "^4.4.6",
                 "react-grid-layout": "^1.4.4",
                 "react-icons": "^5.0.1",
                 "react-jsx-parser": "^2.0.0",
@@ -249,7 +251,7 @@
                 "xregexp": "^5.1.1"
             },
             "devDependencies": {
-                "@eslint/js": "^9.1.1",
+                "@eslint/js": "^9.10.0",
                 "@types/adm-zip": "^0.5.5",
                 "@types/animejs": "^3.1.12",
                 "@types/archiver": "^6.0.2",
@@ -298,9 +300,9 @@
                 "@types/youtube": "^0.1.0",
                 "chai": "^5.0.0",
                 "cross-env": "^7.0.3",
-                "eslint": "^9.9.0",
-                "eslint-plugin-react": "^7.34.1",
-                "globals": "^15.1.0",
+                "eslint": "^9.10.0",
+                "eslint-plugin-react": "^7.36.1",
+                "globals": "^15.9.0",
                 "jsdom": "^25.0.0",
                 "mocha": "^10.2.0",
                 "prettier": "^3.1.0",
@@ -309,7 +311,7 @@
                 "ts-loader": "^9.5.1",
                 "ts-node": "^10.9.1",
                 "ts-node-dev": "^2.0.0",
-                "typescript-eslint": "^8.2.0",
+                "typescript-eslint": "^8.6.0",
                 "webpack-dev-server": "^5.0.4"
             },
             "engines": {
@@ -2698,9 +2700,9 @@
             }
         },
         "node_modules/@eslint/js": {
-            "version": "9.9.1",
-            "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.1.tgz",
-            "integrity": "sha512-xIDQRsfg5hNBqHz04H1R3scSVwmI+KUbqjsQKHKQ1DAUSaUjYPReZZmS/5PNiKu1fUvzDd6H7DEDKACSEhu+TQ==",
+            "version": "9.10.0",
+            "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.10.0.tgz",
+            "integrity": "sha512-fuXtbiP5GWIn8Fz+LWoOMVf/Jxm+aajZYkhi6CuEm4SxymFM+eUWzbO9qXT+L0iCkL5+KGYMCSGxo686H19S1g==",
             "engines": {
                 "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
             }
@@ -2713,6 +2715,17 @@
                 "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
             }
         },
+        "node_modules/@eslint/plugin-kit": {
+            "version": "0.1.0",
+            "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.1.0.tgz",
+            "integrity": "sha512-autAXT203ixhqei9xt+qkYOvY8l6LAFIdT2UXc/RPNeUVfqRF1BV94GTJyVPFKT8nFM6MyVJhjLj9E8JWvf5zQ==",
+            "dependencies": {
+                "levn": "^0.4.1"
+            },
+            "engines": {
+                "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+            }
+        },
         "node_modules/@ffmpeg/core": {
             "version": "0.12.6",
             "resolved": "https://registry.npmjs.org/@ffmpeg/core/-/core-0.12.6.tgz",
@@ -10064,16 +10077,16 @@
             "dev": true
         },
         "node_modules/@typescript-eslint/eslint-plugin": {
-            "version": "8.4.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.4.0.tgz",
-            "integrity": "sha512-rg8LGdv7ri3oAlenMACk9e+AR4wUV0yrrG+XKsGKOK0EVgeEDqurkXMPILG2836fW4ibokTB5v4b6Z9+GYQDEw==",
+            "version": "8.6.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.6.0.tgz",
+            "integrity": "sha512-UOaz/wFowmoh2G6Mr9gw60B1mm0MzUtm6Ic8G2yM1Le6gyj5Loi/N+O5mocugRGY+8OeeKmkMmbxNqUCq3B4Sg==",
             "dev": true,
             "dependencies": {
                 "@eslint-community/regexpp": "^4.10.0",
-                "@typescript-eslint/scope-manager": "8.4.0",
-                "@typescript-eslint/type-utils": "8.4.0",
-                "@typescript-eslint/utils": "8.4.0",
-                "@typescript-eslint/visitor-keys": "8.4.0",
+                "@typescript-eslint/scope-manager": "8.6.0",
+                "@typescript-eslint/type-utils": "8.6.0",
+                "@typescript-eslint/utils": "8.6.0",
+                "@typescript-eslint/visitor-keys": "8.6.0",
                 "graphemer": "^1.4.0",
                 "ignore": "^5.3.1",
                 "natural-compare": "^1.4.0",
@@ -10097,15 +10110,15 @@
             }
         },
         "node_modules/@typescript-eslint/parser": {
-            "version": "8.4.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.4.0.tgz",
-            "integrity": "sha512-NHgWmKSgJk5K9N16GIhQ4jSobBoJwrmURaLErad0qlLjrpP5bECYg+wxVTGlGZmJbU03jj/dfnb6V9bw+5icsA==",
+            "version": "8.6.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.6.0.tgz",
+            "integrity": "sha512-eQcbCuA2Vmw45iGfcyG4y6rS7BhWfz9MQuk409WD47qMM+bKCGQWXxvoOs1DUp+T7UBMTtRTVT+kXr7Sh4O9Ow==",
             "dev": true,
             "dependencies": {
-                "@typescript-eslint/scope-manager": "8.4.0",
-                "@typescript-eslint/types": "8.4.0",
-                "@typescript-eslint/typescript-estree": "8.4.0",
-                "@typescript-eslint/visitor-keys": "8.4.0",
+                "@typescript-eslint/scope-manager": "8.6.0",
+                "@typescript-eslint/types": "8.6.0",
+                "@typescript-eslint/typescript-estree": "8.6.0",
+                "@typescript-eslint/visitor-keys": "8.6.0",
                 "debug": "^4.3.4"
             },
             "engines": {
@@ -10125,13 +10138,13 @@
             }
         },
         "node_modules/@typescript-eslint/scope-manager": {
-            "version": "8.4.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.4.0.tgz",
-            "integrity": "sha512-n2jFxLeY0JmKfUqy3P70rs6vdoPjHK8P/w+zJcV3fk0b0BwRXC/zxRTEnAsgYT7MwdQDt/ZEbtdzdVC+hcpF0A==",
+            "version": "8.6.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.6.0.tgz",
+            "integrity": "sha512-ZuoutoS5y9UOxKvpc/GkvF4cuEmpokda4wRg64JEia27wX+PysIE9q+lzDtlHHgblwUWwo5/Qn+/WyTUvDwBHw==",
             "dev": true,
             "dependencies": {
-                "@typescript-eslint/types": "8.4.0",
-                "@typescript-eslint/visitor-keys": "8.4.0"
+                "@typescript-eslint/types": "8.6.0",
+                "@typescript-eslint/visitor-keys": "8.6.0"
             },
             "engines": {
                 "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -10142,13 +10155,13 @@
             }
         },
         "node_modules/@typescript-eslint/type-utils": {
-            "version": "8.4.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.4.0.tgz",
-            "integrity": "sha512-pu2PAmNrl9KX6TtirVOrbLPLwDmASpZhK/XU7WvoKoCUkdtq9zF7qQ7gna0GBZFN0hci0vHaSusiL2WpsQk37A==",
+            "version": "8.6.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.6.0.tgz",
+            "integrity": "sha512-dtePl4gsuenXVwC7dVNlb4mGDcKjDT/Ropsk4za/ouMBPplCLyznIaR+W65mvCvsyS97dymoBRrioEXI7k0XIg==",
             "dev": true,
             "dependencies": {
-                "@typescript-eslint/typescript-estree": "8.4.0",
-                "@typescript-eslint/utils": "8.4.0",
+                "@typescript-eslint/typescript-estree": "8.6.0",
+                "@typescript-eslint/utils": "8.6.0",
                 "debug": "^4.3.4",
                 "ts-api-utils": "^1.3.0"
             },
@@ -10166,9 +10179,9 @@
             }
         },
         "node_modules/@typescript-eslint/types": {
-            "version": "8.4.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.4.0.tgz",
-            "integrity": "sha512-T1RB3KQdskh9t3v/qv7niK6P8yvn7ja1mS7QK7XfRVL6wtZ8/mFs/FHf4fKvTA0rKnqnYxl/uHFNbnEt0phgbw==",
+            "version": "8.6.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.6.0.tgz",
+            "integrity": "sha512-rojqFZGd4MQxw33SrOy09qIDS8WEldM8JWtKQLAjf/X5mGSeEFh5ixQlxssMNyPslVIk9yzWqXCsV2eFhYrYUw==",
             "dev": true,
             "engines": {
                 "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -10179,13 +10192,13 @@
             }
         },
         "node_modules/@typescript-eslint/typescript-estree": {
-            "version": "8.4.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.4.0.tgz",
-            "integrity": "sha512-kJ2OIP4dQw5gdI4uXsaxUZHRwWAGpREJ9Zq6D5L0BweyOrWsL6Sz0YcAZGWhvKnH7fm1J5YFE1JrQL0c9dd53A==",
+            "version": "8.6.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.6.0.tgz",
+            "integrity": "sha512-MOVAzsKJIPIlLK239l5s06YXjNqpKTVhBVDnqUumQJja5+Y94V3+4VUFRA0G60y2jNnTVwRCkhyGQpavfsbq/g==",
             "dev": true,
             "dependencies": {
-                "@typescript-eslint/types": "8.4.0",
-                "@typescript-eslint/visitor-keys": "8.4.0",
+                "@typescript-eslint/types": "8.6.0",
+                "@typescript-eslint/visitor-keys": "8.6.0",
                 "debug": "^4.3.4",
                 "fast-glob": "^3.3.2",
                 "is-glob": "^4.0.3",
@@ -10234,15 +10247,15 @@
             }
         },
         "node_modules/@typescript-eslint/utils": {
-            "version": "8.4.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.4.0.tgz",
-            "integrity": "sha512-swULW8n1IKLjRAgciCkTCafyTHHfwVQFt8DovmaF69sKbOxTSFMmIZaSHjqO9i/RV0wIblaawhzvtva8Nmm7lQ==",
+            "version": "8.6.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.6.0.tgz",
+            "integrity": "sha512-eNp9cWnYf36NaOVjkEUznf6fEgVy1TWpE0o52e4wtojjBx7D1UV2WAWGzR+8Y5lVFtpMLPwNbC67T83DWSph4A==",
             "dev": true,
             "dependencies": {
                 "@eslint-community/eslint-utils": "^4.4.0",
-                "@typescript-eslint/scope-manager": "8.4.0",
-                "@typescript-eslint/types": "8.4.0",
-                "@typescript-eslint/typescript-estree": "8.4.0"
+                "@typescript-eslint/scope-manager": "8.6.0",
+                "@typescript-eslint/types": "8.6.0",
+                "@typescript-eslint/typescript-estree": "8.6.0"
             },
             "engines": {
                 "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -10256,12 +10269,12 @@
             }
         },
         "node_modules/@typescript-eslint/visitor-keys": {
-            "version": "8.4.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.4.0.tgz",
-            "integrity": "sha512-zTQD6WLNTre1hj5wp09nBIDiOc2U5r/qmzo7wxPn4ZgAjHql09EofqhF9WF+fZHzL5aCyaIpPcT2hyxl73kr9A==",
+            "version": "8.6.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.6.0.tgz",
+            "integrity": "sha512-wapVFfZg9H0qOYh4grNVQiMklJGluQrOUiOhYRrQWhx7BY/+I1IYb8BczWNbbUpO+pqy0rDciv3lQH5E1bCLrg==",
             "dev": true,
             "dependencies": {
-                "@typescript-eslint/types": "8.4.0",
+                "@typescript-eslint/types": "8.6.0",
                 "eslint-visitor-keys": "^3.4.3"
             },
             "engines": {
@@ -17701,15 +17714,16 @@
             }
         },
         "node_modules/eslint": {
-            "version": "9.9.1",
-            "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.9.1.tgz",
-            "integrity": "sha512-dHvhrbfr4xFQ9/dq+jcVneZMyRYLjggWjk6RVsIiHsP8Rz6yZ8LvZ//iU4TrZF+SXWG+JkNF2OyiZRvzgRDqMg==",
+            "version": "9.10.0",
+            "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.10.0.tgz",
+            "integrity": "sha512-Y4D0IgtBZfOcOUAIQTSXBKoNGfY0REGqHJG6+Q81vNippW5YlKjHFj4soMxamKK1NXHUWuBZTLdU3Km+L/pcHw==",
             "dependencies": {
                 "@eslint-community/eslint-utils": "^4.2.0",
                 "@eslint-community/regexpp": "^4.11.0",
                 "@eslint/config-array": "^0.18.0",
                 "@eslint/eslintrc": "^3.1.0",
-                "@eslint/js": "9.9.1",
+                "@eslint/js": "9.10.0",
+                "@eslint/plugin-kit": "^0.1.0",
                 "@humanwhocodes/module-importer": "^1.0.1",
                 "@humanwhocodes/retry": "^0.3.0",
                 "@nodelib/fs.walk": "^1.2.8",
@@ -17732,7 +17746,6 @@
                 "is-glob": "^4.0.0",
                 "is-path-inside": "^3.0.3",
                 "json-stable-stringify-without-jsonify": "^1.0.1",
-                "levn": "^0.4.1",
                 "lodash.merge": "^4.6.2",
                 "minimatch": "^3.1.2",
                 "natural-compare": "^1.4.0",
@@ -17759,9 +17772,9 @@
             }
         },
         "node_modules/eslint-plugin-react": {
-            "version": "7.35.2",
-            "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.35.2.tgz",
-            "integrity": "sha512-Rbj2R9zwP2GYNcIak4xoAMV57hrBh3hTaR0k7hVjwCQgryE/pw5px4b13EYjduOI0hfXyZhwBxaGpOTbWSGzKQ==",
+            "version": "7.36.1",
+            "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.36.1.tgz",
+            "integrity": "sha512-/qwbqNXZoq+VP30s1d4Nc1C5GTxjJQjk4Jzs4Wq2qzxFM7dSmuG2UkIjg2USMLh3A/aVcUNrK7v0J5U1XEGGwA==",
             "dev": true,
             "dependencies": {
                 "array-includes": "^3.1.8",
@@ -20914,6 +20927,14 @@
                 "inspect-function": "^0.3.1"
             }
         },
+        "node_modules/install": {
+            "version": "0.13.0",
+            "resolved": "https://registry.npmjs.org/install/-/install-0.13.0.tgz",
+            "integrity": "sha512-zDml/jzr2PKU9I8J/xyZBQn8rPCAY//UOYNmR01XwNwyfhEWObo2SWfSl1+0tm1u6PhxLwDnfsT/6jB7OUxqFA==",
+            "engines": {
+                "node": ">= 0.10"
+            }
+        },
         "node_modules/internal-slot": {
             "version": "1.0.7",
             "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz",
@@ -32710,14 +32731,14 @@
             "integrity": "sha512-7sI4e/bZijOzyURng88oOFZCISQPTHozfE2sUu5AviFYk5QV7fYGb6YiDl+vKjF/pICA354JImBImL9XJWUvdQ=="
         },
         "node_modules/typescript-eslint": {
-            "version": "8.4.0",
-            "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.4.0.tgz",
-            "integrity": "sha512-67qoc3zQZe3CAkO0ua17+7aCLI0dU+sSQd1eKPGq06QE4rfQjstVXR6woHO5qQvGUa550NfGckT4tzh3b3c8Pw==",
+            "version": "8.6.0",
+            "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.6.0.tgz",
+            "integrity": "sha512-eEhhlxCEpCd4helh3AO1hk0UP2MvbRi9CtIAJTVPQjuSXOOO2jsEacNi4UdcJzZJbeuVg1gMhtZ8UYb+NFYPrA==",
             "dev": true,
             "dependencies": {
-                "@typescript-eslint/eslint-plugin": "8.4.0",
-                "@typescript-eslint/parser": "8.4.0",
-                "@typescript-eslint/utils": "8.4.0"
+                "@typescript-eslint/eslint-plugin": "8.6.0",
+                "@typescript-eslint/parser": "8.6.0",
+                "@typescript-eslint/utils": "8.6.0"
             },
             "engines": {
                 "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 623a5251e..aac15059e 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -488,14 +488,7 @@ export class DocumentOptions {
     userColor?: STRt = new StrInfo('color associated with a Dash user (seen in header fields of shared documents)');
 
     cardSort?: STRt = new StrInfo('way cards are sorted in deck view');
-    // cardSortForDropDown?: STRt = new StrInfo('needed for dropdown and i dont know why')
-    // cardSort_customField?: STRt = new StrInfo('field key used for sorting cards');
-    cardSort_activeIcons?: List<string>; //icons each card is tagges with
-    // cardSort_visibleSortGroups?: List<string>; // which sorting values are being filtered (shown)
-
-    // cardSort_visibleSortGroups?: List<number>; // which sorting values are being filtered (shown)
     cardSort_isDesc?: BOOLt = new BoolInfo('whether the cards are sorted ascending or descending');
-    // test?: STRt = new StrInfo('testing for filtering')
 }
 
 export const DocOptions = new DocumentOptions();
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index ea859a1e5..ccde1de80 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -9,7 +9,7 @@ import * as React from 'react';
 import { FaEdit } from 'react-icons/fa';
 import { returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick } from '../../ClientUtils';
 import { emptyFunction } from '../../Utils';
-import { Doc, DocListCast } from '../../fields/Doc';
+import { Doc } from '../../fields/Doc';
 import { Cast, DocCast } from '../../fields/Types';
 import { DocUtils, IsFollowLinkScript } from '../documents/DocUtils';
 import { CalendarManager } from '../util/CalendarManager';
@@ -263,6 +263,26 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
         );
     }
 
+    @computed
+    get calendarButton() {
+        const targetDoc = this.view0?.Document;
+        return !targetDoc ? null : (
+            <Tooltip title={<div className="dash-calendar-button">Open calendar menu</div>}>
+                <div
+                    className="documentButtonBar-icon"
+                    style={{ color: 'white' }}
+                    onClick={() => {
+                        CalendarManager.Instance.open(this.view0, targetDoc);
+                    }}>
+                    <FontAwesomeIcon className="documentdecorations-icon" icon={faCalendarDays as IconLookup} />
+                </div>
+            </Tooltip>
+        );
+    }
+
+    /**
+     * Allows for both the keywords and the icon tags to be shown using a quasi- multitoggle
+     */
     @computed
     get keywordButton() {
         const targetDoc = this.view0?.Document;
@@ -279,21 +299,10 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
                             size="sm"
                             icon={icon}
                             onClick={e => {
-                                // console.log('wtfff')
-                                // name === 'tags' ?? 
                                 if (name === 'tags'){
-                                    undoable(e => (targetDoc && (targetDoc[DocData].showIconTags = !targetDoc[DocData].showIconTags)), 'show icon tags')
+                                    (targetDoc && (targetDoc[DocData].showIconTags = !targetDoc[DocData].showIconTags))
                                 } else {
-                                    undoable(e => {
-                                        const showing = DocumentView.Selected().some(dv => dv.layoutDoc._layout_showTags);
-                                        DocumentView.Selected().forEach(dv => {
-                                            dv.layoutDoc._layout_showTags = !showing;
-                                            if (e.shiftKey)
-                                                DocListCast(dv.Document[Doc.LayoutFieldKey(dv.Document) + '_annotations']).forEach(doc => {
-                                                    if (doc.face) doc.hidden = showing;
-                                                });
-                                        });
-                                    }, 'show Doc tags')
+                                    (targetDoc && (targetDoc[DocData].showLabels = !targetDoc[DocData].showLabels))
                                 }
 
 
@@ -309,15 +318,23 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
 
 
 
-        return !DocumentView.Selected().length ? null : (
+        return !targetDoc ? null : (
             <div className='documentButtonBar-icon'>
             <div className="documentButtonBar-pinTypes" style = {{width: '40px'}}>
                 {metaBtn('tags', 'star')}
                 {metaBtn("keywords", 'id-card')}
              </div>
 
-            <Tooltip title={<div className="dash-keyword-button">Open keyword / icon tag menu</div>}>
-                <div className="documentButtonBar-icon" style={{ color: 'white' }}>
+            <Tooltip title={<div className="dash-keyword-button">Open keyword menu</div>}>
+                <div
+                    className="documentButtonBar-icon"
+                    style={{ color: 'white' }}
+                    onClick={() => {
+                        // targetDoc[DocData].showIconTags = !targetDoc[DocData].showIconTags;
+                    }}
+                    >
+
+                   
                     <FontAwesomeIcon className="documentdecorations-icon" icon="tag" />
                 </div>
             </Tooltip>
@@ -495,10 +512,11 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
                 {!DocumentView.Selected().some(v => v.allLinks.length) ? null : <div className="documentButtonBar-button">{this.followLinkButton}</div>}
                 <div className="documentButtonBar-button">{this.pinButton}</div>
                 <div className="documentButtonBar-button">{this.recordButton}</div>
+                <div className="documentButtonBar-button">{this.calendarButton}</div>
                 <div className="documentButtonBar-button">{this.keywordButton}</div>
                 {!Doc.UserDoc().documentLinksButton_fullMenu ? null : <div className="documentButtonBar-button">{this.shareButton}</div>}
                 <div className="documentButtonBar-button">{this.menuButton}</div>
             </div>
         );
     }
-}
+}
\ No newline at end of file
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 45ce681aa..37b1f04e7 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -773,10 +773,6 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
         const freeformDoc = DocumentView.Selected().some(v => CollectionFreeFormDocumentView.from(v));
       
 
-        const keyWordTrans = doc[DocData].showLabels ? NumCast(doc[DocData].keywordHeight) : 0
-        const tagTrans = doc[DocData].showIconTags ? NumCast(doc[DocData].tagHeight) : 0
-
-
         return (
             <div className="documentDecorations" style={{ display: this._showNothing && !freeformDoc ? 'none' : undefined }}>
                 <div
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index cda39b474..54698ab2d 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -367,13 +367,12 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
                     </Tooltip>
                 );
             };
-            const tags = () => props?.DocumentView?.() && CollectionFreeFormDocumentView.from(props.DocumentView()) ? <TagsView View={props.DocumentView()}/> :  null;
+            const tags = () => props?.DocumentView?.() ? <TagsView Views={[props.DocumentView()]}/> :  null;
 
             const iconTags = () => {
                 if (doc && doc![DocData].showIconTags)
                     {return (<IconTagBox doc= {doc}></IconTagBox>)}
             }
-
             return (
                 <>
                     {paint()}
diff --git a/src/client/views/collections/CollectionCardDeckView.scss b/src/client/views/collections/CollectionCardDeckView.scss
index 1728f7aee..5ccc3d9a8 100644
--- a/src/client/views/collections/CollectionCardDeckView.scss
+++ b/src/client/views/collections/CollectionCardDeckView.scss
@@ -12,7 +12,6 @@
         height: 35px;
         border-radius: 50%;
         background-color: $dark-gray;
-        // border-color: $medium-blue;
         margin: 5px; // transform: translateY(-50px);
     }
 }
@@ -20,7 +19,6 @@
 .card-wrapper {
     display: grid;
     grid-template-columns: repeat(10, 1fr);
-    // width: 100%;
     transform-origin: top left;
 
     position: absolute;
@@ -31,33 +29,6 @@
     transition: transform 0.3s cubic-bezier(0.455, 0.03, 0.515, 0.955);
 }
 
-// .card-button-container {
-//     display: flex;
-//     padding: 3px;
-//     // width: 300px;
-//     // height:100px;
-//     pointer-events: none; /* This ensures the container does not capture hover events */
-
-//     background-color: rgb(218, 218, 218); /* Background color of the container */
-//     border-radius: 50px; /* Rounds the corners of the container */
-//     transform: translateY(25px);
-//     // box-shadow: 0 4px 8px rgba(0,0,0,0.1); /* Optional: Adds shadow for depth */
-//     align-items: center; /* Centers buttons vertically */
-//     justify-content: start; /* Centers buttons horizontally */
-
-//     button {
-//         pointer-events: auto; /* Re-enable pointer events for the buttons */
-    
-//         width: 70px;
-//         height: 70px;
-//         border-radius: 50%;
-//         background-color: $dark-gray;
-//         // border-color: $medium-blue;
-//         margin: 5px; // transform: translateY(-50px);
-//         background-color: transparent;
-//     }
-// }
-
 .no-card-span{
     position: relative;
     width: fit-content;
@@ -70,28 +41,6 @@
 
 
 
-// button:hover {
-//     transform: translateY(-50px);
-// }
-
-// .card-wrapper::after {
-//     content: "";
-//     width: 100%; /* Forces wrapping */
-// }
-
-// .card-wrapper > .card-item:nth-child(10n)::after {
-//     content: "";
-//     width: 100%; /* Forces wrapping after every 10th item */
-// }
-
-// .card-row{
-//     display: flex;
-//     position: absolute;
-//     align-items: center;
-//     transition: transform 0.3s cubic-bezier(0.455, 0.03, 0.515, 0.955);
-
-// }
-
 .card-item-inactive,
 .card-item-active,
 .card-item {
@@ -101,18 +50,11 @@
     flex-direction: column;
 }
 
-// .card-item:hover {
-//     box-shadow: 0 20px 20px $medium-blue;
-//     transform: scale(1.05);
-    
-
-// }
 
 .card-item-inactive {
     opacity: 0.5;
 }
 
 .card-item-active {
-  //  position: absolute;
     z-index: 100;
 }
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index 3d9f28a49..bb932b8b2 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -1,9 +1,9 @@
 import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { ClientUtils, DashColor, returnFalse, returnZero } from '../../../ClientUtils';
-import { emptyFunction, numberRange } from '../../../Utils';
-import { Doc, NumListCast, StrListCast } from '../../../fields/Doc';
+import { ClientUtils, returnFalse, returnZero } from '../../../ClientUtils';
+import { emptyFunction } from '../../../Utils';
+import { Doc, StrListCast } from '../../../fields/Doc';
 import { DocData } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { BoolCast, Cast, DateCast, NumCast, RTFCast, ScriptCast, StrCast } from '../../../fields/Types';
@@ -144,18 +144,7 @@ export class CollectionCardView extends CollectionSubView() {
      * custom group
      */
     @computed get childDocsWithoutLinks() {
-        const regularDocs = this.childDocs.filter(l => l.type !== DocumentType.LINK);
-        const activeGroups = StrListCast(this.Document.cardSort_visibleSortGroups);
-
-        if (activeGroups.length > 0) {
-            return regularDocs.filter(doc => {
-                const activeTags = StrListCast(doc.cardSort_activeIcons);
-                return activeTags !== undefined && activeTags.some(tag => activeGroups.includes(tag));
-            });
-        }
-
-        // Default return for non-custom cardSort or other cases, filtering out links
-        return regularDocs;
+        return this.childDocs.filter(l => l.type !== DocumentType.LINK);
     }
 
     /**
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index 53e10d44d..65bd524fe 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -147,7 +147,7 @@ ScriptingGlobals.add(function showFreeform(attr: 'hcenter' | 'vcenter' | 'grid'
     }
 
     // prettier-ignore
-    const map: Map<'flashcards' | 'center' | 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll' | 'fitOnce' | 'time' | 'docType' | 'color' | 'chat' | 'up' | 'down' | 'pile' | 'toggle-chat' | 'tag', 
+    const map: Map<'flashcards' | 'hcenter' | 'vcenter' | 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll' | 'fitOnce' | 'time' | 'docType' | 'color' | 'chat' | 'up' | 'down' | 'pile' | 'toggle-chat' | 'tag', 
     { 
         waitForRender?: boolean; 
         checkResult: (doc: Doc) => boolean; 
@@ -169,9 +169,13 @@ ScriptingGlobals.add(function showFreeform(attr: 'hcenter' | 'vcenter' | 'grid'
                 else (dv.ComponentView as CollectionFreeFormView)?.fitContentOnce();
             },
         }],
-        ['center', {
-            checkResult: (doc: Doc) => BoolCast(doc?._stacking_alignCenter, false),
-            setDoc: (doc: Doc) => { doc._stacking_alignCenter = !doc._stacking_alignCenter; },
+        ['vcenter', {
+            checkResult: (doc:Doc) => !StrCast(doc?._layout_dontCenter).includes('y'),
+            setDoc: (doc:Doc) => { doc._layout_dontCenter = StrCast(doc.layout_dontCenter).includes('y') ? StrCast(doc.layout_dontCenter).replace(/y/,"") : StrCast(doc.layout_dontCenter) + 'y'; },
+        }],
+        ['hcenter', {
+            checkResult: (doc:Doc) => !StrCast(doc?._layout_dontCenter).includes('x'),
+            setDoc: (doc:Doc) => { doc._layout_dontCenter = StrCast(doc.layout_dontCenter).includes('x') ? StrCast(doc.layout_dontCenter).replace(/x/,"") : 'x'+ StrCast(doc.layout_dontCenter); },
         }],
         ['clusters', {
             waitForRender: true, // flags that undo batch should terminate after a re-render giving the script the chance to fire
diff --git a/src/client/views/nodes/IconTagBox.scss b/src/client/views/nodes/IconTagBox.scss
index 8c0f92c90..211a961c1 100644
--- a/src/client/views/nodes/IconTagBox.scss
+++ b/src/client/views/nodes/IconTagBox.scss
@@ -4,27 +4,21 @@
     display: flex;
     padding: 3px;
     position: absolute;
-    // width: 300px;
-    // height:100px;
-    pointer-events: none; /* This ensures the container does not capture hover events */
-
-    background-color: rgb(218, 218, 218); /* Background color of the container */
-    border-radius: 50px; /* Rounds the corners of the container */
+    pointer-events: none;
+    background-color: rgb(218, 218, 218); 
+    border-radius: 50px;
     transform: translateY(25px);
-    // box-shadow: 0 4px 8px rgba(0,0,0,0.1); /* Optional: Adds shadow for depth */
-    align-items: center; /* Centers buttons vertically */
-    justify-content: start; /* Centers buttons horizontally */
+    align-items: center; 
+    justify-content: start;
 
     button {
-        pointer-events: auto; /* Re-enable pointer events for the buttons */
+        pointer-events: auto;
         transform: translateY(-7.5px);
-
         width: 30px;
         height: 30px;
         border-radius: 50%;
         background-color: $dark-gray;
-        // border-color: $medium-blue;
-        margin: 5px; // transform: translateY(-50px);
+        margin: 5px; 
         background-color: transparent;
     }
 }
\ No newline at end of file
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.scss b/src/client/views/pdf/GPTPopup/GPTPopup.scss
index 1defd1a7f..0247dc10c 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.scss
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.scss
@@ -104,9 +104,9 @@ $highlightedText: #82e0ff;
             display: flex;
             flex-direction: column;
             width: 100%;
-            max-height: calc(100vh - 80px); /* Height minus the input box and some padding */
+            max-height: calc(100vh - 80px);
             overflow-y: auto;
-            padding-bottom: 60px; /* Space for the input */
+            padding-bottom: 60px; 
         }
     
         .chat-bubbles {
@@ -150,15 +150,7 @@ $highlightedText: #82e0ff;
         
     }
 
-    // button {
-    //     font-size: 9px;
-    //     padding: 10px;
-    //     color: #ffffff;
-    //     width: 100%;
-    //     background-color: $button;
-    //     border-radius: 5px;
 
-    // }
 
     .text-btn {
         &:hover {
-- 
cgit v1.2.3-70-g09d2


From bde7bd192b1a81e3fd1c4ead13e821b95b42f3cf Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 17 Sep 2024 21:02:35 -0400
Subject: updating packages and more cleanup of cardview merge

---
 package-lock.json                                  | 1098 ++++++++++----------
 package.json                                       |    6 +-
 src/client/views/DocumentDecorations.tsx           |    3 -
 src/client/views/StyleProvider.tsx                 |    7 +-
 .../views/collections/CollectionCardDeckView.tsx   |    7 +-
 src/client/views/nodes/FontIconBox/FontIconBox.tsx |    4 +-
 src/client/views/pdf/AnchorMenu.tsx                |    2 +-
 7 files changed, 585 insertions(+), 542 deletions(-)

(limited to 'src/client/views/StyleProvider.tsx')

diff --git a/package-lock.json b/package-lock.json
index d20aac0c5..78a1281d0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -51,7 +51,7 @@
                 "@types/reveal": "^4.2.0",
                 "@types/supercluster": "^7.1.3",
                 "@types/textfit": "^2.4.4",
-                "@types/web": "^0.0.159",
+                "@types/web": "^0.0.163",
                 "@types/webpack-hot-middleware": "^2.25.9",
                 "@webscopeio/react-textarea-autocomplete": "^4.9.2",
                 "adm-zip": "^0.5.10",
@@ -109,7 +109,7 @@
                 "file-saver": "^2.0.5",
                 "find-in-files": "^0.5.0",
                 "fit-curve": "^0.2.0",
-                "flexlayout-react": "^0.7.15",
+                "flexlayout-react": "^0.8.0",
                 "fluent-ffmpeg": "^2.1.2",
                 "forever-agent": "^0.6.1",
                 "fork-ts-checker-webpack-plugin": "^9.0.2",
@@ -119,7 +119,7 @@
                 "function-plot": "^1.23.3",
                 "golden-layout": "^2.6.0",
                 "google-auth-library": "^9.4.1",
-                "googleapis": "^142.0.0",
+                "googleapis": "^144.0.0",
                 "googlephotos": "^0.3.5",
                 "got": "^14.0.0",
                 "howler": "^2.2.4",
@@ -451,9 +451,9 @@
             }
         },
         "node_modules/@azure/core-auth": {
-            "version": "1.7.2",
-            "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.7.2.tgz",
-            "integrity": "sha512-Igm/S3fDYmnMq1uKS38Ae1/m37B3zigdlZw+kocwEhh5GjyKjPrXKO2J6rzpC1wAxrNil/jX9BJRqBshyjnF3g==",
+            "version": "1.8.0",
+            "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.8.0.tgz",
+            "integrity": "sha512-YvFMowkXzLbXNM11yZtVLhUCmuG0ex7JKOH366ipjmHBhL3vpDcPAeWF+jf0X+jVXwFqo3UhsWUq4kH0ZPdu/g==",
             "dependencies": {
                 "@azure/abort-controller": "^2.0.0",
                 "@azure/core-util": "^1.1.0",
@@ -563,12 +563,12 @@
             }
         },
         "node_modules/@azure/core-rest-pipeline": {
-            "version": "1.16.3",
-            "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.16.3.tgz",
-            "integrity": "sha512-VxLk4AHLyqcHsfKe4MZ6IQ+D+ShuByy+RfStKfSjxJoL3WBWq17VNmrz8aT8etKzqc2nAeIyLxScjpzsS4fz8w==",
+            "version": "1.17.0",
+            "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.17.0.tgz",
+            "integrity": "sha512-62Vv8nC+uPId3j86XJ0WI+sBf0jlqTqPUFCBNrGtlaUeQUIXWV/D8GE5A1d+Qx8H7OQojn2WguC8kChD6v0shA==",
             "dependencies": {
                 "@azure/abort-controller": "^2.0.0",
-                "@azure/core-auth": "^1.4.0",
+                "@azure/core-auth": "^1.8.0",
                 "@azure/core-tracing": "^1.0.1",
                 "@azure/core-util": "^1.9.0",
                 "@azure/logger": "^1.0.0",
@@ -603,9 +603,9 @@
             }
         },
         "node_modules/@azure/core-util": {
-            "version": "1.9.2",
-            "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.9.2.tgz",
-            "integrity": "sha512-l1Qrqhi4x1aekkV+OlcqsJa4AnAkj5p0JV8omgwjaV9OAbP41lvrMvs+CptfetKkeEaGRGSzby7sjPZEX7+kkQ==",
+            "version": "1.10.0",
+            "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.10.0.tgz",
+            "integrity": "sha512-dqLWQsh9Nro1YQU+405POVtXnwrIVqPyfUzc4zXCbThTg7+vNNaiMkwbX9AMXKyoFYFClxmB3s25ZFr3+jZkww==",
             "dependencies": {
                 "@azure/abort-controller": "^2.0.0",
                 "tslib": "^2.6.2"
@@ -2590,9 +2590,9 @@
             }
         },
         "node_modules/@eslint-community/regexpp": {
-            "version": "4.11.0",
-            "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz",
-            "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==",
+            "version": "4.11.1",
+            "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz",
+            "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==",
             "engines": {
                 "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
             }
@@ -2754,29 +2754,29 @@
             }
         },
         "node_modules/@floating-ui/core": {
-            "version": "1.6.7",
-            "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.7.tgz",
-            "integrity": "sha512-yDzVT/Lm101nQ5TCVeK65LtdN7Tj4Qpr9RTXJ2vPFLqtLxwOrpoxAHAJI8J3yYWUc40J0BDBheaitK5SJmno2g==",
+            "version": "1.6.8",
+            "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.8.tgz",
+            "integrity": "sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==",
             "dependencies": {
-                "@floating-ui/utils": "^0.2.7"
+                "@floating-ui/utils": "^0.2.8"
             }
         },
         "node_modules/@floating-ui/dom": {
-            "version": "1.6.10",
-            "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.10.tgz",
-            "integrity": "sha512-fskgCFv8J8OamCmyun8MfjB1Olfn+uZKjOKZ0vhYF3gRmEUXcGOjxWL8bBr7i4kIuPZ2KD2S3EUIOxnjC8kl2A==",
+            "version": "1.6.11",
+            "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.11.tgz",
+            "integrity": "sha512-qkMCxSR24v2vGkhYDo/UzxfJN3D4syqSjyuTFz6C7XcpU1pASPRieNI0Kj5VP3/503mOfYiGY891ugBX1GlABQ==",
             "dependencies": {
                 "@floating-ui/core": "^1.6.0",
-                "@floating-ui/utils": "^0.2.7"
+                "@floating-ui/utils": "^0.2.8"
             }
         },
         "node_modules/@floating-ui/react": {
-            "version": "0.26.23",
-            "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.23.tgz",
-            "integrity": "sha512-9u3i62fV0CFF3nIegiWiRDwOs7OW/KhSUJDNx2MkQM3LbE5zQOY01sL3nelcVBXvX7Ovvo3A49I8ql+20Wg/Hw==",
+            "version": "0.26.24",
+            "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.24.tgz",
+            "integrity": "sha512-2ly0pCkZIGEQUq5H8bBK0XJmc1xIK/RM3tvVzY3GBER7IOD1UgmC2Y2tjj4AuS+TC+vTE1KJv2053290jua0Sw==",
             "dependencies": {
-                "@floating-ui/react-dom": "^2.1.1",
-                "@floating-ui/utils": "^0.2.7",
+                "@floating-ui/react-dom": "^2.1.2",
+                "@floating-ui/utils": "^0.2.8",
                 "tabbable": "^6.0.0"
             },
             "peerDependencies": {
@@ -2785,9 +2785,9 @@
             }
         },
         "node_modules/@floating-ui/react-dom": {
-            "version": "2.1.1",
-            "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.1.tgz",
-            "integrity": "sha512-4h84MJt3CHrtG18mGsXuLCHMrug49d7DFkU0RMIyshRveBeyV2hmV/pDaF2Uxtu8kgq5r46llp5E5FQiR0K2Yg==",
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz",
+            "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==",
             "dependencies": {
                 "@floating-ui/dom": "^1.0.0"
             },
@@ -2797,9 +2797,9 @@
             }
         },
         "node_modules/@floating-ui/utils": {
-            "version": "0.2.7",
-            "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.7.tgz",
-            "integrity": "sha512-X8R8Oj771YRl/w+c1HqAC1szL8zWQRwFvgDwT129k9ACdBoud/+/rX9V0qiMl6LWUdP9voC2nDVZYPMQQsb6eA=="
+            "version": "0.2.8",
+            "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.8.tgz",
+            "integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig=="
         },
         "node_modules/@formatjs/ecma402-abstract": {
             "version": "2.0.0",
@@ -3010,9 +3010,9 @@
             "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="
         },
         "node_modules/@iconify/utils": {
-            "version": "2.1.32",
-            "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-2.1.32.tgz",
-            "integrity": "sha512-LeifFZPPKu28O3AEDpYJNdEbvS4/ojAPyIW+pF/vUpJTYnbTiXUHkCh0bwgFRzKvdpb8H4Fbfd/742++MF4fPQ==",
+            "version": "2.1.33",
+            "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-2.1.33.tgz",
+            "integrity": "sha512-jP9h6v/g0BIZx0p7XGJJVtkVnydtbgTgt9mVNcGDYwaa7UhdHdI9dvoq+gKj9sijMSJKxUPEG2JyjsgXjxL7Kw==",
             "dependencies": {
                 "@antfu/install-pkg": "^0.4.0",
                 "@antfu/utils": "^0.7.10",
@@ -3081,9 +3081,9 @@
             }
         },
         "node_modules/@isaacs/cliui/node_modules/ansi-regex": {
-            "version": "6.0.1",
-            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
-            "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+            "version": "6.1.0",
+            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
+            "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
             "engines": {
                 "node": ">=12"
             },
@@ -3197,13 +3197,13 @@
             }
         },
         "node_modules/@jimp/core": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/@jimp/core/-/core-1.3.0.tgz",
-            "integrity": "sha512-3+ndSrQYQxyyKyUSdXyk29vHt9vc3zg+3aFrVX2RHzAMeLdjkQHqzQc/7v6VxRk6BtOh1v/VPtmsXHXVg1vVhA==",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/@jimp/core/-/core-1.6.0.tgz",
+            "integrity": "sha512-EQQlKU3s9QfdJqiSrZWNTxBs3rKXgO2W+GxNXDtwchF3a4IqxDheFX1ti+Env9hdJXDiYLp2jTRjlxhPthsk8w==",
             "dependencies": {
-                "@jimp/file-ops": "1.3.0",
-                "@jimp/types": "1.3.0",
-                "@jimp/utils": "1.3.0",
+                "@jimp/file-ops": "1.6.0",
+                "@jimp/types": "1.6.0",
+                "@jimp/utils": "1.6.0",
                 "await-to-js": "^3.0.0",
                 "exif-parser": "^0.1.12",
                 "file-type": "^16.0.0",
@@ -3214,13 +3214,13 @@
             }
         },
         "node_modules/@jimp/diff": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/@jimp/diff/-/diff-1.3.0.tgz",
-            "integrity": "sha512-eGFfZi8UjSZ6gGu9kpQmGPRRDt5fMV3V1qKRn0cTqtBsECAnKPG5PPT1dvnjDDBbtAOH81jhIr//ko8H5WV8jg==",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/@jimp/diff/-/diff-1.6.0.tgz",
+            "integrity": "sha512-+yUAQ5gvRC5D1WHYxjBHZI7JBRusGGSLf8AmPRPCenTzh4PA+wZ1xv2+cYqQwTfQHU5tXYOhA0xDytfHUf1Zyw==",
             "dependencies": {
-                "@jimp/plugin-resize": "1.3.0",
-                "@jimp/types": "1.3.0",
-                "@jimp/utils": "1.3.0",
+                "@jimp/plugin-resize": "1.6.0",
+                "@jimp/types": "1.6.0",
+                "@jimp/utils": "1.6.0",
                 "pixelmatch": "^5.3.0"
             },
             "engines": {
@@ -3228,21 +3228,21 @@
             }
         },
         "node_modules/@jimp/file-ops": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/@jimp/file-ops/-/file-ops-1.3.0.tgz",
-            "integrity": "sha512-DzbSLgUdOGT9T9qf+Ik/hBA8e4zA9tWyHnJJA/N9sJHwHNDCMtoaX3KY5ssHuRGmcngGKLwbeGjddnZXF4oIVA==",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/@jimp/file-ops/-/file-ops-1.6.0.tgz",
+            "integrity": "sha512-Dx/bVDmgnRe1AlniRpCKrGRm5YvGmUwbDzt+MAkgmLGf+jvBT75hmMEZ003n9HQI/aPnm/YKnXjg/hOpzNCpHQ==",
             "engines": {
                 "node": ">=18"
             }
         },
         "node_modules/@jimp/js-bmp": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/@jimp/js-bmp/-/js-bmp-1.3.0.tgz",
-            "integrity": "sha512-sc3jvJJOMHoUtP9mlnuBCkwhHy9T2KiSfdV3XKg81v+bg9O0LudB33v3Y0dtGxSo/WOL2V6PVNIWTRf7gu+hJw==",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/@jimp/js-bmp/-/js-bmp-1.6.0.tgz",
+            "integrity": "sha512-FU6Q5PC/e3yzLyBDXupR3SnL3htU7S3KEs4e6rjDP6gNEOXRFsWs6YD3hXuXd50jd8ummy+q2WSwuGkr8wi+Gw==",
             "dependencies": {
-                "@jimp/core": "1.3.0",
-                "@jimp/types": "1.3.0",
-                "@jimp/utils": "1.3.0",
+                "@jimp/core": "1.6.0",
+                "@jimp/types": "1.6.0",
+                "@jimp/utils": "1.6.0",
                 "bmp-ts": "^1.0.9"
             },
             "engines": {
@@ -3250,12 +3250,12 @@
             }
         },
         "node_modules/@jimp/js-gif": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/@jimp/js-gif/-/js-gif-1.3.0.tgz",
-            "integrity": "sha512-hhAd/TpZSp6AIuRjYKFGGEsu9HzQG16Q9lZHPcTZz1TlxtTUqW60AJvFrGvnUZbTecDj3JnI3TipX8aeqZpBDg==",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/@jimp/js-gif/-/js-gif-1.6.0.tgz",
+            "integrity": "sha512-N9CZPHOrJTsAUoWkWZstLPpwT5AwJ0wge+47+ix3++SdSL/H2QzyMqxbcDYNFe4MoI5MIhATfb0/dl/wmX221g==",
             "dependencies": {
-                "@jimp/core": "1.3.0",
-                "@jimp/types": "1.3.0",
+                "@jimp/core": "1.6.0",
+                "@jimp/types": "1.6.0",
                 "gifwrap": "^0.10.1",
                 "omggif": "^1.0.10"
             },
@@ -3264,12 +3264,12 @@
             }
         },
         "node_modules/@jimp/js-jpeg": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/@jimp/js-jpeg/-/js-jpeg-1.3.0.tgz",
-            "integrity": "sha512-SrZL35FvKsgySS5kpqYWbVsi0rswcgxw3oRTVOy55q8F045fE4U0YqlnRdkg77dxdahUOIWVhvn5+0V+Re5F5A==",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/@jimp/js-jpeg/-/js-jpeg-1.6.0.tgz",
+            "integrity": "sha512-6vgFDqeusblf5Pok6B2DUiMXplH8RhIKAryj1yn+007SIAQ0khM1Uptxmpku/0MfbClx2r7pnJv9gWpAEJdMVA==",
             "dependencies": {
-                "@jimp/core": "1.3.0",
-                "@jimp/types": "1.3.0",
+                "@jimp/core": "1.6.0",
+                "@jimp/types": "1.6.0",
                 "jpeg-js": "^0.4.4"
             },
             "engines": {
@@ -3277,12 +3277,12 @@
             }
         },
         "node_modules/@jimp/js-png": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/@jimp/js-png/-/js-png-1.3.0.tgz",
-            "integrity": "sha512-9x8uFueVupNocQQ5WEFat61M31MdRDV7O3QBDR2iEsbVeQ1+LE9Tvvm9r1PG9W91KZYzG4IYPPsogQ0TFEixqA==",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/@jimp/js-png/-/js-png-1.6.0.tgz",
+            "integrity": "sha512-AbQHScy3hDDgMRNfG0tPjL88AV6qKAILGReIa3ATpW5QFjBKpisvUaOqhzJ7Reic1oawx3Riyv152gaPfqsBVg==",
             "dependencies": {
-                "@jimp/core": "1.3.0",
-                "@jimp/types": "1.3.0",
+                "@jimp/core": "1.6.0",
+                "@jimp/types": "1.6.0",
                 "pngjs": "^7.0.0"
             },
             "engines": {
@@ -3290,12 +3290,12 @@
             }
         },
         "node_modules/@jimp/js-tiff": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/@jimp/js-tiff/-/js-tiff-1.3.0.tgz",
-            "integrity": "sha512-uin5WkVm0M8ZgotIANXU0sfVkNApsKws1ZSqsc9NZf0MYJsZkz/w8D4ld6hXkFCEQcJ/TMu7aeMZSP+I8cbmOg==",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/@jimp/js-tiff/-/js-tiff-1.6.0.tgz",
+            "integrity": "sha512-zhReR8/7KO+adijj3h0ZQUOiun3mXUv79zYEAKvE0O+rP7EhgtKvWJOZfRzdZSNv0Pu1rKtgM72qgtwe2tFvyw==",
             "dependencies": {
-                "@jimp/core": "1.3.0",
-                "@jimp/types": "1.3.0",
+                "@jimp/core": "1.6.0",
+                "@jimp/types": "1.6.0",
                 "utif2": "^4.1.0"
             },
             "engines": {
@@ -3303,12 +3303,12 @@
             }
         },
         "node_modules/@jimp/plugin-blit": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-1.3.0.tgz",
-            "integrity": "sha512-5OjEUM0jtzQ6KJBTougs8ozbUAxYyJiKZqceFb8mqKVMjuiM94N9425mIDDgOl5MDFwAESeTMIchKCAPMOv2FQ==",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-1.6.0.tgz",
+            "integrity": "sha512-M+uRWl1csi7qilnSK8uxK4RJMSuVeBiO1AY0+7APnfUbQNZm6hCe0CCFv1Iyw1D/Dhb8ph8fQgm5mwM0eSxgVA==",
             "dependencies": {
-                "@jimp/types": "1.3.0",
-                "@jimp/utils": "1.3.0",
+                "@jimp/types": "1.6.0",
+                "@jimp/utils": "1.6.0",
                 "zod": "^3.23.8"
             },
             "engines": {
@@ -3316,23 +3316,23 @@
             }
         },
         "node_modules/@jimp/plugin-blur": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-1.3.0.tgz",
-            "integrity": "sha512-108RHeCvHFJqpQvuaydhxwJLBwdjEWKLW6ZXWWCnCadrpKbH2yqu9P6oUhHS7atLjQ0ZBzXcM+Wj2VYR7XU8ng==",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-1.6.0.tgz",
+            "integrity": "sha512-zrM7iic1OTwUCb0g/rN5y+UnmdEsT3IfuCXCJJNs8SZzP0MkZ1eTvuwK9ZidCuMo4+J3xkzCidRwYXB5CyGZTw==",
             "dependencies": {
-                "@jimp/core": "1.3.0",
-                "@jimp/utils": "1.3.0"
+                "@jimp/core": "1.6.0",
+                "@jimp/utils": "1.6.0"
             },
             "engines": {
                 "node": ">=18"
             }
         },
         "node_modules/@jimp/plugin-circle": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/@jimp/plugin-circle/-/plugin-circle-1.3.0.tgz",
-            "integrity": "sha512-Lkz1uwD2wgysuu4TDzAVQ26+urr+siYlO/qXnMYHui9k0P735S6B6EsWrzssLDGOtqevQyYcx5u6h0Kv4lzehg==",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/@jimp/plugin-circle/-/plugin-circle-1.6.0.tgz",
+            "integrity": "sha512-xt1Gp+LtdMKAXfDp3HNaG30SPZW6AQ7dtAtTnoRKorRi+5yCJjKqXRgkewS5bvj8DEh87Ko1ydJfzqS3P2tdWw==",
             "dependencies": {
-                "@jimp/types": "1.3.0",
+                "@jimp/types": "1.6.0",
                 "zod": "^3.23.8"
             },
             "engines": {
@@ -3340,13 +3340,13 @@
             }
         },
         "node_modules/@jimp/plugin-color": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-1.3.0.tgz",
-            "integrity": "sha512-HLFtZB86W2nVnfZT+LAsyooF9efapWPmxuOKECeevunb1zHieO1ni19QXJfcqtt+cVj8UxIBGC4v9IFDJ9PGYw==",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-1.6.0.tgz",
+            "integrity": "sha512-J5q8IVCpkBsxIXM+45XOXTrsyfblyMZg3a9eAo0P7VPH4+CrvyNQwaYatbAIamSIN1YzxmO3DkIZXzRjFSz1SA==",
             "dependencies": {
-                "@jimp/core": "1.3.0",
-                "@jimp/types": "1.3.0",
-                "@jimp/utils": "1.3.0",
+                "@jimp/core": "1.6.0",
+                "@jimp/types": "1.6.0",
+                "@jimp/utils": "1.6.0",
                 "tinycolor2": "^1.6.0",
                 "zod": "^3.23.8"
             },
@@ -3355,15 +3355,15 @@
             }
         },
         "node_modules/@jimp/plugin-contain": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-1.3.0.tgz",
-            "integrity": "sha512-1xPJ/CC4hh8IDrZFCtwQezw0RFzdrFvatzXkmfZD0cRyUXtYQ8VzExeK9MXLWi2+/nfufh+2SIhThTQ8xIzLBw==",
-            "dependencies": {
-                "@jimp/core": "1.3.0",
-                "@jimp/plugin-blit": "1.3.0",
-                "@jimp/plugin-resize": "1.3.0",
-                "@jimp/types": "1.3.0",
-                "@jimp/utils": "1.3.0",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-1.6.0.tgz",
+            "integrity": "sha512-oN/n+Vdq/Qg9bB4yOBOxtY9IPAtEfES8J1n9Ddx+XhGBYT1/QTU/JYkGaAkIGoPnyYvmLEDqMz2SGihqlpqfzQ==",
+            "dependencies": {
+                "@jimp/core": "1.6.0",
+                "@jimp/plugin-blit": "1.6.0",
+                "@jimp/plugin-resize": "1.6.0",
+                "@jimp/types": "1.6.0",
+                "@jimp/utils": "1.6.0",
                 "zod": "^3.23.8"
             },
             "engines": {
@@ -3371,14 +3371,14 @@
             }
         },
         "node_modules/@jimp/plugin-cover": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-1.3.0.tgz",
-            "integrity": "sha512-Q8vlXepruKU+A55PS2A+d7TPwIoYthnX61ae+TQa+/4DjYk6XZA2YlmUFhq7P3RH5p288N/84ILNSIwH5YCqAw==",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-1.6.0.tgz",
+            "integrity": "sha512-Iow0h6yqSC269YUJ8HC3Q/MpCi2V55sMlbkkTTx4zPvd8mWZlC0ykrNDeAy9IJegrQ7v5E99rJwmQu25lygKLA==",
             "dependencies": {
-                "@jimp/core": "1.3.0",
-                "@jimp/plugin-crop": "1.3.0",
-                "@jimp/plugin-resize": "1.3.0",
-                "@jimp/types": "1.3.0",
+                "@jimp/core": "1.6.0",
+                "@jimp/plugin-crop": "1.6.0",
+                "@jimp/plugin-resize": "1.6.0",
+                "@jimp/types": "1.6.0",
                 "zod": "^3.23.8"
             },
             "engines": {
@@ -3386,13 +3386,13 @@
             }
         },
         "node_modules/@jimp/plugin-crop": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-1.3.0.tgz",
-            "integrity": "sha512-AoCTYFgcDEH+sqc2IQ5CI0CgYrQZSFfZ6q4zSXkWA+irs1nDbjJeA+0vClkYxJNSkk2wqB0fys69OBoqfDabrw==",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-1.6.0.tgz",
+            "integrity": "sha512-KqZkEhvs+21USdySCUDI+GFa393eDIzbi1smBqkUPTE+pRwSWMAf01D5OC3ZWB+xZsNla93BDS9iCkLHA8wang==",
             "dependencies": {
-                "@jimp/core": "1.3.0",
-                "@jimp/types": "1.3.0",
-                "@jimp/utils": "1.3.0",
+                "@jimp/core": "1.6.0",
+                "@jimp/types": "1.6.0",
+                "@jimp/utils": "1.6.0",
                 "zod": "^3.23.8"
             },
             "engines": {
@@ -3400,12 +3400,12 @@
             }
         },
         "node_modules/@jimp/plugin-displace": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-1.3.0.tgz",
-            "integrity": "sha512-8t/R0SjE7YWujeMLbUT2js9WIeyFbeQXxAiCPt4AJy1BUD56sbcWIx1zJzrL52eF+bG4AS8oLOp5arL+P7ocmQ==",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-1.6.0.tgz",
+            "integrity": "sha512-4Y10X9qwr5F+Bo5ME356XSACEF55485j5nGdiyJ9hYzjQP9nGgxNJaZ4SAOqpd+k5sFaIeD7SQ0Occ26uIng5Q==",
             "dependencies": {
-                "@jimp/types": "1.3.0",
-                "@jimp/utils": "1.3.0",
+                "@jimp/types": "1.6.0",
+                "@jimp/utils": "1.6.0",
                 "zod": "^3.23.8"
             },
             "engines": {
@@ -3413,23 +3413,23 @@
             }
         },
         "node_modules/@jimp/plugin-dither": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-1.3.0.tgz",
-            "integrity": "sha512-oE6kHne88OOcJBu+fk9KvMMB71UZUO4B2YYoaHVjGbtciPG9FbBBGqgD9oULVWhHuICZdDnfgFF0hhemQuQloQ==",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-1.6.0.tgz",
+            "integrity": "sha512-600d1RxY0pKwgyU0tgMahLNKsqEcxGdbgXadCiVCoGd6V6glyCvkNrnnwC0n5aJ56Htkj88PToSdF88tNVZEEQ==",
             "dependencies": {
-                "@jimp/types": "1.3.0"
+                "@jimp/types": "1.6.0"
             },
             "engines": {
                 "node": ">=18"
             }
         },
         "node_modules/@jimp/plugin-fisheye": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/@jimp/plugin-fisheye/-/plugin-fisheye-1.3.0.tgz",
-            "integrity": "sha512-VLaqY/IxrqHyjKeWpUwJZpAqug4DE26hM/8ejfPm5FmofAS1dI0deecDfbthRbw17hnPVcAiTkZ6QTiQL71Z4w==",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/@jimp/plugin-fisheye/-/plugin-fisheye-1.6.0.tgz",
+            "integrity": "sha512-E5QHKWSCBFtpgZarlmN3Q6+rTQxjirFqo44ohoTjzYVrDI6B6beXNnPIThJgPr0Y9GwfzgyarKvQuQuqCnnfbA==",
             "dependencies": {
-                "@jimp/types": "1.3.0",
-                "@jimp/utils": "1.3.0",
+                "@jimp/types": "1.6.0",
+                "@jimp/utils": "1.6.0",
                 "zod": "^3.23.8"
             },
             "engines": {
@@ -3437,11 +3437,11 @@
             }
         },
         "node_modules/@jimp/plugin-flip": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-1.3.0.tgz",
-            "integrity": "sha512-cHeefBsjBYLbjqq8TFJHQD/6sgJLvb6XPdB8wVvKe682Y8jIilxlEhZJeUFXwsMrKKhbXNZxmtSc/pEIYCo6cA==",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-1.6.0.tgz",
+            "integrity": "sha512-/+rJVDuBIVOgwoyVkBjUFHtP+wmW0r+r5OQ2GpatQofToPVbJw1DdYWXlwviSx7hvixTWLKVgRWQ5Dw862emDg==",
             "dependencies": {
-                "@jimp/types": "1.3.0",
+                "@jimp/types": "1.6.0",
                 "zod": "^3.23.8"
             },
             "engines": {
@@ -3449,19 +3449,19 @@
             }
         },
         "node_modules/@jimp/plugin-hash": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/@jimp/plugin-hash/-/plugin-hash-1.3.0.tgz",
-            "integrity": "sha512-LAUi9FFT3Kp2bI2hWXQ75t1pl6q6ZyKH/jJQZg8DPL+fFK0//TIsw/g0VxW5lZoV3mHUUorQlsZzWtNi/DGtWA==",
-            "dependencies": {
-                "@jimp/core": "1.3.0",
-                "@jimp/js-bmp": "1.3.0",
-                "@jimp/js-jpeg": "1.3.0",
-                "@jimp/js-png": "1.3.0",
-                "@jimp/js-tiff": "1.3.0",
-                "@jimp/plugin-color": "1.3.0",
-                "@jimp/plugin-resize": "1.3.0",
-                "@jimp/types": "1.3.0",
-                "@jimp/utils": "1.3.0",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/@jimp/plugin-hash/-/plugin-hash-1.6.0.tgz",
+            "integrity": "sha512-wWzl0kTpDJgYVbZdajTf+4NBSKvmI3bRI8q6EH9CVeIHps9VWVsUvEyb7rpbcwVLWYuzDtP2R0lTT6WeBNQH9Q==",
+            "dependencies": {
+                "@jimp/core": "1.6.0",
+                "@jimp/js-bmp": "1.6.0",
+                "@jimp/js-jpeg": "1.6.0",
+                "@jimp/js-png": "1.6.0",
+                "@jimp/js-tiff": "1.6.0",
+                "@jimp/plugin-color": "1.6.0",
+                "@jimp/plugin-resize": "1.6.0",
+                "@jimp/types": "1.6.0",
+                "@jimp/utils": "1.6.0",
                 "any-base": "^1.1.0"
             },
             "engines": {
@@ -3469,11 +3469,11 @@
             }
         },
         "node_modules/@jimp/plugin-mask": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-1.3.0.tgz",
-            "integrity": "sha512-fpU6rZ75c1gD6/8zsiPQW57+doX3KfexZ3lVYToyd720HPO/qfG9lzwfY30tJVXArI4DMbG8qN7lXKgGeWwGqw==",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-1.6.0.tgz",
+            "integrity": "sha512-Cwy7ExSJMZszvkad8NV8o/Z92X2kFUFM8mcDAhNVxU0Q6tA0op2UKRJY51eoK8r6eds/qak3FQkXakvNabdLnA==",
             "dependencies": {
-                "@jimp/types": "1.3.0",
+                "@jimp/types": "1.6.0",
                 "zod": "^3.23.8"
             },
             "engines": {
@@ -3481,15 +3481,15 @@
             }
         },
         "node_modules/@jimp/plugin-print": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-1.3.0.tgz",
-            "integrity": "sha512-WeN35Fo9Bushm6VGUdQXqXrVIFDYECeKLKN+LlAqQ/YnIlUiTirPlcyGHzEBKD8uXDCmjBYqxwadcPvRDVwFEw==",
-            "dependencies": {
-                "@jimp/core": "1.3.0",
-                "@jimp/js-jpeg": "1.3.0",
-                "@jimp/js-png": "1.3.0",
-                "@jimp/plugin-blit": "1.3.0",
-                "@jimp/types": "1.3.0",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-1.6.0.tgz",
+            "integrity": "sha512-zarTIJi8fjoGMSI/M3Xh5yY9T65p03XJmPsuNet19K/Q7mwRU6EV2pfj+28++2PV2NJ+htDF5uecAlnGyxFN2A==",
+            "dependencies": {
+                "@jimp/core": "1.6.0",
+                "@jimp/js-jpeg": "1.6.0",
+                "@jimp/js-png": "1.6.0",
+                "@jimp/plugin-blit": "1.6.0",
+                "@jimp/types": "1.6.0",
                 "parse-bmfont-ascii": "^1.0.6",
                 "parse-bmfont-binary": "^1.0.6",
                 "parse-bmfont-xml": "^1.1.6",
@@ -3501,9 +3501,9 @@
             }
         },
         "node_modules/@jimp/plugin-quantize": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/@jimp/plugin-quantize/-/plugin-quantize-1.3.0.tgz",
-            "integrity": "sha512-4Hgp5UNN8DGeX1ULNANPwlHCyuaZYZPJ/mpe/lnCN4jLI/SeBzR4g8tU+srNF6arPwRXrLNQV6T/ehAa7zhbkg==",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/@jimp/plugin-quantize/-/plugin-quantize-1.6.0.tgz",
+            "integrity": "sha512-EmzZ/s9StYQwbpG6rUGBCisc3f64JIhSH+ncTJd+iFGtGo0YvSeMdAd+zqgiHpfZoOL54dNavZNjF4otK+mvlg==",
             "dependencies": {
                 "image-q": "^4.0.0",
                 "zod": "^3.23.8"
@@ -3513,12 +3513,12 @@
             }
         },
         "node_modules/@jimp/plugin-resize": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-1.3.0.tgz",
-            "integrity": "sha512-9fMw6ff/3kzDwQ2rGNYCJ2jc9IHsxQh9eaoPb4SkVHxzq+O3yka3M2Vjf41gaYhQ5Pt5QLgQ9uYUA+2kp1RF1g==",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-1.6.0.tgz",
+            "integrity": "sha512-uSUD1mqXN9i1SGSz5ov3keRZ7S9L32/mAQG08wUwZiEi5FpbV0K8A8l1zkazAIZi9IJzLlTauRNU41Mi8IF9fA==",
             "dependencies": {
-                "@jimp/core": "1.3.0",
-                "@jimp/types": "1.3.0",
+                "@jimp/core": "1.6.0",
+                "@jimp/types": "1.6.0",
                 "zod": "^3.23.8"
             },
             "engines": {
@@ -3526,15 +3526,15 @@
             }
         },
         "node_modules/@jimp/plugin-rotate": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-1.3.0.tgz",
-            "integrity": "sha512-lpzk37tzk7b5RG5U6P/E3vk+bwU86TnNZOc1LHQeTOEpfFAPMTJl6w+OlLLxVNJ7HUQege/8P47N2onQX00wXw==",
-            "dependencies": {
-                "@jimp/core": "1.3.0",
-                "@jimp/plugin-crop": "1.3.0",
-                "@jimp/plugin-resize": "1.3.0",
-                "@jimp/types": "1.3.0",
-                "@jimp/utils": "1.3.0",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-1.6.0.tgz",
+            "integrity": "sha512-JagdjBLnUZGSG4xjCLkIpQOZZ3Mjbg8aGCCi4G69qR+OjNpOeGI7N2EQlfK/WE8BEHOW5vdjSyglNqcYbQBWRw==",
+            "dependencies": {
+                "@jimp/core": "1.6.0",
+                "@jimp/plugin-crop": "1.6.0",
+                "@jimp/plugin-resize": "1.6.0",
+                "@jimp/types": "1.6.0",
+                "@jimp/utils": "1.6.0",
                 "zod": "^3.23.8"
             },
             "engines": {
@@ -3542,15 +3542,15 @@
             }
         },
         "node_modules/@jimp/plugin-threshold": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/@jimp/plugin-threshold/-/plugin-threshold-1.3.0.tgz",
-            "integrity": "sha512-lKBzZEgjI/zM51/muGyL2juGEkK361/yFpRcmjafIijq0sHNww1rhqSa0AhO80iCmN77A4Ym/oelY0qh4mtlLQ==",
-            "dependencies": {
-                "@jimp/core": "1.3.0",
-                "@jimp/plugin-color": "1.3.0",
-                "@jimp/plugin-hash": "1.3.0",
-                "@jimp/types": "1.3.0",
-                "@jimp/utils": "1.3.0",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/@jimp/plugin-threshold/-/plugin-threshold-1.6.0.tgz",
+            "integrity": "sha512-M59m5dzLoHOVWdM41O8z9SyySzcDn43xHseOH0HavjsfQsT56GGCC4QzU1banJidbUrePhzoEdS42uFE8Fei8w==",
+            "dependencies": {
+                "@jimp/core": "1.6.0",
+                "@jimp/plugin-color": "1.6.0",
+                "@jimp/plugin-hash": "1.6.0",
+                "@jimp/types": "1.6.0",
+                "@jimp/utils": "1.6.0",
                 "zod": "^3.23.8"
             },
             "engines": {
@@ -3558,9 +3558,9 @@
             }
         },
         "node_modules/@jimp/types": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/@jimp/types/-/types-1.3.0.tgz",
-            "integrity": "sha512-K4RaTmDTqZqbjjwOtxzVH9QyCgQBukXjKOmdgNuCmu7ugrpeTeWV7SvrwZZPhTt31lmja8A3a3Dw1ScVZdtmyA==",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/@jimp/types/-/types-1.6.0.tgz",
+            "integrity": "sha512-7UfRsiKo5GZTAATxm2qQ7jqmUXP0DxTArztllTcYdyw6Xi5oT4RaoXynVtCD4UyLK5gJgkZJcwonoijrhYFKfg==",
             "dependencies": {
                 "zod": "^3.23.8"
             },
@@ -3569,11 +3569,11 @@
             }
         },
         "node_modules/@jimp/utils": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-1.3.0.tgz",
-            "integrity": "sha512-QIye6IWJaQ3Q9+6rxgQiFI1I7MwrQZYxlhYhPolJv+BfCXBT8XWJymV8J75vlnjvz3kN2AMXAEU7c7w0h2Tz3Q==",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-1.6.0.tgz",
+            "integrity": "sha512-gqFTGEosKbOkYF/WFj26jMHOI5OH2jeP1MmC/zbK6BF6VJBf8rIC5898dPfSzZEbSA0wbbV5slbntWVc5PKLFA==",
             "dependencies": {
-                "@jimp/types": "1.3.0",
+                "@jimp/types": "1.6.0",
                 "tinycolor2": "^1.6.0"
             },
             "engines": {
@@ -3881,20 +3881,20 @@
             }
         },
         "node_modules/@mui/core-downloads-tracker": {
-            "version": "6.0.2",
-            "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.0.2.tgz",
-            "integrity": "sha512-Cg68oOlAfbJgMgvbCwcX3Y3HdygCl6X1nREYTdEWcEKUQhNarrC45Cc35mP+zA7p3ZXE/7FLiaTCCgwuSoef/Q==",
+            "version": "6.1.0",
+            "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.1.0.tgz",
+            "integrity": "sha512-covEnIn/2er5YdtuukDRA52kmARhKrHjOvPsyTFMQApZdrTBI4h8jbEy2mxZqwMwcAFS9coonQXnEZKL1rUNdQ==",
             "funding": {
                 "type": "opencollective",
                 "url": "https://opencollective.com/mui-org"
             }
         },
         "node_modules/@mui/icons-material": {
-            "version": "6.0.2",
-            "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.0.2.tgz",
-            "integrity": "sha512-WaTPSvKcx8X7NdWAHzJWDZv+YXvK0MUY8+JI/r4/q2GgIa5RW+n4+08CGX6jB7sWhU1R3zy28NfsDUwwQjOThw==",
+            "version": "6.1.0",
+            "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.1.0.tgz",
+            "integrity": "sha512-HxfB0jxwiMTYMN8gAnYn3avbF1aDrqBEuGIj6JDQ3YkLl650E1Wy8AIhwwyP47wdrv0at9aAR0iOO6VLb74A9w==",
             "dependencies": {
-                "@babel/runtime": "^7.25.0"
+                "@babel/runtime": "^7.25.6"
             },
             "engines": {
                 "node": ">=14.0.0"
@@ -3904,7 +3904,7 @@
                 "url": "https://opencollective.com/mui-org"
             },
             "peerDependencies": {
-                "@mui/material": "^6.0.2",
+                "@mui/material": "^6.1.0",
                 "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
                 "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
             },
@@ -3915,15 +3915,15 @@
             }
         },
         "node_modules/@mui/material": {
-            "version": "6.0.2",
-            "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.0.2.tgz",
-            "integrity": "sha512-KrnkJFSyhsAh8V30DNUbWyRyxMi4ZHjFg1ikQGx+mUAIffFTYIEx9Q+Kxd3vCT0FUFGOmbsuh6F6yRhpybsjkg==",
+            "version": "6.1.0",
+            "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.1.0.tgz",
+            "integrity": "sha512-4MJ46vmy1xbm8x+ZdRcWm8jEMMowdS8pYlhKQzg/qoKhOcLhImZvf2Jn6z9Dj6gl+lY+C/0MxaHF/avAAGys3Q==",
             "dependencies": {
-                "@babel/runtime": "^7.25.0",
-                "@mui/core-downloads-tracker": "^6.0.2",
-                "@mui/system": "^6.0.2",
+                "@babel/runtime": "^7.25.6",
+                "@mui/core-downloads-tracker": "^6.1.0",
+                "@mui/system": "^6.1.0",
                 "@mui/types": "^7.2.16",
-                "@mui/utils": "^6.0.2",
+                "@mui/utils": "^6.1.0",
                 "@popperjs/core": "^2.11.8",
                 "@types/react-transition-group": "^4.4.11",
                 "clsx": "^2.1.1",
@@ -3942,7 +3942,7 @@
             "peerDependencies": {
                 "@emotion/react": "^11.5.0",
                 "@emotion/styled": "^11.3.0",
-                "@mui/material-pigment-css": "^6.0.2",
+                "@mui/material-pigment-css": "^6.1.0",
                 "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
                 "react": "^17.0.0 || ^18.0.0 || ^19.0.0",
                 "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
@@ -3963,12 +3963,12 @@
             }
         },
         "node_modules/@mui/private-theming": {
-            "version": "6.0.2",
-            "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.0.2.tgz",
-            "integrity": "sha512-emddFcRhA0hPGVIwIbW5g0V8vtCgw2g/H/A7jTdGe7dpCWEPpp6jPIXRRKcEUWgmg91R6rBNfV+LFHxBxmZXOQ==",
+            "version": "6.1.0",
+            "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.1.0.tgz",
+            "integrity": "sha512-+L5qccs4gwsR0r1dgjqhN24QEQRkqIbfOdxILyMbMkuI50x6wNyt9XrV+J3WtjtZTMGJCrUa5VmZBE6OEPGPWA==",
             "dependencies": {
-                "@babel/runtime": "^7.25.0",
-                "@mui/utils": "^6.0.2",
+                "@babel/runtime": "^7.25.6",
+                "@mui/utils": "^6.1.0",
                 "prop-types": "^15.8.1"
             },
             "engines": {
@@ -3989,12 +3989,13 @@
             }
         },
         "node_modules/@mui/styled-engine": {
-            "version": "6.0.2",
-            "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.0.2.tgz",
-            "integrity": "sha512-qd3Vlhted0SYVGotnCfVNcxff7vW2WN0fclbAexff60NeNS1qs/H/CImHEHUBiUGeNWMPRochbN6VF1arQ7/jA==",
+            "version": "6.1.0",
+            "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.1.0.tgz",
+            "integrity": "sha512-MZ+vtaCkjamrT41+b0Er9OMenjAtP/32+L6fARL9/+BZKuV2QbR3q3TmavT2x0NhDu35IM03s4yKqj32Ziqnyg==",
             "dependencies": {
-                "@babel/runtime": "^7.25.0",
+                "@babel/runtime": "^7.25.6",
                 "@emotion/cache": "^11.13.1",
+                "@emotion/sheet": "^1.4.0",
                 "csstype": "^3.1.3",
                 "prop-types": "^15.8.1"
             },
@@ -4020,15 +4021,15 @@
             }
         },
         "node_modules/@mui/system": {
-            "version": "6.0.2",
-            "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.0.2.tgz",
-            "integrity": "sha512-AZv1/C4PuHgWFTA8YraIzl3FTVLdRz0RIMRwEADWZBdIhnuTHS/4+r8qE9+3CcpTHg1WsEu8btaO3AhQahSM9A==",
+            "version": "6.1.0",
+            "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.1.0.tgz",
+            "integrity": "sha512-NumkGDqT6EdXfcoFLYQ+M4XlTW5hH3+aK48xAbRqKPXJfxl36CBt4DLduw/Voa5dcayGus9T6jm1AwU2hoJ5hQ==",
             "dependencies": {
-                "@babel/runtime": "^7.25.0",
-                "@mui/private-theming": "^6.0.2",
-                "@mui/styled-engine": "^6.0.2",
+                "@babel/runtime": "^7.25.6",
+                "@mui/private-theming": "^6.1.0",
+                "@mui/styled-engine": "^6.1.0",
                 "@mui/types": "^7.2.16",
-                "@mui/utils": "^6.0.2",
+                "@mui/utils": "^6.1.0",
                 "clsx": "^2.1.1",
                 "csstype": "^3.1.3",
                 "prop-types": "^15.8.1"
@@ -4072,11 +4073,11 @@
             }
         },
         "node_modules/@mui/utils": {
-            "version": "6.0.2",
-            "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.0.2.tgz",
-            "integrity": "sha512-TeFrYsxcmeoDSlkoPhX+LjIuuqC5Pyj+xz2kRceKCkUpwMNTEeVOfowXDPe+mboZwmpJ5ZxP4eiAgQMdeEasjg==",
+            "version": "6.1.0",
+            "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.1.0.tgz",
+            "integrity": "sha512-oT8ZzMISRUhTVpdbYzY0CgrCBb3t/YEdcaM13tUnuTjZ15pdA6g5lx15ZJUdgYXV6PbJdw7tDQgMEr4uXK5TXQ==",
             "dependencies": {
-                "@babel/runtime": "^7.25.0",
+                "@babel/runtime": "^7.25.6",
                 "@mui/types": "^7.2.16",
                 "@types/prop-types": "^15.7.12",
                 "clsx": "^2.1.1",
@@ -7210,9 +7211,9 @@
             "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="
         },
         "node_modules/@sindresorhus/is": {
-            "version": "7.0.0",
-            "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.0.0.tgz",
-            "integrity": "sha512-WDTlVTyvFivSOuyvMeedzg2hdoBLZ3f1uNVuEida2Rl9BrfjrIRjWA/VZIrMRLvSwJYCAlCRA3usDt1THytxWQ==",
+            "version": "7.0.1",
+            "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.0.1.tgz",
+            "integrity": "sha512-QWLl2P+rsCJeofkDNIT3WFmb6NrRud1SUYW8dIhXK/46XFV8Q/g7Bsvib0Askb0reRLe+WYPeeE+l5cH7SlkuQ==",
             "engines": {
                 "node": ">=18"
             },
@@ -9579,9 +9580,9 @@
             "dev": true
         },
         "node_modules/@types/mocha": {
-            "version": "10.0.7",
-            "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.7.tgz",
-            "integrity": "sha512-GN8yJ1mNTcFcah/wKEFIJckJx9iJLoMSzWcfRRuxz/Jk+U6KQNnml+etbtxFK8lPjzOw3zp4Ha/kjSst9fsHYw==",
+            "version": "10.0.8",
+            "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.8.tgz",
+            "integrity": "sha512-HfMcUmy9hTMJh66VNcmeC9iVErIZJli2bszuXc6julh5YGuRb/W5OnkHjwLNYdFlMis0sY3If5SEAp+PktdJjw==",
             "dev": true
         },
         "node_modules/@types/ms": {
@@ -9590,9 +9591,9 @@
             "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g=="
         },
         "node_modules/@types/node": {
-            "version": "22.5.4",
-            "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz",
-            "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==",
+            "version": "22.5.5",
+            "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.5.tgz",
+            "integrity": "sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA==",
             "dependencies": {
                 "undici-types": "~6.19.2"
             }
@@ -9616,9 +9617,9 @@
             }
         },
         "node_modules/@types/nodemailer": {
-            "version": "6.4.15",
-            "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.15.tgz",
-            "integrity": "sha512-0EBJxawVNjPkng1zm2vopRctuWVCxk34JcIlRuXSf54habUWdz1FB7wHDqOqvDa8Mtpt0Q3LTXQkAs2LNyK5jQ==",
+            "version": "6.4.16",
+            "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.16.tgz",
+            "integrity": "sha512-uz6hN6Pp0upXMcilM61CoKyjT7sskBoOWpptkjjJp8jIMlTdc3xG01U7proKkXzruMS4hS0zqtHNkNPFB20rKQ==",
             "dev": true,
             "dependencies": {
                 "@types/node": "*"
@@ -9706,14 +9707,15 @@
             "integrity": "sha512-+gbBHbNCVGGYw1S9lAIIvrHW47UYOhMIFUsJcMkMrzy1Jf0vulBN3XQIjPgnoOXveMuHnF3b57fXROnY/Or7eg=="
         },
         "node_modules/@types/prop-types": {
-            "version": "15.7.12",
-            "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz",
-            "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q=="
+            "version": "15.7.13",
+            "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz",
+            "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA=="
         },
         "node_modules/@types/qs": {
-            "version": "6.9.15",
-            "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz",
-            "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg=="
+            "version": "6.9.16",
+            "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz",
+            "integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==",
+            "dev": true
         },
         "node_modules/@types/range-parser": {
             "version": "1.2.7",
@@ -9731,9 +9733,9 @@
             }
         },
         "node_modules/@types/react": {
-            "version": "18.3.5",
-            "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.5.tgz",
-            "integrity": "sha512-WeqMfGJLGuLCqHGYRGHxnKrXcTitc6L/nBUWfWPcTarG3t9PsquqUMuVeXZeca+mglY4Vo5GZjCi0A3Or2lnxA==",
+            "version": "18.3.7",
+            "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.7.tgz",
+            "integrity": "sha512-KUnDCJF5+AiZd8owLIeVHqmW9yM4sqmDVf2JRJiBMFkGvkoZ4/WyV2lL4zVsoinmRS/W3FeEdZLEWFRofnT2FQ==",
             "dependencies": {
                 "@types/prop-types": "*",
                 "csstype": "^3.0.2"
@@ -9991,9 +9993,9 @@
             "dev": true
         },
         "node_modules/@types/web": {
-            "version": "0.0.159",
-            "resolved": "https://registry.npmjs.org/@types/web/-/web-0.0.159.tgz",
-            "integrity": "sha512-BHPaU+yHqHOrua8iFksPmgLCXEt1LE/2sPB+MPTRuxDFm4z6gBgmDiXUCGleyHiOLT6R+fklgR99hq/iFGVO1w=="
+            "version": "0.0.163",
+            "resolved": "https://registry.npmjs.org/@types/web/-/web-0.0.163.tgz",
+            "integrity": "sha512-5Pg2gKfulo186wFnv+YXx0luJGWQ94cCY2/Dy8lU5WAE50FdBoOK45uBbp8FceOSpLJ4UW3dmTW5tvsN9uuX7A=="
         },
         "node_modules/@types/webgl-ext": {
             "version": "0.0.30",
@@ -10303,55 +10305,55 @@
             "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ=="
         },
         "node_modules/@vue/compiler-core": {
-            "version": "3.5.1",
-            "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.1.tgz",
-            "integrity": "sha512-WdjF+NSgFYdWttHevHw5uaJFtKPalhmxhlu2uREj8cLP0uyKKIR60/JvSZNTp0x+NSd63iTiORQTx3+tt55NWQ==",
+            "version": "3.5.6",
+            "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.6.tgz",
+            "integrity": "sha512-r+gNu6K4lrvaQLQGmf+1gc41p3FO2OUJyWmNqaIITaJU6YFiV5PtQSFZt8jfztYyARwqhoCayjprC7KMvT3nRA==",
             "dependencies": {
                 "@babel/parser": "^7.25.3",
-                "@vue/shared": "3.5.1",
+                "@vue/shared": "3.5.6",
                 "entities": "^4.5.0",
                 "estree-walker": "^2.0.2",
                 "source-map-js": "^1.2.0"
             }
         },
         "node_modules/@vue/compiler-dom": {
-            "version": "3.5.1",
-            "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.1.tgz",
-            "integrity": "sha512-Ao23fB1lINo18HLCbJVApvzd9OQe8MgmQSgyY5+umbWj2w92w9KykVmJ4Iv2US5nak3ixc2B+7Km7JTNhQ8kSQ==",
+            "version": "3.5.6",
+            "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.6.tgz",
+            "integrity": "sha512-xRXqxDrIqK8v8sSScpistyYH0qYqxakpsIvqMD2e5sV/PXQ1mTwtXp4k42yHK06KXxKSmitop9e45Ui/3BrTEw==",
             "dependencies": {
-                "@vue/compiler-core": "3.5.1",
-                "@vue/shared": "3.5.1"
+                "@vue/compiler-core": "3.5.6",
+                "@vue/shared": "3.5.6"
             }
         },
         "node_modules/@vue/compiler-sfc": {
-            "version": "3.5.1",
-            "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.1.tgz",
-            "integrity": "sha512-DFizMNH8eDglLhlfwJ0+ciBsztaYe3fY/zcZjrqL1ljXvUw/UpC84M1d7HpBTCW68SNqZyIxrs1XWmf+73Y65w==",
+            "version": "3.5.6",
+            "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.6.tgz",
+            "integrity": "sha512-pjWJ8Kj9TDHlbF5LywjVso+BIxCY5wVOLhkEXRhuCHDxPFIeX1zaFefKs8RYoHvkSMqRWt93a0f2gNJVJixHwg==",
             "dependencies": {
                 "@babel/parser": "^7.25.3",
-                "@vue/compiler-core": "3.5.1",
-                "@vue/compiler-dom": "3.5.1",
-                "@vue/compiler-ssr": "3.5.1",
-                "@vue/shared": "3.5.1",
+                "@vue/compiler-core": "3.5.6",
+                "@vue/compiler-dom": "3.5.6",
+                "@vue/compiler-ssr": "3.5.6",
+                "@vue/shared": "3.5.6",
                 "estree-walker": "^2.0.2",
                 "magic-string": "^0.30.11",
-                "postcss": "^8.4.44",
+                "postcss": "^8.4.47",
                 "source-map-js": "^1.2.0"
             }
         },
         "node_modules/@vue/compiler-ssr": {
-            "version": "3.5.1",
-            "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.1.tgz",
-            "integrity": "sha512-C1hpSHQgRM8bg+5XWWD7CkFaVpSn9wZHCLRd10AmxqrH17d4EMP6+XcZpwBOM7H1jeStU5naEapZZWX0kso1tQ==",
+            "version": "3.5.6",
+            "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.6.tgz",
+            "integrity": "sha512-VpWbaZrEOCqnmqjE83xdwegtr5qO/2OPUC6veWgvNqTJ3bYysz6vY3VqMuOijubuUYPRpG3OOKIh9TD0Stxb9A==",
             "dependencies": {
-                "@vue/compiler-dom": "3.5.1",
-                "@vue/shared": "3.5.1"
+                "@vue/compiler-dom": "3.5.6",
+                "@vue/shared": "3.5.6"
             }
         },
         "node_modules/@vue/shared": {
-            "version": "3.5.1",
-            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.1.tgz",
-            "integrity": "sha512-NdcTRoO4KuW2RSFgpE2c+E/R/ZHaRzWPxAGxhmxZaaqLh6nYCXx7lc9a88ioqOCxCaV2SFJmujkxbUScW7dNsQ=="
+            "version": "3.5.6",
+            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.6.tgz",
+            "integrity": "sha512-eidH0HInnL39z6wAt6SFIwBrvGOpDWsDxlw3rCgo1B+CQ1781WzQUSU3YjxgdkcJo9Q8S6LmXTkvI+cLHGkQfA=="
         },
         "node_modules/@webassemblyjs/ast": {
             "version": "1.12.1",
@@ -10610,9 +10612,9 @@
             }
         },
         "node_modules/acorn-walk": {
-            "version": "8.3.3",
-            "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz",
-            "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==",
+            "version": "8.3.4",
+            "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
+            "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
             "dev": true,
             "dependencies": {
                 "acorn": "^8.11.0"
@@ -11168,9 +11170,9 @@
             }
         },
         "node_modules/babel-loader": {
-            "version": "9.1.3",
-            "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.3.tgz",
-            "integrity": "sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw==",
+            "version": "9.2.1",
+            "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.2.1.tgz",
+            "integrity": "sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==",
             "dependencies": {
                 "find-cache-dir": "^4.0.0",
                 "schema-utils": "^4.0.0"
@@ -11442,9 +11444,9 @@
             "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
         },
         "node_modules/body-parser": {
-            "version": "1.20.2",
-            "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
-            "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
+            "version": "1.20.3",
+            "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
+            "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
             "dependencies": {
                 "bytes": "3.1.2",
                 "content-type": "~1.0.5",
@@ -11454,7 +11456,7 @@
                 "http-errors": "2.0.0",
                 "iconv-lite": "0.4.24",
                 "on-finished": "2.4.1",
-                "qs": "6.11.0",
+                "qs": "6.13.0",
                 "raw-body": "2.5.2",
                 "type-is": "~1.6.18",
                 "unpipe": "1.0.0"
@@ -15064,9 +15066,9 @@
             }
         },
         "node_modules/caniuse-lite": {
-            "version": "1.0.30001655",
-            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001655.tgz",
-            "integrity": "sha512-jRGVy3iSGO5Uutn2owlb5gR6qsGngTw9ZTb4ali9f3glshcNmJ2noam4Mo9zia5P9Dk3jNNydy7vQjuE5dQmfg==",
+            "version": "1.0.30001660",
+            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001660.tgz",
+            "integrity": "sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg==",
             "funding": [
                 {
                     "type": "opencollective",
@@ -16183,23 +16185,17 @@
             }
         },
         "node_modules/cssstyle": {
-            "version": "4.0.1",
-            "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.0.1.tgz",
-            "integrity": "sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==",
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.1.0.tgz",
+            "integrity": "sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==",
             "dev": true,
             "dependencies": {
-                "rrweb-cssom": "^0.6.0"
+                "rrweb-cssom": "^0.7.1"
             },
             "engines": {
                 "node": ">=18"
             }
         },
-        "node_modules/cssstyle/node_modules/rrweb-cssom": {
-            "version": "0.6.0",
-            "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz",
-            "integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==",
-            "dev": true
-        },
         "node_modules/csstype": {
             "version": "3.1.3",
             "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
@@ -16835,11 +16831,11 @@
             "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg=="
         },
         "node_modules/debug": {
-            "version": "4.3.6",
-            "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz",
-            "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==",
+            "version": "4.3.7",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
+            "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
             "dependencies": {
-                "ms": "2.1.2"
+                "ms": "^2.1.3"
             },
             "engines": {
                 "node": ">=6.0"
@@ -17376,9 +17372,9 @@
             "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
         },
         "node_modules/electron-to-chromium": {
-            "version": "1.5.13",
-            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz",
-            "integrity": "sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q=="
+            "version": "1.5.24",
+            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.24.tgz",
+            "integrity": "sha512-0x0wLCmpdKFCi9ulhvYZebgcPmHTkFVUfU2wzDykadkslKwT4oAmDTHEKLnlrDsMGZe4B+ksn8quZfZjYsBetA=="
         },
         "node_modules/emoji-regex": {
             "version": "9.2.2",
@@ -17394,9 +17390,9 @@
             }
         },
         "node_modules/encodeurl": {
-            "version": "1.0.2",
-            "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
-            "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+            "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
             "engines": {
                 "node": ">= 0.8"
             }
@@ -17505,9 +17501,9 @@
             }
         },
         "node_modules/envinfo": {
-            "version": "7.13.0",
-            "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.13.0.tgz",
-            "integrity": "sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q==",
+            "version": "7.14.0",
+            "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.14.0.tgz",
+            "integrity": "sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg==",
             "bin": {
                 "envinfo": "dist/cli.js"
             },
@@ -18135,36 +18131,36 @@
             }
         },
         "node_modules/express": {
-            "version": "4.19.2",
-            "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
-            "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
+            "version": "4.21.0",
+            "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz",
+            "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==",
             "dependencies": {
                 "accepts": "~1.3.8",
                 "array-flatten": "1.1.1",
-                "body-parser": "1.20.2",
+                "body-parser": "1.20.3",
                 "content-disposition": "0.5.4",
                 "content-type": "~1.0.4",
                 "cookie": "0.6.0",
                 "cookie-signature": "1.0.6",
                 "debug": "2.6.9",
                 "depd": "2.0.0",
-                "encodeurl": "~1.0.2",
+                "encodeurl": "~2.0.0",
                 "escape-html": "~1.0.3",
                 "etag": "~1.8.1",
-                "finalhandler": "1.2.0",
+                "finalhandler": "1.3.1",
                 "fresh": "0.5.2",
                 "http-errors": "2.0.0",
-                "merge-descriptors": "1.0.1",
+                "merge-descriptors": "1.0.3",
                 "methods": "~1.1.2",
                 "on-finished": "2.4.1",
                 "parseurl": "~1.3.3",
-                "path-to-regexp": "0.1.7",
+                "path-to-regexp": "0.1.10",
                 "proxy-addr": "~2.0.7",
-                "qs": "6.11.0",
+                "qs": "6.13.0",
                 "range-parser": "~1.2.1",
                 "safe-buffer": "5.2.1",
-                "send": "0.18.0",
-                "serve-static": "1.15.0",
+                "send": "0.19.0",
+                "serve-static": "1.16.2",
                 "setprototypeof": "1.2.0",
                 "statuses": "2.0.1",
                 "type-is": "~1.6.18",
@@ -18527,12 +18523,12 @@
             }
         },
         "node_modules/finalhandler": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
-            "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
+            "version": "1.3.1",
+            "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
+            "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
             "dependencies": {
                 "debug": "2.6.9",
-                "encodeurl": "~1.0.2",
+                "encodeurl": "~2.0.0",
                 "escape-html": "~1.0.3",
                 "on-finished": "2.4.1",
                 "parseurl": "~1.3.3",
@@ -18653,9 +18649,9 @@
             "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw=="
         },
         "node_modules/flexlayout-react": {
-            "version": "0.7.15",
-            "resolved": "https://registry.npmjs.org/flexlayout-react/-/flexlayout-react-0.7.15.tgz",
-            "integrity": "sha512-ydTMdEoQO5BniylxVkSxa59rEY0+96lqqRII+QK+yq6028eHywPuxZawt4g45y5pMb9ptP4N9HPAQXAFsxwowQ==",
+            "version": "0.8.0",
+            "resolved": "https://registry.npmjs.org/flexlayout-react/-/flexlayout-react-0.8.0.tgz",
+            "integrity": "sha512-nTkEOxVw438/6ugzKb0nBFRzJkWPYOgx8laGhDmTsXMuJQMEEC4dxyRSswcjt8IWsCKl8zSQHlsxSG+PrdJENQ==",
             "peerDependencies": {
                 "react": "^18.0.0",
                 "react-dom": "^18.0.0"
@@ -18690,9 +18686,9 @@
             }
         },
         "node_modules/follow-redirects": {
-            "version": "1.15.8",
-            "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.8.tgz",
-            "integrity": "sha512-xgrmBhBToVKay1q2Tao5LI26B83UhrB/vM1avwVSDzt8rx3rO6AizBAaF46EgksTVr+rFTQaqZZ9MVBfUe4nig==",
+            "version": "1.15.9",
+            "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
+            "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
             "funding": [
                 {
                     "type": "individual",
@@ -19591,9 +19587,9 @@
             }
         },
         "node_modules/googleapis": {
-            "version": "142.0.0",
-            "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-142.0.0.tgz",
-            "integrity": "sha512-LsU1ynez4/KNPwnFMSDI93pBEsETNdQPCrT3kz2qgiNg5H2pW4dKW+1VmENMkZ4u9lMxA89nnXD3nqWBJ0rruQ==",
+            "version": "144.0.0",
+            "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-144.0.0.tgz",
+            "integrity": "sha512-ELcWOXtJxjPX4vsKMh+7V+jZvgPwYMlEhQFiu2sa9Qmt5veX8nwXPksOWGGN6Zk4xCiLygUyaz7xGtcMO+Onxw==",
             "dependencies": {
                 "google-auth-library": "^9.0.0",
                 "googleapis-common": "^7.0.0"
@@ -19833,9 +19829,9 @@
             }
         },
         "node_modules/hast-util-from-html": {
-            "version": "2.0.2",
-            "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.2.tgz",
-            "integrity": "sha512-HwOHwxdt2zC5KQ/CNoybBntRook2zJvfZE/u5/Ap7aLPe22bDqen7KwGkOqOyzL5zIqKwiYX/OTtE0FWgr6XXA==",
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz",
+            "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==",
             "dependencies": {
                 "@types/hast": "^3.0.0",
                 "devlop": "^1.1.0",
@@ -20805,9 +20801,9 @@
             "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
         },
         "node_modules/inline-style-parser": {
-            "version": "0.2.3",
-            "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.3.tgz",
-            "integrity": "sha512-qlD8YNDqyTKTyuITrDOffsl6Tdhv+UC4hcdAVuQsK4IMQ99nSgd1MIA/Q+jQYoh9r3hVUXhYh7urSRmXPkW04g=="
+            "version": "0.2.4",
+            "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz",
+            "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q=="
         },
         "node_modules/inspect-function": {
             "version": "0.3.4",
@@ -21775,37 +21771,37 @@
             }
         },
         "node_modules/jimp": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/jimp/-/jimp-1.3.0.tgz",
-            "integrity": "sha512-eJnVMuqDQ545taNLp13gVZynnoOvE0xZ2Oti9alkld47dNhmFHBmFTBMTYaZr7zceGTf54RGdr7C4d2WUNwc0g==",
-            "dependencies": {
-                "@jimp/core": "1.3.0",
-                "@jimp/diff": "1.3.0",
-                "@jimp/js-bmp": "1.3.0",
-                "@jimp/js-gif": "1.3.0",
-                "@jimp/js-jpeg": "1.3.0",
-                "@jimp/js-png": "1.3.0",
-                "@jimp/js-tiff": "1.3.0",
-                "@jimp/plugin-blit": "1.3.0",
-                "@jimp/plugin-blur": "1.3.0",
-                "@jimp/plugin-circle": "1.3.0",
-                "@jimp/plugin-color": "1.3.0",
-                "@jimp/plugin-contain": "1.3.0",
-                "@jimp/plugin-cover": "1.3.0",
-                "@jimp/plugin-crop": "1.3.0",
-                "@jimp/plugin-displace": "1.3.0",
-                "@jimp/plugin-dither": "1.3.0",
-                "@jimp/plugin-fisheye": "1.3.0",
-                "@jimp/plugin-flip": "1.3.0",
-                "@jimp/plugin-hash": "1.3.0",
-                "@jimp/plugin-mask": "1.3.0",
-                "@jimp/plugin-print": "1.3.0",
-                "@jimp/plugin-quantize": "1.3.0",
-                "@jimp/plugin-resize": "1.3.0",
-                "@jimp/plugin-rotate": "1.3.0",
-                "@jimp/plugin-threshold": "1.3.0",
-                "@jimp/types": "1.3.0",
-                "@jimp/utils": "1.3.0"
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/jimp/-/jimp-1.6.0.tgz",
+            "integrity": "sha512-YcwCHw1kiqEeI5xRpDlPPBGL2EOpBKLwO4yIBJcXWHPj5PnA5urGq0jbyhM5KoNpypQ6VboSoxc9D8HyfvngSg==",
+            "dependencies": {
+                "@jimp/core": "1.6.0",
+                "@jimp/diff": "1.6.0",
+                "@jimp/js-bmp": "1.6.0",
+                "@jimp/js-gif": "1.6.0",
+                "@jimp/js-jpeg": "1.6.0",
+                "@jimp/js-png": "1.6.0",
+                "@jimp/js-tiff": "1.6.0",
+                "@jimp/plugin-blit": "1.6.0",
+                "@jimp/plugin-blur": "1.6.0",
+                "@jimp/plugin-circle": "1.6.0",
+                "@jimp/plugin-color": "1.6.0",
+                "@jimp/plugin-contain": "1.6.0",
+                "@jimp/plugin-cover": "1.6.0",
+                "@jimp/plugin-crop": "1.6.0",
+                "@jimp/plugin-displace": "1.6.0",
+                "@jimp/plugin-dither": "1.6.0",
+                "@jimp/plugin-fisheye": "1.6.0",
+                "@jimp/plugin-flip": "1.6.0",
+                "@jimp/plugin-hash": "1.6.0",
+                "@jimp/plugin-mask": "1.6.0",
+                "@jimp/plugin-print": "1.6.0",
+                "@jimp/plugin-quantize": "1.6.0",
+                "@jimp/plugin-resize": "1.6.0",
+                "@jimp/plugin-rotate": "1.6.0",
+                "@jimp/plugin-threshold": "1.6.0",
+                "@jimp/types": "1.6.0",
+                "@jimp/utils": "1.6.0"
             },
             "engines": {
                 "node": ">=18"
@@ -21879,9 +21875,9 @@
             "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg=="
         },
         "node_modules/js-datepicker": {
-            "version": "5.18.2",
-            "resolved": "https://registry.npmjs.org/js-datepicker/-/js-datepicker-5.18.2.tgz",
-            "integrity": "sha512-lBh5tIwb1ewDVlNHuwczoLiMhTFWnGYuHSA7ONAPKyMeofIFZDFGeOZ71UTY/Mk2evJJt+L66ec/RiXUy8XzEg=="
+            "version": "5.18.3",
+            "resolved": "https://registry.npmjs.org/js-datepicker/-/js-datepicker-5.18.3.tgz",
+            "integrity": "sha512-M1+UKeRkLT0p04JhnSpNugveFSfy6VfEeNTyAyFef6unKIqQx4oQqx66lsBi0FRKHb4HtQsmJ8V4L4SPj1lbRQ=="
         },
         "node_modules/js-stringify": {
             "version": "1.0.2",
@@ -22293,9 +22289,9 @@
             }
         },
         "node_modules/launch-editor": {
-            "version": "2.8.2",
-            "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.8.2.tgz",
-            "integrity": "sha512-eF5slEUZXmi6WvFzI3dYcv+hA24/iKnROf24HztcURJpSz9RBmBgz5cNCVOeguouf1llrwy6Yctl4C4HM+xI8g==",
+            "version": "2.9.1",
+            "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.9.1.tgz",
+            "integrity": "sha512-Gcnl4Bd+hRO9P9icCP/RVVT2o8SFlPXofuCxvA2SaZuH45whSvf5p8x5oih5ftLiVhEI4sp5xDY+R+b3zJBh5w==",
             "dev": true,
             "dependencies": {
                 "picocolors": "^1.0.0",
@@ -23024,9 +23020,9 @@
             }
         },
         "node_modules/mdast-util-mdx-expression": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.0.tgz",
-            "integrity": "sha512-fGCu8eWdKUKNu5mohVGkhBXCXGnOTLuFqOvGMvdikr+J1w7lDJgxThOKpwRWzzbyXAU2hhSwsmssOY4yTokluw==",
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz",
+            "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==",
             "dependencies": {
                 "@types/estree-jsx": "^1.0.0",
                 "@types/hast": "^3.0.0",
@@ -23187,9 +23183,12 @@
             }
         },
         "node_modules/merge-descriptors": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
-            "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
+            "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
         },
         "node_modules/merge-stream": {
             "version": "2.0.0",
@@ -23206,9 +23205,9 @@
             }
         },
         "node_modules/mermaid": {
-            "version": "11.1.0",
-            "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.1.0.tgz",
-            "integrity": "sha512-ICexrwPRzU1USFcpAdrVVGjCwEajD+iAwu2LVHi59D6VbXmFhwfB9TbCL3sA6NBR1tl5qUjQSAOdc9lOKlXnEw==",
+            "version": "11.2.1",
+            "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.2.1.tgz",
+            "integrity": "sha512-F8TEaLVVyxTUmvKswVFyOkjPrlJA5h5vNR1f7ZnSWSpqxgEZG1hggtn/QCa7znC28bhlcrNh10qYaIiill7q4A==",
             "dependencies": {
                 "@braintree/sanitize-url": "^7.0.1",
                 "@iconify/utils": "^2.1.32",
@@ -23963,9 +23962,9 @@
             "integrity": "sha512-yc0LhH6tItlvfLBugVUEtgawwFU2sIe+cSdmRJJCTMZ5GEJyLxNyC/NIOAOGk67Fa8GNpOttO3Xz/1bHpXFD/g=="
         },
         "node_modules/mobx": {
-            "version": "6.13.1",
-            "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.13.1.tgz",
-            "integrity": "sha512-ekLRxgjWJr8hVxj9ZKuClPwM/iHckx3euIJ3Np7zLVNtqJvfbbq7l370W/98C8EabdQ1pB5Jd3BbDWxJPNnaOg==",
+            "version": "6.13.2",
+            "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.13.2.tgz",
+            "integrity": "sha512-GIubI2qf+P6lG6rSEG0T2pg3jV9/0+O0ncF09+0umRe75+Cbnh1KNLM1GvbTY9RSc7QuU+LcPNZfxDY8B+3XRg==",
             "funding": {
                 "type": "opencollective",
                 "url": "https://opencollective.com/mobx"
@@ -24121,12 +24120,6 @@
                 "node": ">=10"
             }
         },
-        "node_modules/mocha/node_modules/ms": {
-            "version": "2.1.3",
-            "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
-            "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
-            "dev": true
-        },
         "node_modules/mocha/node_modules/supports-color": {
             "version": "8.1.1",
             "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
@@ -24143,9 +24136,9 @@
             }
         },
         "node_modules/mongodb": {
-            "version": "6.8.0",
-            "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.8.0.tgz",
-            "integrity": "sha512-HGQ9NWDle5WvwMnrvUxsFYPd3JEbqD3RgABHBQRuoCEND0qzhsd0iH5ypHsf1eJ+sXmvmyKpP+FLOKY8Il7jMw==",
+            "version": "6.9.0",
+            "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.9.0.tgz",
+            "integrity": "sha512-UMopBVx1LmEUbW/QE0Hw18u583PEDVQmUmVzzBRH0o/xtE9DBRA5ZYLOjpLIa03i8FXjzvQECJcqoMvCXftTUA==",
             "dependencies": {
                 "@mongodb-js/saslprep": "^1.1.5",
                 "bson": "^6.7.0",
@@ -24220,9 +24213,9 @@
             }
         },
         "node_modules/mongoose": {
-            "version": "8.6.1",
-            "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.6.1.tgz",
-            "integrity": "sha512-dppGcYqvsdg+VcnqXR5b467V4a+iNhmvkfYNpEPi6AjaUxnz6ioEDmrMLOi+sOWjvoHapuwPOigV4f2l7HC6ag==",
+            "version": "8.6.3",
+            "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.6.3.tgz",
+            "integrity": "sha512-++yRmm7hjMbqVA/8WeiygTnEfrFbiy+OBjQi49GFJIvCQuSYE56myyQWo4j5hbpcHjhHQU8NukMNGTwAWFWjIw==",
             "dependencies": {
                 "bson": "^6.7.0",
                 "kareem": "2.6.3",
@@ -24240,10 +24233,50 @@
                 "url": "https://opencollective.com/mongoose"
             }
         },
-        "node_modules/mongoose/node_modules/ms": {
-            "version": "2.1.3",
-            "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
-            "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
+        "node_modules/mongoose/node_modules/mongodb": {
+            "version": "6.8.0",
+            "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.8.0.tgz",
+            "integrity": "sha512-HGQ9NWDle5WvwMnrvUxsFYPd3JEbqD3RgABHBQRuoCEND0qzhsd0iH5ypHsf1eJ+sXmvmyKpP+FLOKY8Il7jMw==",
+            "dependencies": {
+                "@mongodb-js/saslprep": "^1.1.5",
+                "bson": "^6.7.0",
+                "mongodb-connection-string-url": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=16.20.1"
+            },
+            "peerDependencies": {
+                "@aws-sdk/credential-providers": "^3.188.0",
+                "@mongodb-js/zstd": "^1.1.0",
+                "gcp-metadata": "^5.2.0",
+                "kerberos": "^2.0.1",
+                "mongodb-client-encryption": ">=6.0.0 <7",
+                "snappy": "^7.2.2",
+                "socks": "^2.7.1"
+            },
+            "peerDependenciesMeta": {
+                "@aws-sdk/credential-providers": {
+                    "optional": true
+                },
+                "@mongodb-js/zstd": {
+                    "optional": true
+                },
+                "gcp-metadata": {
+                    "optional": true
+                },
+                "kerberos": {
+                    "optional": true
+                },
+                "mongodb-client-encryption": {
+                    "optional": true
+                },
+                "snappy": {
+                    "optional": true
+                },
+                "socks": {
+                    "optional": true
+                }
+            }
         },
         "node_modules/mpath": {
             "version": "0.9.0",
@@ -24270,9 +24303,9 @@
             "integrity": "sha512-hug+mpbSSKnH13rFqy3zm+XiG+QTStiDAgMTHK355TIstQE0qBkBtSJsa5YHP94AuarVX9b/4dcebdTRZ9YiEw=="
         },
         "node_modules/ms": {
-            "version": "2.1.2",
-            "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
-            "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+            "version": "2.1.3",
+            "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+            "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
         },
         "node_modules/multicast-dns": {
             "version": "7.2.5",
@@ -24517,9 +24550,9 @@
             }
         },
         "node_modules/nodemon": {
-            "version": "3.1.4",
-            "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.4.tgz",
-            "integrity": "sha512-wjPBbFhtpJwmIeY2yP7QF+UKzPfltVGtfce1g/bB15/8vCGZj8uxD62b/b9M9/WVgme0NZudpownKN+c0plXlQ==",
+            "version": "3.1.5",
+            "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.5.tgz",
+            "integrity": "sha512-V5UtfYc7hjFD4SI3EzD5TR8ChAHEZ+Ns7Z5fBk8fAbTVAj+q3G+w7sHJrHxXBkVn6ApLVTljau8wfHwqmGUjMw==",
             "dependencies": {
                 "chokidar": "^3.5.2",
                 "debug": "^4",
@@ -27214,19 +27247,17 @@
             }
         },
         "node_modules/openai": {
-            "version": "4.57.3",
-            "resolved": "https://registry.npmjs.org/openai/-/openai-4.57.3.tgz",
-            "integrity": "sha512-mTz5/SmulkkeSpqbSr6WNLRU6krkyhnbfRUC8XfaXbj1T6xUorKEELjZvbRSzI714JLOk1MeFkqYS9H4WHhqDQ==",
+            "version": "4.62.0",
+            "resolved": "https://registry.npmjs.org/openai/-/openai-4.62.0.tgz",
+            "integrity": "sha512-cPSsarEXoJENNwYMx/Xh/wuvnyYf8lPSR4zDVSnRvbcMHmKkDIzXhUVvPPfuI4M4T83x25gVnlW7huWEGKG+SA==",
             "dependencies": {
                 "@types/node": "^18.11.18",
                 "@types/node-fetch": "^2.6.4",
-                "@types/qs": "^6.9.15",
                 "abort-controller": "^3.0.0",
                 "agentkeepalive": "^4.2.1",
                 "form-data-encoder": "1.7.2",
                 "formdata-node": "^4.3.2",
-                "node-fetch": "^2.6.7",
-                "qs": "^6.10.3"
+                "node-fetch": "^2.6.7"
             },
             "bin": {
                 "openai": "bin/cli"
@@ -27649,9 +27680,9 @@
             "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="
         },
         "node_modules/path-to-regexp": {
-            "version": "0.1.7",
-            "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
-            "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
+            "version": "0.1.10",
+            "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz",
+            "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w=="
         },
         "node_modules/path-type": {
             "version": "4.0.0",
@@ -27992,9 +28023,9 @@
             }
         },
         "node_modules/postcss": {
-            "version": "8.4.45",
-            "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.45.tgz",
-            "integrity": "sha512-7KTLTdzdZZYscUc65XmjFiB73vBhBfbPztCYdUNvlaso9PrzjzcmjqBPR0lNGkcVlcO4BjiO5rK/qNz+XAen1Q==",
+            "version": "8.4.47",
+            "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
+            "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
             "funding": [
                 {
                     "type": "opencollective",
@@ -28011,8 +28042,8 @@
             ],
             "dependencies": {
                 "nanoid": "^3.3.7",
-                "picocolors": "^1.0.1",
-                "source-map-js": "^1.2.0"
+                "picocolors": "^1.1.0",
+                "source-map-js": "^1.2.1"
             },
             "engines": {
                 "node": "^10 || ^12 || >=14"
@@ -28462,11 +28493,11 @@
             }
         },
         "node_modules/qs": {
-            "version": "6.11.0",
-            "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
-            "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
+            "version": "6.13.0",
+            "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
+            "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
             "dependencies": {
-                "side-channel": "^1.0.4"
+                "side-channel": "^1.0.6"
             },
             "engines": {
                 "node": ">=0.6"
@@ -28940,15 +28971,15 @@
             "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="
         },
         "node_modules/react-jsx-parser": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/react-jsx-parser/-/react-jsx-parser-2.0.0.tgz",
-            "integrity": "sha512-1siP+vM47gWE+x2VTF0ano5jJPRilOf7sRIoy4dzb1FmNoDpwe3htuzgcEfvzsc7YThzwmMQNUfy4vAv1ATs2g==",
-            "hasInstallScript": true,
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/react-jsx-parser/-/react-jsx-parser-2.1.0.tgz",
+            "integrity": "sha512-cGp8ceqA6j7OylsqeK2kuCGRitHpWzximsxsQyqkQO+1fwyG82Mb1l2nx9ImrfdCO6GT2kgt7C6cX9vJ46B7ow==",
             "dependencies": {
                 "acorn": "^8.12.1",
-                "acorn-jsx": "^5.3.2",
-                "browserslist": "^4.23.1",
-                "core-js": "^3.37.1"
+                "acorn-jsx": "^5.3.2"
+            },
+            "engines": {
+                "bun": "^1.1.27"
             },
             "optionalDependencies": {
                 "@types/react": "^18.3.3",
@@ -29388,9 +29419,9 @@
             "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A=="
         },
         "node_modules/regenerate-unicode-properties": {
-            "version": "10.1.1",
-            "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz",
-            "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==",
+            "version": "10.2.0",
+            "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz",
+            "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==",
             "dependencies": {
                 "regenerate": "^1.4.2"
             },
@@ -29964,9 +29995,9 @@
             }
         },
         "node_modules/rimraf/node_modules/lru-cache": {
-            "version": "11.0.0",
-            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.0.tgz",
-            "integrity": "sha512-Qv32eSV1RSCfhY3fpPE2GNZ8jgM9X7rdAfemLWqTUxwiyIC4jJ6Sy0fZ8H+oLWevO6i4/bizg7c8d8i6bxrzbA==",
+            "version": "11.0.1",
+            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.1.tgz",
+            "integrity": "sha512-CgeuL5uom6j/ZVrg7G/+1IXqRY8JXX4Hghfy5YE0EhoYQWvndP1kufu58cmZLNIDKnRhZrXfdS9urVWx98AipQ==",
             "engines": {
                 "node": "20 || >=22"
             }
@@ -30126,11 +30157,11 @@
             "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
         },
         "node_modules/sass": {
-            "version": "1.78.0",
-            "resolved": "https://registry.npmjs.org/sass/-/sass-1.78.0.tgz",
-            "integrity": "sha512-AaIqGSrjo5lA2Yg7RvFZrlXDBCp3nV4XP73GrLGvdRWWwk+8H3l0SDvq/5bA4eF+0RFPLuWUk3E+P1U/YqnpsQ==",
+            "version": "1.79.1",
+            "resolved": "https://registry.npmjs.org/sass/-/sass-1.79.1.tgz",
+            "integrity": "sha512-+mA7svoNKeL0DiJqZGeR/ZGUu8he4I8o3jyUcOFyo4eBJrwNgIMmAEwCMo/N2Y3wdjOBcRzoNxZIOtrtMX8EXg==",
             "dependencies": {
-                "chokidar": ">=3.0.0 <4.0.0",
+                "chokidar": "^4.0.0",
                 "immutable": "^4.0.0",
                 "source-map-js": ">=0.6.2 <2.0.0"
             },
@@ -30180,6 +30211,32 @@
                 }
             }
         },
+        "node_modules/sass/node_modules/chokidar": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.0.tgz",
+            "integrity": "sha512-mxIojEAQcuEvT/lyXq+jf/3cO/KoA6z4CeNDGGevTybECPOMFCnQy3OPahluUkbqgPNGw5Bi78UC7Po6Lhy+NA==",
+            "dependencies": {
+                "readdirp": "^4.0.1"
+            },
+            "engines": {
+                "node": ">= 14.16.0"
+            },
+            "funding": {
+                "url": "https://paulmillr.com/funding/"
+            }
+        },
+        "node_modules/sass/node_modules/readdirp": {
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.1.tgz",
+            "integrity": "sha512-GkMg9uOTpIWWKbSsgwb5fA4EavTR+SG/PMPoAY8hkhHfEEY0/vqljY+XHqtDf2cr2IJtoNRDbrrEpZUiZCkYRw==",
+            "engines": {
+                "node": ">= 14.16.0"
+            },
+            "funding": {
+                "type": "individual",
+                "url": "https://paulmillr.com/funding/"
+            }
+        },
         "node_modules/sax": {
             "version": "1.4.1",
             "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz",
@@ -30314,9 +30371,9 @@
             "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow=="
         },
         "node_modules/send": {
-            "version": "0.18.0",
-            "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
-            "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
+            "version": "0.19.0",
+            "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
+            "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
             "dependencies": {
                 "debug": "2.6.9",
                 "depd": "2.0.0",
@@ -30349,6 +30406,14 @@
             "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
             "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
         },
+        "node_modules/send/node_modules/encodeurl": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+            "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
+            "engines": {
+                "node": ">= 0.8"
+            }
+        },
         "node_modules/send/node_modules/mime": {
             "version": "1.6.0",
             "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
@@ -30360,11 +30425,6 @@
                 "node": ">=4"
             }
         },
-        "node_modules/send/node_modules/ms": {
-            "version": "2.1.3",
-            "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
-            "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
-        },
         "node_modules/serialize-javascript": {
             "version": "6.0.2",
             "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
@@ -30465,14 +30525,14 @@
             }
         },
         "node_modules/serve-static": {
-            "version": "1.15.0",
-            "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
-            "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
+            "version": "1.16.2",
+            "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
+            "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
             "dependencies": {
-                "encodeurl": "~1.0.2",
+                "encodeurl": "~2.0.0",
                 "escape-html": "~1.0.3",
                 "parseurl": "~1.3.3",
-                "send": "0.18.0"
+                "send": "0.19.0"
             },
             "engines": {
                 "node": ">= 0.8.0"
@@ -30762,11 +30822,11 @@
             }
         },
         "node_modules/simple-xml-to-json": {
-            "version": "1.2.2",
-            "resolved": "https://registry.npmjs.org/simple-xml-to-json/-/simple-xml-to-json-1.2.2.tgz",
-            "integrity": "sha512-bmJJf5YiYL60eOQk3gaVxbM6vgYuwrFydCEAA2x3jccHUTsAffiPyblS/yQGr8GDUQVxSDm3WwLNL5HmRqDUcg==",
+            "version": "1.2.3",
+            "resolved": "https://registry.npmjs.org/simple-xml-to-json/-/simple-xml-to-json-1.2.3.tgz",
+            "integrity": "sha512-kWJDCr9EWtZ+/EYYM5MareWj2cRnZGF93YDNpH4jQiHB+hBIZnfPFSQiVMzZOdk+zXWqTZ/9fTeQNu2DqeiudA==",
             "engines": {
-                "node": ">=14.20.0"
+                "node": ">=20.12.2"
             }
         },
         "node_modules/skmeans": {
@@ -30907,9 +30967,9 @@
             }
         },
         "node_modules/source-map-js": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
-            "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+            "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
             "engines": {
                 "node": ">=0.10.0"
             }
@@ -31167,9 +31227,9 @@
             "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
         },
         "node_modules/streamx": {
-            "version": "2.20.0",
-            "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.20.0.tgz",
-            "integrity": "sha512-ZGd1LhDeGFucr1CUCTBOS58ZhEendd0ttpGT3usTvosS4ntIwKN9LJFp+OeCSprsCPL14BXVRZlHGRY1V9PVzQ==",
+            "version": "2.20.1",
+            "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.20.1.tgz",
+            "integrity": "sha512-uTa0mU6WUC65iUvzKH4X9hEdvSW7rbPxPtwfWiLMSj3qTdQbAiUboZTxauKfpFuGIGa1C2BYijZ7wgdUXICJhA==",
             "dependencies": {
                 "fast-fifo": "^1.3.2",
                 "queue-tick": "^1.0.1",
@@ -31231,9 +31291,9 @@
             "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
         },
         "node_modules/string-width/node_modules/ansi-regex": {
-            "version": "6.0.1",
-            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
-            "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+            "version": "6.1.0",
+            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
+            "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
             "engines": {
                 "node": ">=12"
             },
@@ -31483,11 +31543,11 @@
             }
         },
         "node_modules/style-to-object": {
-            "version": "1.0.7",
-            "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.7.tgz",
-            "integrity": "sha512-uSjr59G5u6fbxUfKbb8GcqMGT3Xs9v5IbPkjb0S16GyOeBLAzSRK0CixBv5YrYvzO6TDLzIS6QCn78tkqWngPw==",
+            "version": "1.0.8",
+            "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.8.tgz",
+            "integrity": "sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==",
             "dependencies": {
-                "inline-style-parser": "0.2.3"
+                "inline-style-parser": "0.2.4"
             }
         },
         "node_modules/styled-components": {
@@ -31681,9 +31741,9 @@
             "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
         },
         "node_modules/terser": {
-            "version": "5.31.6",
-            "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.6.tgz",
-            "integrity": "sha512-PQ4DAriWzKj+qgehQ7LK5bQqCFNMmlhjR2PFFLuqGCpuCAauxemVBWwWOxo3UIwWQx8+Pr61Df++r76wDmkQBg==",
+            "version": "5.33.0",
+            "resolved": "https://registry.npmjs.org/terser/-/terser-5.33.0.tgz",
+            "integrity": "sha512-JuPVaB7s1gdFKPKTelwUyRq5Sid2A3Gko2S0PncwdBq7kN9Ti9HPWDQ06MPsEDGsZeVESjKEnyGy68quBk1w6g==",
             "dependencies": {
                 "@jridgewell/source-map": "^0.3.3",
                 "acorn": "^8.8.2",
@@ -31788,9 +31848,9 @@
             "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
         },
         "node_modules/text-decoder": {
-            "version": "1.1.1",
-            "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.1.1.tgz",
-            "integrity": "sha512-8zll7REEv4GDD3x4/0pW+ppIxSNs7H1J10IKFZsuOMscumCdM2a+toDGLPA3T+1+fLBql4zbt5z83GEQGGV5VA==",
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.0.tgz",
+            "integrity": "sha512-n1yg1mOj9DNpk3NeZOx7T6jchTbyJS3i3cucbNN6FcdPriMZx7NsgrGpWWdWZZGxD7ES1XB+3uoqHMgOKaN+fg==",
             "dependencies": {
                 "b4a": "^1.6.4"
             }
@@ -32618,9 +32678,9 @@
             }
         },
         "node_modules/type-fest": {
-            "version": "4.26.0",
-            "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.0.tgz",
-            "integrity": "sha512-OduNjVJsFbifKb57UqZ2EMP1i4u64Xwow3NYXUtBbD4vIwJdQd4+xl8YDou1dlm4DVrtwT/7Ky8z8WyCULVfxw==",
+            "version": "4.26.1",
+            "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.1.tgz",
+            "integrity": "sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==",
             "engines": {
                 "node": ">=16"
             },
@@ -32714,9 +32774,9 @@
             }
         },
         "node_modules/typescript": {
-            "version": "5.5.4",
-            "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
-            "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
+            "version": "5.6.2",
+            "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz",
+            "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==",
             "bin": {
                 "tsc": "bin/tsc",
                 "tsserver": "bin/tsserver"
@@ -32834,9 +32894,9 @@
             "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="
         },
         "node_modules/unicode-canonical-property-names-ecmascript": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz",
-            "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==",
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz",
+            "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==",
             "engines": {
                 "node": ">=4"
             }
@@ -32854,9 +32914,9 @@
             }
         },
         "node_modules/unicode-match-property-value-ecmascript": {
-            "version": "2.1.0",
-            "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz",
-            "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==",
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz",
+            "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==",
             "engines": {
                 "node": ">=4"
             }
@@ -33154,20 +33214,6 @@
             "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
             "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ=="
         },
-        "node_modules/url/node_modules/qs": {
-            "version": "6.13.0",
-            "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
-            "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
-            "dependencies": {
-                "side-channel": "^1.0.6"
-            },
-            "engines": {
-                "node": ">=0.6"
-            },
-            "funding": {
-                "url": "https://github.com/sponsors/ljharb"
-            }
-        },
         "node_modules/use-composed-ref": {
             "version": "1.3.0",
             "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.3.0.tgz",
@@ -33660,9 +33706,9 @@
             }
         },
         "node_modules/webpack-dev-middleware/node_modules/memfs": {
-            "version": "4.11.1",
-            "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.11.1.tgz",
-            "integrity": "sha512-LZcMTBAgqUUKNXZagcZxvXXfgF1bHX7Y7nQ0QyEiNbRJgE29GhgPd8Yna1VQcLlPiHt/5RFJMWYN9Uv/VPNvjQ==",
+            "version": "4.11.2",
+            "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.11.2.tgz",
+            "integrity": "sha512-VcR7lEtgQgv7AxGkrNNeUAimFLT+Ov8uGu1LuOfbe/iF/dKoh/QgpoaMZlhfejvLtMxtXYyeoT7Ar1jEbWdbPA==",
             "dependencies": {
                 "@jsonjoy.com/json-pack": "^1.0.3",
                 "@jsonjoy.com/util": "^1.3.0",
@@ -34162,9 +34208,9 @@
             }
         },
         "node_modules/wrap-ansi/node_modules/ansi-regex": {
-            "version": "6.0.1",
-            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
-            "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+            "version": "6.1.0",
+            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
+            "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
             "engines": {
                 "node": ">=12"
             },
diff --git a/package.json b/package.json
index 1f3d86be9..967549197 100644
--- a/package.json
+++ b/package.json
@@ -130,7 +130,7 @@
         "@types/reveal": "^4.2.0",
         "@types/supercluster": "^7.1.3",
         "@types/textfit": "^2.4.4",
-        "@types/web": "^0.0.159",
+        "@types/web": "^0.0.163",
         "@types/webpack-hot-middleware": "^2.25.9",
         "@webscopeio/react-textarea-autocomplete": "^4.9.2",
         "adm-zip": "^0.5.10",
@@ -188,7 +188,7 @@
         "file-saver": "^2.0.5",
         "find-in-files": "^0.5.0",
         "fit-curve": "^0.2.0",
-        "flexlayout-react": "^0.7.15",
+        "flexlayout-react": "^0.8.0",
         "fluent-ffmpeg": "^2.1.2",
         "forever-agent": "^0.6.1",
         "fork-ts-checker-webpack-plugin": "^9.0.2",
@@ -198,7 +198,7 @@
         "function-plot": "^1.23.3",
         "golden-layout": "^2.6.0",
         "google-auth-library": "^9.4.1",
-        "googleapis": "^142.0.0",
+        "googleapis": "^144.0.0",
         "googlephotos": "^0.3.5",
         "got": "^14.0.0",
         "howler": "^2.2.4",
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 37b1f04e7..5e7908725 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -708,7 +708,6 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
         const maxDist = Math.min((this.Bounds.r - this.Bounds.x) / 2, (this.Bounds.b - this.Bounds.y) / 2);
         const radiusHandle = (borderRadius / docMax) * maxDist;
         const radiusHandleLocation = Math.min(radiusHandle, maxDist);
-        
 
         const sharingMenu =
             Doc.IsSharingEnabled && docShareMode ? (
@@ -771,8 +770,6 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
         const centery = hideTitle ? 0 : this._titleHeight;
         const transformOrigin = `${50}% calc(50% + ${centery / 2}px)`;
         const freeformDoc = DocumentView.Selected().some(v => CollectionFreeFormDocumentView.from(v));
-      
-
         return (
             <div className="documentDecorations" style={{ display: this._showNothing && !freeformDoc ? 'none' : undefined }}>
                 <div
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 54698ab2d..13fef101a 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -9,6 +9,7 @@ import { BsArrowDown, BsArrowDownUp, BsArrowUp } from 'react-icons/bs';
 import { FaFilter } from 'react-icons/fa';
 import { ClientUtils, DashColor, lightOrDark } from '../../ClientUtils';
 import { Doc, Opt, StrListCast } from '../../fields/Doc';
+import { DocData } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { ScriptField } from '../../fields/ScriptField';
 import { BoolCast, Cast, DocCast, ImageCast, NumCast, ScriptCast, StrCast } from '../../fields/Types';
@@ -19,14 +20,12 @@ import { SnappingManager } from '../util/SnappingManager';
 import { undoable, UndoManager } from '../util/UndoManager';
 import { TreeSort } from './collections/TreeSort';
 import { Colors } from './global/globalEnums';
-import { TagsView } from './TagsView';
-import { CollectionFreeFormDocumentView } from './nodes/CollectionFreeFormDocumentView';
 import { DocumentView, DocumentViewProps } from './nodes/DocumentView';
 import { FieldViewProps } from './nodes/FieldView';
+import { IconTagBox } from './nodes/IconTagBox';
 import { StyleProp } from './StyleProp';
 import './StyleProvider.scss';
-import { IconTagBox } from './nodes/IconTagBox';
-import { DocData } from '../../fields/DocSymbols';
+import { TagsView } from './TagsView';
 
 function toggleLockedPosition(doc: Doc) {
     UndoManager.RunInBatch(() => Doc.toggleLockedPosition(doc), 'toggleBackground');
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index c029d17c6..97ff852b2 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -577,7 +577,8 @@ export class CollectionCardView extends CollectionSubView() {
     /**
      * Actually renders all the cards
      */
-    renderCards = (sortedDocs: Doc[]) => {
+    @computed get renderCards() {
+        const sortedDocs = this.sortedDocs;
         const anySelected = this.childDocs.some(doc => DocumentView.SelectedDocs().includes(doc));
         const isEmpty = this.childDocsWithoutLinks.length === 0;
         const isDesc = BoolCast(this.Document.cardSort_isDesc);
@@ -640,7 +641,7 @@ export class CollectionCardView extends CollectionSubView() {
                 </div>
             );
         });
-    };
+    }
 
     render() {
         const isEmpty = this.childDocsWithoutLinks.length === 0;
@@ -664,7 +665,7 @@ export class CollectionCardView extends CollectionSubView() {
                         gridAutoRows: `${100 / this.numRows}%`,
                     }}
                     onMouseLeave={() => this.setHoveredNodeIndex(-1)}>
-                    {this.renderCards(this.sortedDocs)}
+                    {this.renderCards}
                 </div>
             </div>
         );
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index 7feb17df1..f53a7d163 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -157,7 +157,7 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
 
         let noviceList: string[] = [];
         let text: string | undefined;
-        const getStyle: (val: string) => { [key: string]: string } = () => ({});
+        let getStyle: (val: string) => { [key: string]: string } = () => ({});
         let icon: IconProp = 'caret-down';
         const isViewDropdown = script?.script.originalScript.startsWith('{ return setView');
         if (isViewDropdown) {
@@ -192,7 +192,7 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
         } else {
             text = script?.script.run({ this: this.Document, value: '', _readOnly_: true }).result as string;
             // text = StrCast((RichTextMenu.Instance?.TextView?.EditorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
-            //   getStyle = (val: string) => ({ fontFamily: val });
+            if (this.Document.title === 'Font') getStyle = (val: string) => ({ fontFamily: val }); // bcz: major hack to style the font dropdown items --- needs to become part of the dropdown's metadata
         }
 
         // Get items to place into the list
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index f5f758ad2..03585a8b7 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -133,7 +133,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
             _layout_autoHeight: true,
         });
 
-        this.addToCollection?.(newCol); //this._props.addDocument(newCol)
+        this.addToCollection?.(newCol);
     };
 
     pointerDown = (e: React.PointerEvent) => {
-- 
cgit v1.2.3-70-g09d2


From 4e0ea65c08ff07429cb32f3907268206c80889fa Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 17 Sep 2024 22:11:22 -0400
Subject: linted cardView files after merge.

---
 src/client/apis/gpt/GPT.ts             |  16 +-
 src/client/views/DocumentButtonBar.tsx |  51 ++---
 src/client/views/FilterPanel.tsx       | 352 ++++++++++++++++-----------------
 src/client/views/StyleProvider.tsx     |  10 +-
 src/client/views/nodes/FieldView.tsx   |   4 +-
 5 files changed, 208 insertions(+), 225 deletions(-)

(limited to 'src/client/views/StyleProvider.tsx')

diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts
index 2657bf12e..5be9d84ff 100644
--- a/src/client/apis/gpt/GPT.ts
+++ b/src/client/apis/gpt/GPT.ts
@@ -1,4 +1,4 @@
-import { ChatCompletionMessageParam } from 'openai/resources';
+import { ChatCompletionMessageParam, Image } from 'openai/resources';
 import { openai } from './setup';
 
 enum GPTCallType {
@@ -15,7 +15,7 @@ enum GPTCallType {
     RUBRIC = 'rubric',
     TYPE = 'type',
     SUBSET = 'subset',
-    INFO = 'info'
+    INFO = 'info',
 }
 
 type GPTCallOpts = {
@@ -48,7 +48,7 @@ const callTypeMap: { [type: string]: GPTCallOpts } = {
         model: 'gpt-4o',
         maxTokens: 2048,
         temp: 0.25,
-        prompt: "The user is going to give you a list of descriptions. Each one is separated by `======` on either side. Descriptions will vary in length, so make sure to only separate when you see `======`. Sort them by the user's specifications. Make sure each description is only in the list once. Each item should be separated by `======`. Immediately afterward, surrounded by `------` on BOTH SIDES, provide some insight into your reasoning for the way you sorted (and mention nothing about the formatting details given in this description). It is VERY important that you format it exactly as described, ensuring the proper number of `=` and `-` (6 of each) and NO commas"
+        prompt: "The user is going to give you a list of descriptions. Each one is separated by `======` on either side. Descriptions will vary in length, so make sure to only separate when you see `======`. Sort them by the user's specifications. Make sure each description is only in the list once. Each item should be separated by `======`. Immediately afterward, surrounded by `------` on BOTH SIDES, provide some insight into your reasoning for the way you sorted (and mention nothing about the formatting details given in this description). It is VERY important that you format it exactly as described, ensuring the proper number of `=` and `-` (6 of each) and NO commas",
     },
     describe: { model: 'gpt-4-vision-preview', maxTokens: 2048, temp: 0, prompt: 'Describe these images in 3-5 words' },
     chatcard: { model: 'gpt-4-turbo', maxTokens: 512, temp: 0.5, prompt: 'Answer the following question as a short flashcard response. Do not include a label.' },
@@ -77,17 +77,15 @@ const callTypeMap: { [type: string]: GPTCallOpts } = {
         model: 'gpt-4-turbo',
         maxTokens: 1024,
         temp: 0,
-        prompt: "I'm going to give you a list of descriptions. Each one is separated by `======` on either side. Descriptions will vary in length, so make sure to only separate when you see `======`. Based on the question the user asks, provide a subset of the given descriptions that best matches the user's specifications. Make sure each description is only in the list once. Each item should be separated by `======`. Immediately afterward, surrounded by `------` on BOTH SIDES, provide some insight into your reasoning in the 2nd person (and mention nothing about the formatting details given in this description). It is VERY important that you format it exactly as described, ensuring the proper number of `=` and `-` (6 of each) and no commas"
+        prompt: "I'm going to give you a list of descriptions. Each one is separated by `======` on either side. Descriptions will vary in length, so make sure to only separate when you see `======`. Based on the question the user asks, provide a subset of the given descriptions that best matches the user's specifications. Make sure each description is only in the list once. Each item should be separated by `======`. Immediately afterward, surrounded by `------` on BOTH SIDES, provide some insight into your reasoning in the 2nd person (and mention nothing about the formatting details given in this description). It is VERY important that you format it exactly as described, ensuring the proper number of `=` and `-` (6 of each) and no commas",
     },
 
     info: {
         model: 'gpt-4-turbo',
         maxTokens: 1024,
         temp: 0,
-        prompt: "Answer the user's question with a short (<100 word) response. If a particular document is selected I will provide that information (which may help with your response)"
+        prompt: "Answer the user's question with a short (<100 word) response. If a particular document is selected I will provide that information (which may help with your response)",
     },
-
-
 };
 
 let lastCall = '';
@@ -98,7 +96,7 @@ let lastResp = '';
  * @param inputText Text to process
  * @returns AI Output
  */
-const gptAPICall = async (inputTextIn: string, callType: GPTCallType, prompt?: any) => {
+const gptAPICall = async (inputTextIn: string, callType: GPTCallType, prompt?: string) => {
     const inputText = [GPTCallType.SUMMARY, GPTCallType.FLASHCARD, GPTCallType.QUIZ].includes(callType) ? inputTextIn + '.' : inputTextIn;
     const opts: GPTCallOpts = callTypeMap[callType];
     if (lastCall === inputText) return lastResp;
@@ -131,7 +129,7 @@ const gptImageCall = async (prompt: string, n?: number) => {
             n: n ?? 1,
             size: '1024x1024',
         });
-        return response.data.map((data: any) => data.url);
+        return response.data.map((data: Image) => data.url);
         // return response.data.data[0].url;
     } catch (err) {
         console.error(err);
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index ccde1de80..87dd5f45a 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -298,16 +298,12 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
                             key={icon.toString()}
                             size="sm"
                             icon={icon}
-                            onClick={e => {
-                                if (name === 'tags'){
-                                    (targetDoc && (targetDoc[DocData].showIconTags = !targetDoc[DocData].showIconTags))
+                            onClick={() => {
+                                if (name === 'tags') {
+                                    targetDoc && (targetDoc[DocData].showIconTags = !targetDoc[DocData].showIconTags);
                                 } else {
-                                    (targetDoc && (targetDoc[DocData].showLabels = !targetDoc[DocData].showLabels))
+                                    targetDoc && (targetDoc[DocData].showLabels = !targetDoc[DocData].showLabels);
                                 }
-
-
-
-                               
                             }}
                         />
                     </div>
@@ -315,31 +311,24 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
             );
         };
 
-
-
-
         return !targetDoc ? null : (
-            <div className='documentButtonBar-icon'>
-            <div className="documentButtonBar-pinTypes" style = {{width: '40px'}}>
-                {metaBtn('tags', 'star')}
-                {metaBtn("keywords", 'id-card')}
-             </div>
-
-            <Tooltip title={<div className="dash-keyword-button">Open keyword menu</div>}>
-                <div
-                    className="documentButtonBar-icon"
-                    style={{ color: 'white' }}
-                    onClick={() => {
-                        // targetDoc[DocData].showIconTags = !targetDoc[DocData].showIconTags;
-                    }}
-                    >
-
-                   
-                    <FontAwesomeIcon className="documentdecorations-icon" icon="tag" />
+            <div className="documentButtonBar-icon">
+                <div className="documentButtonBar-pinTypes" style={{ width: '40px' }}>
+                    {metaBtn('tags', 'star')}
+                    {metaBtn('keywords', 'id-card')}
                 </div>
-            </Tooltip>
-            </div>
 
+                <Tooltip title={<div className="dash-keyword-button">Open keyword menu</div>}>
+                    <div
+                        className="documentButtonBar-icon"
+                        style={{ color: 'white' }}
+                        onClick={() => {
+                            // targetDoc[DocData].showIconTags = !targetDoc[DocData].showIconTags;
+                        }}>
+                        <FontAwesomeIcon className="documentdecorations-icon" icon="tag" />
+                    </div>
+                </Tooltip>
+            </div>
         );
     }
 
@@ -519,4 +508,4 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
             </div>
         );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
index 8133a4d0d..3795a4a6d 100644
--- a/src/client/views/FilterPanel.tsx
+++ b/src/client/views/FilterPanel.tsx
@@ -1,4 +1,3 @@
-/* eslint-disable react/jsx-props-no-spreading */
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@mui/material';
 import { action, computed, makeObservable, observable, ObservableMap } from 'mobx';
@@ -24,14 +23,186 @@ import { DocumentView } from './nodes/DocumentView';
 import { ButtonType } from './nodes/FontIconBox/FontIconBox';
 import { Handle, Tick, TooltipRail, Track } from './nodes/SliderBox-components';
 import { ObservableReactComponent } from './ObservableReactComponent';
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
 
+interface HotKeyButtonProps {
+    hotKey: string;
+    selected?: Doc;
+}
+
+/**
+ * Renders the buttons that correspond to each icon tag in the properties view. Allows users to change the icon,
+ * title, and delete.
+ */
+const HotKeyIconButton: React.FC<HotKeyButtonProps> = observer(({ hotKey /*, selected */ }) => {
+    const state = useLocalObservable(() => ({
+        isActive: false,
+        isEditing: false,
+        myHotKey: hotKey,
+
+        toggleActive() {
+            this.isActive = !this.isActive;
+        },
+        deactivate() {
+            this.isActive = false;
+        },
+        startEditing() {
+            this.isEditing = true;
+        },
+        stopEditing() {
+            this.isEditing = false;
+        },
+        setHotKey(newHotKey: string) {
+            this.myHotKey = newHotKey;
+        },
+    }));
+
+    const panelRef = useRef<HTMLDivElement>(null);
+    const inputRef = useRef<HTMLInputElement>(null);
+
+    const handleClick = () => {
+        state.toggleActive();
+    };
+
+    const hotKeys = StrListCast(Doc.UserDoc().myFilterHotKeyTitles);
+    const buttons = DocCast(Doc.UserDoc().myContextMenuBtns);
+    const filter = DocCast(buttons.Filter);
+
+    /**
+     * The doc of the button in the context menu that corresponds to the current hotkey
+     * @returns
+     */
+    const myHotKeyDoc = () => {
+        const hotKeyDocs = DocListCast(filter.data);
+        return hotKeyDocs.filter(k => StrCast(k.title) === hotKey)[0];
+    };
+
+    /**
+     * Removes a hotkey from list
+     */
+    const removeHotKey = () => {
+        Doc.RemoveDocFromList(filter, 'data', myHotKeyDoc());
+    };
+
+    /**
+     * Updates the list of hotkeys based on the users input. replaces the old title with the new one and then assigns this new
+     * hotkey with the current icon
+     */
+    const updateFromInput = undoable(() => {
+        const myDoc = myHotKeyDoc();
+        Doc.UserDoc().myFilterHotKeyTitles = new List<string>(hotKeys.map(k => (k === hotKey ? state.myHotKey : k)));
+        Doc.UserDoc()[state.myHotKey] = StrCast(Doc.UserDoc()[hotKey]);
+        Doc.UserDoc()[hotKey] = '';
+        myDoc.title = state.myHotKey;
+        myDoc.toolTip = `Click to toggle the ${state.myHotKey}'s group's visibility`;
+    }, '');
+
+    /**
+     * Deselects if the user clicks outside the button
+     * @param event
+     */
+    const handleClickOutside = (event: MouseEvent) => {
+        if (panelRef.current && !panelRef.current.contains(event.target as Node)) {
+            state.deactivate();
+            if (state.isEditing) {
+                state.stopEditing();
+
+                updateFromInput();
+            }
+        }
+    };
+
+    useEffect(() => {
+        document.addEventListener('mousedown', handleClickOutside);
+        return () => {
+            document.removeEventListener('mousedown', handleClickOutside);
+        };
+    }, []);
+
+    const iconOpts = ['star', 'heart', 'bolt', 'satellite', 'palette', 'robot', 'lightbulb', 'highlighter', 'book', 'chalkboard'] as IconProp[];
+
+    /**
+     * Panel of icons the user can choose from to represent their tag
+     */
+    const iconPanel = iconOpts.map((icon, i) => (
+        <button
+            key={i}
+            onClick={undoable((e: React.MouseEvent) => {
+                e.stopPropagation;
+                Doc.UserDoc()[hotKey] = icon.toString();
+                myHotKeyDoc().icon = icon.toString();
+            }, '')}
+            className="icon-panel-button">
+            <FontAwesomeIcon icon={icon} color={SnappingManager.userColor} />
+        </button>
+    ));
+
+    /**
+     * Actually renders the buttons
+     */
+
+    return (
+        <div
+            className={`filterHotKey-button`}
+            onClick={e => {
+                e.stopPropagation();
+                state.startEditing();
+                setTimeout(() => inputRef.current?.focus(), 0);
+            }}>
+            <div className={`hotKey-icon-button ${state.isActive ? 'active' : ''}`} ref={panelRef}>
+                <Tooltip title={<div className="dash-tooltip">Click to customize this hotkey&apos;s icon</div>}>
+                    <button
+                        type="button"
+                        className="hotKey-icon"
+                        onClick={(e: React.MouseEvent) => {
+                            e.stopPropagation();
+                            handleClick();
+                        }}>
+                        <FontAwesomeIcon icon={Doc.UserDoc()[hotKey] as IconProp} size="2xl" color={SnappingManager.userColor} />
+                    </button>
+                </Tooltip>
+                {state.isActive && <div className="icon-panel">{iconPanel}</div>}
+            </div>
+            {state.isEditing ? (
+                <input
+                    ref={inputRef}
+                    type="text"
+                    value={state.myHotKey.toUpperCase()}
+                    onChange={e => state.setHotKey(e.target.value)}
+                    onBlur={() => {
+                        state.stopEditing();
+                        updateFromInput();
+                    }}
+                    onKeyDown={e => {
+                        if (e.key === 'Enter') {
+                            state.stopEditing();
+                            updateFromInput();
+                        }
+                    }}
+                    className="hotkey-title-input"
+                />
+            ) : (
+                <p className="hotkey-title">{hotKey.toUpperCase()}</p>
+            )}
+            <button
+                className="hotKey-close"
+                onClick={(e: React.MouseEvent) => {
+                    e.stopPropagation();
+                    Doc.UserDoc().myFilterHotKeyTitles = new List<string>(hotKeys.filter(k => k !== hotKey));
+                    removeHotKey();
+                }}>
+                <FontAwesomeIcon icon={'x' as IconProp} color={SnappingManager.userColor} />
+            </button>
+        </div>
+    );
+});
 interface filterProps {
     Document: Doc;
 }
 
 @observer
 export class FilterPanel extends ObservableReactComponent<filterProps> {
-    @observable _selectedFacetHeaders = new Set<string>();
+    // eslint-disable-next-line no-use-before-define
     public static Instance: FilterPanel;
 
     constructor(props: filterProps) {
@@ -40,6 +211,7 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
         FilterPanel.Instance = this;
     }
 
+    @observable _selectedFacetHeaders = new Set<string>();
     /**
      * @returns the relevant doc according to the value of FilterBox._filterScope i.e. either the Current Dashboard or the Current Collection
      */
@@ -263,8 +435,8 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
         const hotKeys = StrListCast(Doc.UserDoc().myFilterHotKeyTitles);
 
         // Selecting a button should make it so that the icon on the top filter panel becomes said icon
-        const buttons = hotKeys.map((hotKey, i) => (
-            <Tooltip key={hotKey} title={<div className="dash-tooltip">Click to customize this hotkey's icon</div>}>
+        const buttons = hotKeys.map(hotKey => (
+            <Tooltip key={hotKey} title={<div className="dash-tooltip">Click to customize this hotkey&apos;s icon</div>}>
                 <HotKeyIconButton hotKey={hotKey} selected={selected} />
             </Tooltip>
         ));
@@ -457,175 +629,3 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
         return undefined;
     }
 }
-
-interface HotKeyButtonProps {
-    hotKey: string;
-    selected?: Doc;
-}
-
-/**
- * Renders the buttons that correspond to each icon tag in the properties view. Allows users to change the icon,
- * title, and delete.
- */
-const HotKeyIconButton: React.FC<HotKeyButtonProps> = observer(({ hotKey, selected }) => {
-    const state = useLocalObservable(() => ({
-        isActive: false,
-        isEditing: false,
-        myHotKey: hotKey,
-
-        toggleActive() {
-            this.isActive = !this.isActive;
-        },
-        deactivate() {
-            this.isActive = false;
-        },
-        startEditing() {
-            this.isEditing = true;
-        },
-        stopEditing() {
-            this.isEditing = false;
-        },
-        setHotKey(newHotKey: string) {
-            this.myHotKey = newHotKey;
-        },
-    }));
-
-    const panelRef = useRef<HTMLDivElement>(null);
-    const inputRef = useRef<HTMLInputElement>(null);
-
-    const handleClick = () => {
-        state.toggleActive();
-    };
-
-    const hotKeys = StrListCast(Doc.UserDoc().myFilterHotKeyTitles);
-    const buttons = DocCast(Doc.UserDoc().myContextMenuBtns);
-    const filter = DocCast(buttons.Filter);
-
-    /**
-     * The doc of the button in the context menu that corresponds to the current hotkey
-     * @returns
-     */
-    const myHotKeyDoc = () => {
-        const hotKeyDocs = DocListCast(filter.data);
-        return hotKeyDocs.filter(k => StrCast(k.title) === hotKey)[0];
-    };
-
-    /**
-     * Removes a hotkey from list
-     */
-    const removeHotKey = () => {
-        Doc.RemoveDocFromList(filter, 'data', myHotKeyDoc());
-    };
-
-    /**
-     * Deselects if the user clicks outside the button
-     * @param event
-     */
-    const handleClickOutside = (event: MouseEvent) => {
-        if (panelRef.current && !panelRef.current.contains(event.target as Node)) {
-            state.deactivate();
-            if (state.isEditing) {
-                state.stopEditing();
-
-                updateFromInput();
-            }
-        }
-    };
-
-    /**
-     * Updates the list of hotkeys based on the users input. replaces the old title with the new one and then assigns this new
-     * hotkey with the current icon
-     */
-    const updateFromInput = undoable(() => {
-        const myDoc = myHotKeyDoc();
-        Doc.UserDoc().myFilterHotKeyTitles = new List<string>(hotKeys.map(k => (k === hotKey ? state.myHotKey : k)));
-        Doc.UserDoc()[state.myHotKey] = StrCast(Doc.UserDoc()[hotKey]);
-        Doc.UserDoc()[hotKey] = '';
-        myDoc.title = state.myHotKey;
-        myDoc.toolTip = `Click to toggle the ${state.myHotKey}'s group's visibility`;
-    }, '');
-
-    useEffect(() => {
-        document.addEventListener('mousedown', handleClickOutside);
-        return () => {
-            document.removeEventListener('mousedown', handleClickOutside);
-        };
-    }, []);
-
-    const iconOpts = ['star', 'heart', 'bolt', 'satellite', 'palette', 'robot', 'lightbulb', 'highlighter', 'book', 'chalkboard'];
-
-    /**
-     * Panel of icons the user can choose from to represent their tag
-     */
-    const iconPanel = iconOpts.map((icon, i) => (
-        <button
-            key={i}
-            onClick={undoable((e: React.MouseEvent) => {
-                e.stopPropagation;
-                Doc.UserDoc()[hotKey] = icon;
-                myHotKeyDoc().icon = icon;
-            }, '')}
-            className="icon-panel-button">
-            <FontAwesomeIcon icon={icon as any} color={SnappingManager.userColor} />
-        </button>
-    ));
-
-    /**
-     * Actually renders the buttons
-     */
-
-    return (
-        <div
-            className={`filterHotKey-button`}
-            onClick={e => {
-                e.stopPropagation();
-                state.startEditing();
-                setTimeout(() => inputRef.current?.focus(), 0);
-            }}>
-            <div className={`hotKey-icon-button ${state.isActive ? 'active' : ''}`} ref={panelRef}>
-                <Tooltip title={<div className="dash-tooltip">Click to customize this hotkey's icon</div>}>
-                    <button
-                        type="button"
-                        className="hotKey-icon"
-                        onClick={(e: React.MouseEvent) => {
-                            e.stopPropagation();
-                            handleClick();
-                        }}>
-                        <FontAwesomeIcon icon={Doc.UserDoc()[hotKey] as any} size="2xl" color={SnappingManager.userColor} />
-                    </button>
-                </Tooltip>
-                {state.isActive && <div className="icon-panel">{iconPanel}</div>}
-            </div>
-            {state.isEditing ? (
-                <input
-                    ref={inputRef}
-                    type="text"
-                    value={state.myHotKey.toUpperCase()}
-                    onChange={e => state.setHotKey(e.target.value)}
-                    onBlur={() => {
-                        state.stopEditing();
-                        updateFromInput();
-                    }}
-                    onKeyDown={e => {
-                        if (e.key === 'Enter') {
-                            state.stopEditing();
-                            updateFromInput();
-                        }
-                    }}
-                    className="hotkey-title-input"
-                />
-            ) : (
-                <p className="hotkey-title">{hotKey.toUpperCase()}</p>
-            )}
-            <button
-                className="hotKey-close"
-                onClick={(e: React.MouseEvent) => {
-                    e.stopPropagation();
-                    Doc.UserDoc().myFilterHotKeyTitles = new List<string>(hotKeys.filter(k => k !== hotKey));
-                    removeHotKey();
-                }}>
-                <FontAwesomeIcon icon={'x' as any} color={SnappingManager.userColor} />
-            </button>
-        </div>
-    );
-});
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 13fef101a..eb434db40 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -326,7 +326,6 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
                             type={Type.TERT}
                             dropdownType={DropdownType.CLICK}
                             fillWidth
-                            // eslint-disable-next-line react/no-unstable-nested-components
                             iconProvider={() => <div className='styleProvider-filterShift'><FaFilter/></div>}
                             closeOnSelect
                             setSelectedVal={((dvValue: unknown) => {
@@ -366,12 +365,9 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
                     </Tooltip>
                 );
             };
-            const tags = () => props?.DocumentView?.() ? <TagsView Views={[props.DocumentView()]}/> :  null;
-
-            const iconTags = () => {
-                if (doc && doc![DocData].showIconTags)
-                    {return (<IconTagBox doc= {doc}></IconTagBox>)}
-            }
+            const tags = () => docView?.() ? <TagsView Views={[docView?.()]}/> :  null;
+            const iconTags = () => doc?.[DocData].showIconTags ? <IconTagBox doc= {doc}/> : null;
+        
             return (
                 <>
                     {paint()}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index c269c7bcb..683edba16 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -1,5 +1,3 @@
-/* eslint-disable react/no-unused-prop-types */
-/* eslint-disable react/require-default-props */
 import { Property } from 'csstype';
 import { computed } from 'mobx';
 import { observer } from 'mobx-react';
@@ -21,6 +19,7 @@ export type FocusFuncType = (doc: Doc, options: FocusViewOptions) => Opt<number>
 // eslint-disable-next-line no-use-before-define
 export type StyleProviderFuncType = (
     doc: Opt<Doc>,
+    // eslint-disable-next-line no-use-before-define
     props: Opt<FieldViewProps>,
     property: string
 ) =>
@@ -65,6 +64,7 @@ export interface FieldViewSharedProps {
     containerViewPath?: () => DocumentView[];
     fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _freeform_fitContentsToBox property on a Document
     isGroupActive?: () => string | undefined; // is this document part of a group that is active
+    // eslint-disable-next-line no-use-before-define
     setContentViewBox?: (view: ViewBoxInterface<FieldViewProps>) => void; // called by rendered field's viewBox so that DocumentView can make direct calls to the viewBox
     PanelWidth: () => number;
     PanelHeight: () => number;
-- 
cgit v1.2.3-70-g09d2


From d95730d904612640184ca6fdc00864b0c81b0c0c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 18 Sep 2024 20:46:38 -0400
Subject: lots of changes to fix dragging cards, integrate iconTags with other
 tags, sizing docs when selected to fit window,

---
 eslint.config.mjs                                  |   1 +
 src/client/util/CurrentUserUtils.ts                |  14 +-
 src/client/util/DragManager.ts                     |   3 +-
 src/client/views/DocumentButtonBar.tsx             |  44 ++----
 src/client/views/DocumentDecorations.tsx           |   4 +-
 src/client/views/StyleProvider.tsx                 |   4 -
 src/client/views/TagsView.tsx                      |  20 +--
 .../views/collections/CollectionCardDeckView.scss  |   8 +-
 .../views/collections/CollectionCardDeckView.tsx   | 152 ++++++++++++---------
 .../views/collections/CollectionCarouselView.tsx   |   1 -
 .../views/collections/CollectionStackingView.tsx   |   3 -
 src/client/views/collections/CollectionSubView.tsx |   4 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   5 +-
 src/client/views/nodes/DocumentView.tsx            |   5 +
 src/client/views/nodes/IconTagBox.scss             |  24 ++--
 src/client/views/nodes/IconTagBox.tsx              | 106 +++++++-------
 .../views/nodes/formattedText/FormattedTextBox.tsx |   1 +
 src/client/views/search/FaceRecognitionHandler.tsx |   3 +-
 18 files changed, 198 insertions(+), 204 deletions(-)

(limited to 'src/client/views/StyleProvider.tsx')

diff --git a/eslint.config.mjs b/eslint.config.mjs
index 8926afd7c..f7063caa5 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -36,6 +36,7 @@ export default [
             'no-multi-assign': 'off',
             'no-underscore-dangle': 'off',
             'no-nested-ternary': 'off',
+            'no-param-reassign': 'error',
             'lines-between-class-members': 'off',
             'no-shadow': 'off',
             '@typescript-eslint/no-shadow': 'warn',
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index eee6be937..262916312 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -2,7 +2,7 @@
 import { reaction, runInAction } from "mobx";
 import * as rp from 'request-promise';
 import { ClientUtils, OmitKeys } from "../../ClientUtils";
-import { Doc, DocListCast, DocListCastAsync, FieldType, Opt } from "../../fields/Doc";
+import { Doc, DocListCast, DocListCastAsync, FieldType, Opt, StrListCast } from "../../fields/Doc";
 import { DocData } from "../../fields/DocSymbols";
 import { InkTool } from "../../fields/InkField";
 import { List } from "../../fields/List";
@@ -688,18 +688,18 @@ pie title Minerals in my tap water
   
     static tagGroupTools(): Button[] {
         if (!Doc.UserDoc().activeDashboard) {
-        Doc.UserDoc().myFilterHotKeyTitles = new List<string>(['Star', 'Heart', 'Bolt', 'Cloud']);
+        Doc.UserDoc().myFilterHotKeyTitles = new List<string>(['star', 'heart', 'bolt', 'cloud']);
         
-        ['Star', 'Heart', 'Bolt', 'Cloud'].forEach(key => {
+        StrListCast(Doc.UserDoc().myFilterHotKeyTitles).forEach(key => {
             Doc.UserDoc()[key] = key.toLowerCase();
         });
 }
         // hack: if there's no dashboard, create default filters.  otherwise, just make sure that the Options button is preserved
         return (Doc.UserDoc().activeDashboard ? [] : [
-            { title: "Star", isSystem: false, icon: "star", toolTip:"Click to toggle the star group's visibility",  btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"star", funcs: {}, scripts: { onClick: '{ return handleTags(this.toolType, _readOnly_);}'}},
-            { title: "Heart", isSystem: false,icon: "heart", toolTip:"Click to toggle the heart group's visibility",  btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"heart", funcs: {}, scripts: { onClick: '{ return handleTags(this.toolType, _readOnly_);}'}},
-            { title: "Bolt", isSystem: false,icon: "bolt", toolTip:"Click to toggle the bolt group's visibility",  btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"bolt", funcs: {}, scripts: { onClick: '{ return handleTags(this.toolType, _readOnly_);}'}},
-            { title: "Cloud", isSystem: false,icon: "cloud", toolTip:"Click to toggle the cloud group's visibility",  btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"cloud", funcs: {}, scripts: { onClick: '{ return handleTags(this.toolType, _readOnly_);}'}},
+            { title: "star",  isSystem: false,icon: "star", toolTip:"Click to toggle the star group's visibility",   btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"star", funcs: {}, scripts: { onClick: '{ return handleTags(this.toolType, _readOnly_);}'}},
+            { title: "heart", isSystem: false,icon: "heart", toolTip:"Click to toggle the heart group's visibility",  btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"heart", funcs: {}, scripts: { onClick: '{ return handleTags(this.toolType, _readOnly_);}'}},
+            { title: "bolt",  isSystem: false,icon: "bolt", toolTip:"Click to toggle the bolt group's visibility",    btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"bolt", funcs: {}, scripts: { onClick: '{ return handleTags(this.toolType, _readOnly_);}'}},
+            { title: "cloud", isSystem: false,icon: "cloud", toolTip:"Click to toggle the cloud group's visibility",  btnType: ButtonType.ToggleButton,  expertMode: false, toolType:"cloud", funcs: {}, scripts: { onClick: '{ return handleTags(this.toolType, _readOnly_);}'}},
         ]).concat([
             { title: "Options", isSystem: true,icon: "gear", toolTip:"Click to customize your filter panel", btnType: ButtonType.ClickButton,  expertMode: false, toolType:"opts", funcs: {}, scripts: { onClick: '{ return handleTags(this.toolType, _readOnly_);}'}}
         ])
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index d55d193cc..81ea840f1 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -29,7 +29,7 @@ import { dropActionType } from './DropActionTypes';
 import { SnappingManager } from './SnappingManager';
 import { UndoManager } from './UndoManager';
 
-// eslint-disable-next-line @typescript-eslint/no-var-requires
+// eslint-disable-next-line  @typescript-eslint/no-require-imports
 const { contextMenuZindex } = require('../views/global/globalCssVariables.module.scss'); // prettier-ignore
 
 /**
@@ -101,7 +101,6 @@ export namespace DragManager {
 
     // event called when the drag operation results in a drop action
     export class DropEvent {
-        // eslint-disable-next-line no-useless-constructor
         constructor(
             readonly x: number,
             readonly y: number,
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 87dd5f45a..32bf67df1 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -9,7 +9,7 @@ import * as React from 'react';
 import { FaEdit } from 'react-icons/fa';
 import { returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick } from '../../ClientUtils';
 import { emptyFunction } from '../../Utils';
-import { Doc } from '../../fields/Doc';
+import { Doc, DocListCast } from '../../fields/Doc';
 import { Cast, DocCast } from '../../fields/Types';
 import { DocUtils, IsFollowLinkScript } from '../documents/DocUtils';
 import { CalendarManager } from '../util/CalendarManager';
@@ -28,7 +28,6 @@ import { DocumentLinksButton } from './nodes/DocumentLinksButton';
 import { DocumentView } from './nodes/DocumentView';
 import { OpenWhere } from './nodes/OpenWhere';
 import { DashFieldView } from './nodes/formattedText/DashFieldView';
-import { DocData } from '../../fields/DocSymbols';
 
 @observer
 export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (DocumentView | undefined)[]; stack?: unknown }> {
@@ -287,44 +286,27 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
     get keywordButton() {
         const targetDoc = this.view0?.Document;
 
-        const metaBtn = (name: string, icon: IconProp) => {
-            const tooltip = `Toggle ${name}`;
-            return (
-                <Tooltip title={<div className="dash-tooltip">{tooltip}</div>}>
-                    <div className="documentButtonBar-pinIcon">
-                        <FontAwesomeIcon
-                            className="documentdecorations-icon"
-                            style={{ width: 20 }}
-                            key={icon.toString()}
-                            size="sm"
-                            icon={icon}
-                            onClick={() => {
-                                if (name === 'tags') {
-                                    targetDoc && (targetDoc[DocData].showIconTags = !targetDoc[DocData].showIconTags);
-                                } else {
-                                    targetDoc && (targetDoc[DocData].showLabels = !targetDoc[DocData].showLabels);
-                                }
-                            }}
-                        />
-                    </div>
-                </Tooltip>
-            );
-        };
-
         return !targetDoc ? null : (
             <div className="documentButtonBar-icon">
-                <div className="documentButtonBar-pinTypes" style={{ width: '40px' }}>
+                {/* <div className="documentButtonBar-pinTypes" style={{ width: '40px' }}>
                     {metaBtn('tags', 'star')}
                     {metaBtn('keywords', 'id-card')}
-                </div>
+                </div> */}
 
                 <Tooltip title={<div className="dash-keyword-button">Open keyword menu</div>}>
                     <div
                         className="documentButtonBar-icon"
                         style={{ color: 'white' }}
-                        onClick={() => {
-                            // targetDoc[DocData].showIconTags = !targetDoc[DocData].showIconTags;
-                        }}>
+                        onClick={undoable(e => {
+                            const showing = DocumentView.Selected().some(dv => dv.showTags);
+                            DocumentView.Selected().forEach(dv => {
+                                dv.layoutDoc._layout_showTags = !showing;
+                                if (e.shiftKey)
+                                    DocListCast(dv.Document[Doc.LayoutFieldKey(dv.Document) + '_annotations']).forEach(doc => {
+                                        if (doc.face) doc.hidden = showing;
+                                    });
+                            });
+                        }, 'show Doc tags')}>
                         <FontAwesomeIcon className="documentdecorations-icon" icon="tag" />
                     </div>
                 </Tooltip>
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 5e7908725..2ba96c2a9 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -837,7 +837,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
                                 <div
                                     className="link-button-container"
                                     style={{
-                                        top: DocumentView.Selected().length > 1 ? 0 : `${seldocview.Document._layout_showTags ? 4 + seldocview.TagPanelHeight : 4}px`,
+                                        top: DocumentView.Selected().length > 1 ? 0 : `${seldocview.showTags ? 4 + seldocview.TagPanelHeight : 4}px`,
                                         transform: `translate(${-this._resizeBorderWidth / 2 + 10}px, ${this._resizeBorderWidth + bounds.b - bounds.y + this._titleHeight}px) `,
                                     }}>
                                     <DocumentButtonBar views={() => DocumentView.Selected()} />
@@ -846,7 +846,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
                             <div
                                 className="documentDecorations-tagsView"
                                 style={{
-                                    top: `${seldocview.Document._layout_showTags ? 4 + seldocview.TagPanelHeight : 4}px`,
+                                    top: `${seldocview.showTags ? 4 + seldocview.TagPanelHeight : 4}px`,
                                     transform: `translate(${-this._resizeBorderWidth / 2 + 10}px, ${this._resizeBorderWidth + bounds.b - bounds.y + this._titleHeight}px) `,
                                 }}>
                                 {DocumentView.Selected().length > 1 ? <TagsView Views={DocumentView.Selected()} /> : null}
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index eb434db40..3545afcee 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -9,7 +9,6 @@ import { BsArrowDown, BsArrowDownUp, BsArrowUp } from 'react-icons/bs';
 import { FaFilter } from 'react-icons/fa';
 import { ClientUtils, DashColor, lightOrDark } from '../../ClientUtils';
 import { Doc, Opt, StrListCast } from '../../fields/Doc';
-import { DocData } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { ScriptField } from '../../fields/ScriptField';
 import { BoolCast, Cast, DocCast, ImageCast, NumCast, ScriptCast, StrCast } from '../../fields/Types';
@@ -22,7 +21,6 @@ import { TreeSort } from './collections/TreeSort';
 import { Colors } from './global/globalEnums';
 import { DocumentView, DocumentViewProps } from './nodes/DocumentView';
 import { FieldViewProps } from './nodes/FieldView';
-import { IconTagBox } from './nodes/IconTagBox';
 import { StyleProp } from './StyleProp';
 import './StyleProvider.scss';
 import { TagsView } from './TagsView';
@@ -366,7 +364,6 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
                 );
             };
             const tags = () => docView?.() ? <TagsView Views={[docView?.()]}/> :  null;
-            const iconTags = () => doc?.[DocData].showIconTags ? <IconTagBox doc= {doc}/> : null;
         
             return (
                 <>
@@ -375,7 +372,6 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
                     {filter()}
                     {audio()}
                     {tags()}
-                    {iconTags()}
                 </>
             );
         }
diff --git a/src/client/views/TagsView.tsx b/src/client/views/TagsView.tsx
index be2c28185..a678d5580 100644
--- a/src/client/views/TagsView.tsx
+++ b/src/client/views/TagsView.tsx
@@ -18,6 +18,7 @@ import { ObservableReactComponent } from './ObservableReactComponent';
 import './TagsView.scss';
 import { DocumentView } from './nodes/DocumentView';
 import { FaceRecognitionHandler } from './search/FaceRecognitionHandler';
+import { IconTagBox } from './nodes/IconTagBox';
 
 /**
  * The TagsView is a metadata input/display panel shown at the bottom of a DocumentView in a freeform collection.
@@ -59,7 +60,7 @@ export class TagItem extends ObservableReactComponent<TagItemProps> {
      * @param tag tag string
      * @returns tag collection Doc or undefined
      */
-    public static findTagCollectionDoc = (tag: String) => TagItem.AllTagCollectionDocs.find(doc => doc.title === tag);
+    public static findTagCollectionDoc = (tag: string) => TagItem.AllTagCollectionDocs.find(doc => doc.title === tag);
 
     /**
      * Creates a Doc that collects Docs with the specified tag / value
@@ -148,7 +149,7 @@ export class TagItem extends ObservableReactComponent<TagItemProps> {
 
     private _ref: React.RefObject<HTMLDivElement>;
 
-    constructor(props: any) {
+    constructor(props: TagItemProps) {
         super(props);
         makeObservable(this);
         this._ref = React.createRef();
@@ -225,7 +226,7 @@ export class TagItem extends ObservableReactComponent<TagItemProps> {
                                 type="checkbox"
                                 onClick={e => e.stopPropagation()}
                                 onPointerDown={e => e.stopPropagation()}
-                                onChange={undoable(e => (this.doc[metadata] = !this.doc[metadata]), 'metadata toggle')}
+                                onChange={undoable(() => (this.doc[metadata] = !this.doc[metadata]), 'metadata toggle')}
                                 checked={this.doc[metadata] as boolean}
                             />
                         ) : (
@@ -257,7 +258,7 @@ interface TagViewProps {
  */
 @observer
 export class TagsView extends ObservableReactComponent<TagViewProps> {
-    constructor(props: any) {
+    constructor(props: TagViewProps) {
         super(props);
         makeObservable(this);
     }
@@ -271,7 +272,7 @@ export class TagsView extends ObservableReactComponent<TagViewProps> {
     componentDidMount() {
         this._heightDisposer = reaction(
             () => this.View.screenToContentsTransform(),
-            xf => {
+            () => {
                 this._panelHeightDirty = this._panelHeightDirty + 1;
             }
         );
@@ -288,7 +289,7 @@ export class TagsView extends ObservableReactComponent<TagViewProps> {
         return this._props.Views.length > 1 ? 1 : Math.max(1, 1 / this.View.screenToLocalScale());
     }
     @computed get isEditing() {
-        return this._isEditing && (this._props.Views.length > 1 || DocumentView.SelectedDocs().includes(this.View.Document));
+        return this._isEditing && (this._props.Views.length > 1 || (DocumentView.Selected().length === 1 && DocumentView.Selected().includes(this.View)));
     }
 
     /**
@@ -336,7 +337,7 @@ export class TagsView extends ObservableReactComponent<TagViewProps> {
         );
         this._panelHeightDirty;
 
-        return this.View.ComponentView?.isUnstyledView?.() || (!this.View.Document._layout_showTags && this._props.Views.length === 1) ? null : (
+        return this.View.ComponentView?.isUnstyledView?.() || (!this.View.showTags && this._props.Views.length === 1) ? null : (
             <div
                 className="tagsView-container"
                 ref={r => r && new ResizeObserver(action(() => this._props.Views.length === 1 && (this.View.TagPanelHeight = Math.max(0, (r?.getBoundingClientRect().height ?? 0) - this.InsetDist)))).observe(r)}
@@ -352,9 +353,10 @@ export class TagsView extends ObservableReactComponent<TagViewProps> {
                 }}>
                 <div className="tagsView-content" style={{ width: '100%' }}>
                     <div className="tagsView-list">
-                        {!tagsList.size && !facesList.size ? null : ( //
+                        {this._props.Views.length === 1 && !this.View.showTags ? null : ( //
                             <IconButton style={{ width: '8px' }} tooltip="Close Menu" onPointerDown={() => this.setToEditing(!this._isEditing)} icon={<FontAwesomeIcon icon={this._isEditing ? 'chevron-up' : 'chevron-down'} size="sm" />} />
                         )}
+                        <IconTagBox Views={this._props.Views} IsEditing={this._isEditing} />
                         {Array.from(tagsList).map((tag, i) => (
                             <TagItem
                                 key={i}
@@ -388,7 +390,7 @@ export class TagsView extends ObservableReactComponent<TagViewProps> {
                                 />
                             </div>
                             <div className="tagsView-suggestions-box">
-                                {TagItem.AllTagCollectionDocs.map((doc, i) => {
+                                {TagItem.AllTagCollectionDocs.map(doc => {
                                     const tag = StrCast(doc.title);
                                     return (
                                         <Button
diff --git a/src/client/views/collections/CollectionCardDeckView.scss b/src/client/views/collections/CollectionCardDeckView.scss
index 5ccc3d9a8..e5fb7aba6 100644
--- a/src/client/views/collections/CollectionCardDeckView.scss
+++ b/src/client/views/collections/CollectionCardDeckView.scss
@@ -29,18 +29,13 @@
     transition: transform 0.3s cubic-bezier(0.455, 0.03, 0.515, 0.955);
 }
 
-.no-card-span{
+.no-card-span {
     position: relative;
     width: fit-content;
     text-align: center;
     font-size: 65px;
-    
-
-
 }
 
-
-
 .card-item-inactive,
 .card-item-active,
 .card-item {
@@ -50,7 +45,6 @@
     flex-direction: column;
 }
 
-
 .card-item-inactive {
     opacity: 0.5;
 }
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index fced9fd37..1952cc707 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -21,6 +21,7 @@ import { DocumentView } from '../nodes/DocumentView';
 import { GPTPopup, GPTPopupMode } from '../pdf/GPTPopup/GPTPopup';
 import './CollectionCardDeckView.scss';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
+import { List } from '../../../fields/List';
 
 enum cardSortings {
     Time = 'time',
@@ -42,7 +43,6 @@ enum cardSortings {
 @observer
 export class CollectionCardView extends CollectionSubView() {
     private _dropDisposer?: DragManager.DragDropDisposer;
-    private _childDocumentWidth = 600; // target width of a Doc...
     private _disposers: { [key: string]: IReactionDisposer } = {};
     private _textToDoc = new Map<string, Doc>();
 
@@ -77,6 +77,12 @@ export class CollectionCardView extends CollectionSubView() {
         this.setRegenerateCallback();
     }
 
+    protected createDashEventsTarget = (ele: HTMLDivElement | null) => {
+        this._dropDisposer?.();
+        if (ele) {
+            this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
+        }
+    };
     /**
      * Callback to ensure gpt's text versions of the child docs are updated
      */
@@ -94,10 +100,7 @@ export class CollectionCardView extends CollectionSubView() {
     };
 
     componentDidMount() {
-        this.Document.childFilters_boolean = 'OR';
-        this.childDocsWithoutLinks.forEach(c => {
-            c[DocData].showIconTags = true;
-        });
+        this.Document.childFilters_boolean = 'OR'; // bcz: really shouldn't be assigning to fields from within didMount -- this should be a default/override beahavior somehow
 
         // Reaction to cardSort changes
         this._disposers.sort = reaction(
@@ -117,28 +120,24 @@ export class CollectionCardView extends CollectionSubView() {
         this._dropDisposer?.();
     }
 
-    @computed get cardSort_customField() {
-        return StrCast(this.Document.cardSort_customField) as 'chat' | 'star' | 'idea' | 'like';
-    }
-
     @computed get cardSort() {
         return StrCast(this.Document.cardSort) as cardSortings;
     }
 
     /**
-     * how much to scale down the contents of the view so that everything will fit
+     * The child documents to be rendered-- either all of them except the Links or the docs in the currently active
+     * custom group
      */
-    @computed get fitContentScale() {
-        const length = Math.min(this.childDocsWithoutLinks.length, this._maxRowCount);
-        return (this._childDocumentWidth * length) / this._props.PanelWidth();
+    @computed get childDocsWithoutLinks() {
+        return (this.childDocList as Doc[]).filter(l => l.type !== DocumentType.LINK);
     }
 
     /**
-     * The child documents to be rendered-- either all of them except the Links or the docs in the currently active
-     * custom group
+     * how much to scale down the contents of the view so that everything will fit
      */
-    @computed get childDocsWithoutLinks() {
-        return this.childDocs.filter(l => l.type !== DocumentType.LINK);
+    @computed get fitContentScale() {
+        const length = Math.min(this.childDocsWithoutLinks.length, this._maxRowCount);
+        return (this.childPanelWidth() * length) / this._props.PanelWidth();
     }
 
     /**
@@ -154,7 +153,7 @@ export class CollectionCardView extends CollectionSubView() {
      * Number of rows of cards to be rendered
      */
     @computed get numRows() {
-        return Math.ceil(this.sortedDocs.length / 10);
+        return Math.ceil(this.sortedDocs.length / this._maxRowCount);
     }
 
     @action
@@ -177,8 +176,7 @@ export class CollectionCardView extends CollectionSubView() {
      */
     inactiveDocs = () => this.childDocsWithoutLinks.filter(d => !DocumentView.SelectedDocs().includes(d));
 
-    panelWidth = () => this._childDocumentWidth;
-    panelHeight = (layout: Doc) => () => (this.panelWidth() * NumCast(layout._height)) / NumCast(layout._width);
+    childPanelWidth = () => NumCast(this.layoutDoc.childPanelWidth, this._props.PanelWidth() / 2);
     onChildDoubleClick = () => ScriptCast(this.layoutDoc.onChildDoubleClick);
     isContentActive = () => this._props.isSelected() || this._props.isContentActive() || this._props.isAnyChildContentActive();
     isChildContentActive = () => !!this.isContentActive();
@@ -245,6 +243,7 @@ export class CollectionCardView extends CollectionSubView() {
         const currRow = Math.floor((mouseY - 100) / rowHeight); //rows start at 0
 
         if (adjustedX < 0) {
+            console.log('DROP INDEX NO ');
             return 0; // Before the first column
         }
 
@@ -259,6 +258,7 @@ export class CollectionCardView extends CollectionSubView() {
 
             index = Math.floor(adjustedX / cardWidth) + currRow * this._maxRowCount;
         }
+        console.log('DROP INDEX = ' + index);
         return index;
     };
 
@@ -280,21 +280,42 @@ export class CollectionCardView extends CollectionSubView() {
         }
     };
 
+    /**
+     * Handles external drop of images/PDFs etc from outside Dash.
+     */
+    onExternalDrop = async (e: React.DragEvent): Promise<void> => {
+        super.onExternalDrop(e, {});
+    };
+
     /**
      * Resets all the doc dragging vairables once a card is dropped
      * @param e
      * @param de drop event
      * @returns true if a card has been dropped, falls if not
      */
-    onInternalDrop = undoable((e: Event, de: DragManager.DropEvent) => {
-        if (de.complete.docDragData) {
-            this._isACardBeingDragged = false;
-            this._docDraggedIndex = -1;
-            e.stopPropagation();
-            return true;
-        }
-        return false;
-    }, '');
+    onInternalDrop = undoable(
+        action((e: Event, de: DragManager.DropEvent) => {
+            if (de.complete.docDragData) {
+                this._isACardBeingDragged = false;
+                const dragIndex = this._docDraggedIndex;
+                if (dragIndex > -1) {
+                    this._docDraggedIndex = -1;
+                    const draggedDoc = DragManager.docsBeingDragged[0];
+                    const sorted = this.sortedDocs;
+                    const originalIndex = sorted.findIndex(doc => doc === draggedDoc);
+
+                    this.Document.cardSort = '';
+                    sorted.splice(originalIndex, 1);
+                    sorted.splice(dragIndex, 0, draggedDoc);
+                    this.dataDoc[this.fieldKey] = new List<Doc>(sorted);
+                }
+                e.stopPropagation();
+                return true;
+            }
+            return false;
+        }),
+        ''
+    );
 
     @computed get sortedDocs() {
         return this.sort(this.childDocsWithoutLinks, this.cardSort, BoolCast(this.Document.cardSort_isDesc), this._docDraggedIndex);
@@ -348,28 +369,29 @@ export class CollectionCardView extends CollectionSubView() {
      * @returns
      */
     sort = (docs: Doc[], sortType: cardSortings, isDesc: boolean, dragIndex: number) => {
-        docs.sort((docA, docB) => {
-            const [typeA, typeB] = (() => {
-                switch (sortType) {
-                    case cardSortings.Time:
-                        return [DateCast(docA.author_date)?.date ?? Date.now(), DateCast(docB.author_date)?.date ?? Date.now()];
-                    case cardSortings.Color: {
-                        const d1 = DashColor(StrCast(docA.backgroundColor));
-                        const d2 = DashColor(StrCast(docB.backgroundColor));
-                        return [d1.hsv().hue(), d2.hsv().hue()];
+        sortType &&
+            docs.sort((docA, docB) => {
+                const [typeA, typeB] = (() => {
+                    switch (sortType) {
+                        case cardSortings.Time:
+                            return [DateCast(docA.author_date)?.date ?? Date.now(), DateCast(docB.author_date)?.date ?? Date.now()];
+                        case cardSortings.Color: {
+                            const d1 = DashColor(StrCast(docA.backgroundColor));
+                            const d2 = DashColor(StrCast(docB.backgroundColor));
+                            return [d1.hsv().hue(), d2.hsv().hue()];
+                        }
+                        case cardSortings.Tag:
+                            return [this.tagValue(docA) ?? 9999, this.tagValue(docB) ?? 9999];
+                        case cardSortings.Chat:
+                            return [NumCast(docA.chatIndex) ?? 9999, NumCast(docB.chatIndex) ?? 9999];
+                        default:
+                            return [StrCast(docA.type), StrCast(docB.type)];
                     }
-                    case cardSortings.Tag:
-                        return [this.tagValue(docA) ?? 9999, this.tagValue(docB) ?? 9999];
-                    case cardSortings.Chat:
-                        return [NumCast(docA.chatIndex) ?? 9999, NumCast(docB.chatIndex) ?? 9999];
-                    default:
-                        return [StrCast(docA.type), StrCast(docB.type)];
-                }
-            })();
+                })();
 
-            const out = typeA < typeB ? -1 : typeA > typeB ? 1 : 0;
-            return isDesc ? out : -out;
-        });
+                const out = typeA < typeB ? -1 : typeA > typeB ? 1 : 0;
+                return isDesc ? out : -out;
+            });
         if (dragIndex != -1) {
             const draggedDoc = DragManager.docsBeingDragged[0];
             const originalIndex = docs.findIndex(doc => doc === draggedDoc);
@@ -396,9 +418,11 @@ export class CollectionCardView extends CollectionSubView() {
             ScreenToLocalTransform={screenToLocalTransform} // makes sure the box wrapper thing is in the right spot
             isContentActive={emptyFunction}
             isDocumentActive={this._props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this._props.isDocumentActive : this.isContentActive}
-            PanelWidth={this.panelWidth}
-            PanelHeight={this.panelHeight(doc)}
+            PanelWidth={this.childPanelWidth}
+            PanelHeight={() => this._props.PanelHeight() * this.fitContentScale}
+            dontCenter="y" // Don't center it vertically, because the grid it's in is already doing that and we don't want to do it twice.
             dragAction={(this.Document.childDragAction ?? this._props.childDragAction) as dropActionType}
+            showTags={true}
             dontHideOnDrag
         />
     );
@@ -415,18 +439,18 @@ export class CollectionCardView extends CollectionSubView() {
         // 13 - 3 = 10
         const totalCards = this.sortedDocs.length;
         // if 9 or less
-        if (index < totalCards - (totalCards % 10)) {
+        if (index < totalCards - (totalCards % this._maxRowCount)) {
             return this._maxRowCount;
         }
         // (3)
-        return totalCards % 10;
+        return totalCards % this._maxRowCount;
     };
     /**
      * Determines the index a card is in in a row
      * @param realIndex
      * @returns
      */
-    overflowIndexCalc = (realIndex: number) => realIndex % 10;
+    overflowIndexCalc = (realIndex: number) => realIndex % this._maxRowCount;
     /**
      * Translates the cards in the second rows and beyond over to the right
      * @param realIndex
@@ -434,7 +458,7 @@ export class CollectionCardView extends CollectionSubView() {
      * @param calcRowCards
      * @returns
      */
-    translateOverflowX = (realIndex: number, calcRowCards: number) => (realIndex < this._maxRowCount ? 0 : (10 - calcRowCards) * (this.panelWidth() / 2));
+    translateOverflowX = (realIndex: number, calcRowCards: number) => (realIndex < this._maxRowCount ? 0 : (this._maxRowCount - calcRowCards) * (this.childPanelWidth() / 2));
 
     /**
      * Determines how far to translate a card in the y direction depending on its index, whether or not its being hovered, or if it's selected
@@ -613,6 +637,9 @@ export class CollectionCardView extends CollectionSubView() {
                 const rowCenterIndex = Math.min(this._maxRowCount, sortedDocs.length - rowIndex * this._maxRowCount) / 2;
                 return (rowCenterIndex - indexInRow) * 100 - 50;
             };
+            const aspect = NumCast(doc.height) / NumCast(doc.width, 1);
+            const vscale = ((this._props.PanelHeight() * .95) * this.fitContentScale) / (aspect * this.childPanelWidth());
+            const hscale = this._maxRowCount / 2; // bcz: hack - the grid is divided evenly into maxRowCount cells, so the max scaling would be maxRowCount -- but making things that wide is ugly, so cap it off at half the window size
             return (
                 <div
                     key={doc[Id]}
@@ -629,13 +656,13 @@ export class CollectionCardView extends CollectionSubView() {
                         );
                     }}
                     style={{
-                        width: this.panelWidth(),
+                        width: this.childPanelWidth(),
                         height: 'max-content',
                         transform: `translateY(${this.calculateTranslateY(this._hoveredNodeIndex === index, isSelected, realIndex, amCards, calcRowIndex)}px)
-                                    translateX(calc(${(isSelected ? translateIfSelected() : 0) + '% + ' + this.translateOverflowX(realIndex, amCards) + 'px'}))
+                                    translateX(calc(${isSelected ? translateIfSelected() : 0}% + ${this.translateOverflowX(realIndex, amCards)}px))
                                     rotate(${!isSelected ? this.rotate(amCards, calcRowIndex) : 0}deg)
-                                    scale(${isSelected ? 2 : this._hoveredNodeIndex === index ? 1.05 : 1})`,
-                    }}
+                                    scale(${isSelected ? `${Math.min(hscale, vscale) * 100}%` : this._hoveredNodeIndex === index ? 1.05 : 1})`,
+                    }} // prettier-ignore
                     onMouseEnter={() => this.setHoveredNodeIndex(index)}>
                     {this.displayDoc(doc, childScreenToLocal)}
                 </div>
@@ -645,14 +672,13 @@ export class CollectionCardView extends CollectionSubView() {
 
     render() {
         const isEmpty = this.childDocsWithoutLinks.length === 0;
-        const transformValue = `scale(${1 / this.fitContentScale})`;
-        const heightValue = `${100 * this.fitContentScale}%`;
 
         return (
             <div
                 onPointerMove={e => this.onPointerMove(e)}
                 className="collectionCardView-outer"
                 ref={(ele: HTMLDivElement | null) => this.createDashEventsTarget(ele)}
+                onDrop={this.onExternalDrop.bind(this)}
                 style={{
                     background: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor) as string,
                     color: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color) as string,
@@ -660,8 +686,8 @@ export class CollectionCardView extends CollectionSubView() {
                 <div
                     className="card-wrapper"
                     style={{
-                        ...(!isEmpty && { transform: transformValue }),
-                        ...(!isEmpty && { height: heightValue }),
+                        ...(!isEmpty && { transform: `scale(${1 / this.fitContentScale})` }),
+                        ...(!isEmpty && { height: `${100 * this.fitContentScale}%` }),
                         gridAutoRows: `${100 / this.numRows}%`,
                     }}
                     onMouseLeave={() => this.setHoveredNodeIndex(-1)}>
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index ad868fd1e..4609be374 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -1,4 +1,3 @@
-/* eslint-disable react/jsx-props-no-spreading */
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { IReactionDisposer, action, computed, makeObservable, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index e97ee713e..1ac0b6d70 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -1,4 +1,3 @@
-/* eslint-disable react/jsx-props-no-spreading */
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import * as CSS from 'csstype';
 import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction, runInAction } from 'mobx';
@@ -540,7 +539,6 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         if (this.pivotField) {
             const types = docList.length ? docList.map(d => typeof d[key]) : this.filteredChildren.map(d => typeof d[key]);
             if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) {
-                // eslint-disable-next-line prefer-destructuring
                 type = types[0];
             }
         }
@@ -577,7 +575,6 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         let type: 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function' | undefined;
         const types = docList.length ? docList.map(d => typeof d[key]) : this.filteredChildren.map(d => typeof d[key]);
         if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) {
-            // eslint-disable-next-line prefer-destructuring
             type = types[0];
         }
         const rows = () => (!this.isStackingView ? 1 : Math.max(1, Math.min(docList.length, Math.floor((this._props.PanelWidth() - 2 * this.xMargin) / (this.columnWidth + this.gridGap)))));
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 6aca8f2ca..99373da04 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -122,6 +122,9 @@ export function CollectionSubView<X>() {
                 );
             return validPairs.map(({ data, layout }) => ({ data: data as Doc, layout: layout! })); // this mapping is a bit of a hack to coerce types
         }
+        /**
+         * This is the raw, stored list of children on a collection.  If you modify this list, the database will be updated
+         */
         @computed get childDocList() {
             return Cast(this.dataField, listSpec(Doc));
         }
@@ -218,7 +221,6 @@ export function CollectionSubView<X>() {
                 if (!cursors) {
                     proto.cursors = cursors = new List<CursorField>();
                 }
-                // eslint-disable-next-line no-cond-assign
                 if (cursors.length > 0 && (ind = cursors.findIndex(entry => entry.data.metadata.id === id)) > -1) {
                     cursors[ind].setPosition(pos);
                 } else {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index dbf781e63..cbbf063b4 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,4 +1,3 @@
-/* eslint-disable react/jsx-props-no-spreading */
 import { Bezier } from 'bezier-js';
 import { Colors } from 'browndash-components';
 import { Property } from 'csstype';
@@ -1211,7 +1210,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 for (let j = 0; j < otherCtrlPts.length - 3; j += 4) {
                     const neighboringSegment = i === j || i === j - 4 || i === j + 4;
                     // Ensuring that the curve intersected by the eraser is not checked for further ink intersections.
-                    // eslint-disable-next-line no-continue
                     if (ink?.Document === otherInk.Document && neighboringSegment) continue;
 
                     const otherCurve = new Bezier(otherCtrlPts.slice(j, j + 4).map(p => ({ x: p.X, y: p.Y })));
@@ -1481,8 +1479,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const childData = entry.pair.data;
         return (
             <CollectionFreeFormDocumentView
-                // eslint-disable-next-line react/jsx-props-no-spreading, @typescript-eslint/no-explicit-any
-                {...(OmitKeys(entry, ['replica', 'pair']).omit as any)}
+                {...(OmitKeys(entry, ['replica', 'pair']).omit as { x: number; y: number; z: number; width: number; height: number })}
                 key={childLayout[Id] + (entry.replica || '')}
                 Document={childLayout}
                 reactParent={this}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 4c357cf45..758e70508 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -67,6 +67,7 @@ export interface DocumentViewProps extends FieldViewSharedProps {
     hideCaptions?: boolean;
     contentPointerEvents?: Property.PointerEvents | undefined; // pointer events allowed for content of a document view.  eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents
     dontCenter?: 'x' | 'y' | 'xy';
+    showTags?: boolean;
     childHideDecorationTitle?: boolean;
     childHideResizeHandles?: boolean;
     childDragAction?: dropActionType; // allows child documents to be dragged out of collection without holding the embedKey or dragging the doc decorations title bar.
@@ -1126,6 +1127,10 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
     @observable public static CurrentlyPlaying: DocumentView[] = []; // audio or video media views that are currently playing
     @observable public TagPanelHeight = 0;
 
+    @computed get showTags() {
+        return this.Document._layout_showTags || this._props.showTags;
+    }
+
     @computed private get shouldNotScale() {
         return (this.layout_fitWidth && !this.nativeWidth) || this.ComponentView?.isUnstyledView?.();
     }
diff --git a/src/client/views/nodes/IconTagBox.scss b/src/client/views/nodes/IconTagBox.scss
index 211a961c1..90cc06092 100644
--- a/src/client/views/nodes/IconTagBox.scss
+++ b/src/client/views/nodes/IconTagBox.scss
@@ -2,23 +2,25 @@
 
 .card-button-container {
     display: flex;
-    padding: 3px;
-    position: absolute;
+    position: relative;
     pointer-events: none;
-    background-color: rgb(218, 218, 218); 
+    background-color: rgb(218, 218, 218);
     border-radius: 50px;
-    transform: translateY(25px);
-    align-items: center; 
-    justify-content: start;
+    align-items: center;
+    gap: 5px;
+    padding-left: 5px;
+    padding-right: 5px;
+    padding-top: 2px;
+    padding-bottom: 2px;
 
     button {
         pointer-events: auto;
-        transform: translateY(-7.5px);
-        width: 30px;
-        height: 30px;
+        width: 20px;
+        height: 20px;
+        margin: auto;
+        padding: 0;
         border-radius: 50%;
         background-color: $dark-gray;
-        margin: 5px; 
         background-color: transparent;
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/IconTagBox.tsx b/src/client/views/nodes/IconTagBox.tsx
index f86372ec0..3d021fd73 100644
--- a/src/client/views/nodes/IconTagBox.tsx
+++ b/src/client/views/nodes/IconTagBox.tsx
@@ -6,18 +6,18 @@ import { observer } from 'mobx-react';
 import React from 'react';
 import { numberRange } from '../../../Utils';
 import { Doc, StrListCast } from '../../../fields/Doc';
-import { DocData } from '../../../fields/DocSymbols';
-import { BoolCast, DocCast, NumCast, StrCast } from '../../../fields/Types';
-import { CollectionViewType } from '../../documents/DocumentTypes';
+import { StrCast } from '../../../fields/Types';
 import { SnappingManager } from '../../util/SnappingManager';
 import { undoable } from '../../util/UndoManager';
 import { MainView } from '../MainView';
 import { ObservableReactComponent } from '../ObservableReactComponent';
 import { PropertiesView } from '../PropertiesView';
+import { DocumentView } from './DocumentView';
 import './IconTagBox.scss';
 
 export interface IconTagProps {
-    doc: Doc;
+    Views: DocumentView[];
+    IsEditing: boolean;
 }
 
 /**
@@ -26,59 +26,16 @@ export interface IconTagProps {
  */
 @observer
 export class IconTagBox extends ObservableReactComponent<IconTagProps> {
-    @computed
-    get currentScale() {
-        return NumCast((this._props.doc.embedContainer as Doc)?._freeform_scale, 1);
-    }
-
     constructor(props: IconTagProps) {
         super(props);
     }
 
-    componentDidUpdate(): void {
-        this._props.doc[DocData].tagHeight = 36 * this.currentScale;
+    @computed get View() {
+        return this._props.Views.lastElement();
+    }
+    @computed get currentScale() {
+        return this.View?.screenToLocalScale();
     }
-
-    /**
-     * Renders the buttons to customize sorting depending on which group the card belongs to and the amount of total groups
-     * @param doc
-     * @param cardSort
-     * @returns
-     */
-    renderButtons = (doc: Doc): JSX.Element | null => {
-        const amButtons = StrListCast(Doc.UserDoc().myFilterHotKeyTitles).length + 1;
-
-        const keys = StrListCast(Doc.UserDoc().myFilterHotKeyTitles);
-
-        const totalWidth = (amButtons - 1) * 35 + (amButtons - 1) * 2 * 5 + 6;
-
-        const iconMap = (buttonID: number) => {
-            return StrCast(Doc.UserDoc()[keys[buttonID]]) as IconProp;
-        };
-
-        const isCard = DocCast(this._props.doc.embedContainer).type_collection === CollectionViewType.Card;
-
-        return (
-            <div
-                className="card-button-container"
-                style={{
-                    transformOrigin: 'top left',
-                    transform: `scale(${isCard ? 2 : 0.6 / this.currentScale})
-                translateY(${doc[DocData].showLabels ? NumCast(doc[DocData].keywordHeight) * (1 - this.currentScale) : 0}px)
-                `,
-                    width: `${totalWidth}px`,
-                    fontSize: '50px',
-                }}>
-                {numberRange(amButtons - 1).map(i => (
-                    <Tooltip key={i} title={<div className="dash-tooltip">Click to add/remove this card from the {iconMap(i).toString()} group</div>}>
-                        <button key={i} type="button" onClick={() => this.toggleButton(doc, iconMap(i))}>
-                            {this.getButtonIcon(doc, iconMap(i))}
-                        </button>
-                    </Tooltip>
-                ))}
-            </div>
-        );
-    };
 
     /**
      * Opens the filter panel in the properties menu
@@ -93,13 +50,13 @@ export class IconTagBox extends ObservableReactComponent<IconTagProps> {
     };
 
     /**
-     * Toggles the buttons between on and off when creating custom sort groupings/changing those created by gpt
-     * @param childPairIndex
      * @param buttonID
      * @param doc
      */
-    toggleButton = undoable((doc: Doc, icon: string) => {
-        BoolCast(doc[icon]) ? (doc[icon] = false) : (doc[icon] = true);
+    setIconTag = undoable((icon: string, state: boolean) => {
+        this._props.Views.forEach(view => {
+            view.dataDoc[icon] = state;
+        });
     }, 'toggle card tag');
 
     /**
@@ -112,10 +69,43 @@ export class IconTagBox extends ObservableReactComponent<IconTagProps> {
         const isActive = doc[icon.toString()];
         const color = isActive ? '#4476f7' : '#323232';
 
-        return <FontAwesomeIcon icon={icon} style={{ color, height: '30px', width: '30px' }} />;
+        return <FontAwesomeIcon icon={icon} style={{ color, height: '20px', width: '20px' }} />;
     };
 
+    /**
+     * Renders the buttons to customize sorting depending on which group the card belongs to and the amount of total groups
+     */
     render() {
-        return <>{this.renderButtons(this._props.doc)}</>;
+        const amButtons = StrListCast(Doc.UserDoc().myFilterHotKeyTitles).length + 1;
+
+        const keys = StrListCast(Doc.UserDoc().myFilterHotKeyTitles);
+
+        const iconMap = (buttonID: number) => {
+            return StrCast(Doc.UserDoc()[keys[buttonID]]) as IconProp;
+        };
+        const buttons = numberRange(amButtons - 1)
+            .filter(i => this._props.IsEditing || this.View.Document[iconMap(i).toString()] || (DocumentView.Selected.length === 1 && this.View.IsSelected))
+            .map(i => (
+                <Tooltip key={i} title={<div className="dash-tooltip">Click to add/remove this card from the {iconMap(i).toString()} group</div>}>
+                    <button
+                        key={i}
+                        type="button"
+                        onClick={() => {
+                            const state = this.View.Document[iconMap(i).toString()];
+                            this.setIconTag(iconMap(i), !state);
+                        }}>
+                        {this.getButtonIcon(this.View.Document, iconMap(i))}
+                    </button>
+                </Tooltip>
+            ));
+        return !buttons.length ? null : (
+            <div
+                className="card-button-container"
+                style={{
+                    fontSize: '50px',
+                }}>
+                {buttons}
+            </div>
+        );
     }
 }
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 343e255dc..e0331a422 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -275,6 +275,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                 ele.append(contents);
             }
             this._selectionHTML = ele?.innerHTML;
+            // eslint-disable-next-line @typescript-eslint/no-unused-vars
         } catch (e) {
             /* empty */
         }
diff --git a/src/client/views/search/FaceRecognitionHandler.tsx b/src/client/views/search/FaceRecognitionHandler.tsx
index c507e54b6..6f70e96ab 100644
--- a/src/client/views/search/FaceRecognitionHandler.tsx
+++ b/src/client/views/search/FaceRecognitionHandler.tsx
@@ -30,6 +30,7 @@ import { DocumentManager } from '../../util/DocumentManager';
  *     face_annos - a list of face annotations, where each anno has
  */
 export class FaceRecognitionHandler {
+    // eslint-disable-next-line no-use-before-define
     static _instance: FaceRecognitionHandler;
     private _apiModelReady = false;
     private _pendingAPIModelReadyDocs: Doc[] = [];
@@ -221,7 +222,7 @@ export class FaceRecognitionHandler {
                                 const annos = [] as Doc[];
                                 const scale = NumCast(imgDoc.data_nativeWidth) / img.width;
                                 const showTags= imgDocFaceDescriptions.length > 1;
-                                imgDocFaceDescriptions.forEach((fd, i) => {
+                                imgDocFaceDescriptions.forEach(fd => {
                                     const faceDescriptor = new List<number>(Array.from(fd.descriptor));
                                     const matchedUniqueFace = this.findMatchingFaceDoc(fd.descriptor) ?? this.createUniqueFaceDoc(activeDashboard);
                                     const faceAnno = Docs.Create.FreeformDocument([], {
-- 
cgit v1.2.3-70-g09d2

