From 8a0ae4e42d217a222d6a127ec640d5f1113832a0 Mon Sep 17 00:00:00 2001
From: Sophie Zhang <sophie_zhang@brown.edu>
Date: Mon, 16 Oct 2023 17:08:24 -0400
Subject: text box

---
 src/client/views/nodes/trails/PresElementBox.tsx | 35 ++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

(limited to 'src/client/views/nodes/trails/PresElementBox.tsx')

diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 82ed9e8d5..534a0f2aa 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -28,6 +28,7 @@ import React = require('react');
 import { TreeView } from '../../collections/TreeView';
 import { BranchingTrailManager } from '../../../util/BranchingTrailManager';
 import { MultiToggle, Type } from 'browndash-components';
+import { gptTrailSlideCustomization } from '../../../apis/gpt/GPT';
 /**
  * This class models the view a document added to presentation will have in the presentation.
  * It involves some functionality for its buttons and options.
@@ -434,6 +435,27 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return width;
     }
 
+    // GPT
+
+    @action
+    customizeWithGPT = async (input: string) => {
+        const testInput = 'change title to Customized Slide, transition for 2.3s with fade in effect';
+
+        try {
+            const res = await gptTrailSlideCustomization(testInput);
+            if (typeof res === 'string') {
+                const resObj = JSON.parse(res);
+                console.log('Result ', resObj);
+
+                for (let key in resObj) {
+                    this.rootDoc[key] = resObj[key];
+                }
+            }
+        } catch (err) {
+            console.error(err);
+        }
+    };
+
     @computed get presButtons() {
         const presBox = this.presBox;
         const presBoxColor = StrCast(presBox?._backgroundColor);
@@ -537,6 +559,19 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 </div>
             </Tooltip>
         );
+        items.push(
+            <Tooltip key="customize" title={<div className="dash-tooltip">Customize</div>}>
+                <div
+                    className={'slideButton'}
+                    onClick={() => {
+                        PresBox.Instance.setChatActive(true);
+                        PresBox.Instance.slideToModify = this.rootDoc;
+                        // this.customizeWithGPT('');
+                    }}>
+                    <FontAwesomeIcon icon={'message'} onPointerDown={e => e.stopPropagation()} />
+                </div>
+            </Tooltip>
+        );
         return items;
     }
 
-- 
cgit v1.2.3-70-g09d2


From 612f3d05927113c7b010861c17765fcead4752e5 Mon Sep 17 00:00:00 2001
From: Sophie Zhang <sophie_zhang@brown.edu>
Date: Thu, 19 Oct 2023 00:58:37 -0400
Subject: feat: recording

---
 package-lock.json                                  |   28 +
 package.json                                       |    1 +
 report.20231019.001427.35449.0.001.json            | 1264 ++++++++++++++++++++
 src/client/apis/gpt/GPT.ts                         |   10 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |    1 +
 src/client/views/nodes/trails/PresBox.scss         |   13 +-
 src/client/views/nodes/trails/PresBox.tsx          |   71 +-
 src/client/views/nodes/trails/PresElementBox.tsx   |    2 +
 8 files changed, 1374 insertions(+), 16 deletions(-)
 create mode 100644 report.20231019.001427.35449.0.001.json

(limited to 'src/client/views/nodes/trails/PresElementBox.tsx')

diff --git a/package-lock.json b/package-lock.json
index fbfdf0136..e84f33046 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -24804,6 +24804,16 @@
                 "react-is": "^16.8.1"
             }
         },
+        "react-textarea-autosize": {
+            "version": "8.5.3",
+            "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.3.tgz",
+            "integrity": "sha512-XT1024o2pqCuZSuBt9FwHlaDeNtVrtCXu0Rnz88t1jUGheCLa3PhjE1GH8Ctm2axEtvdCl5SUHYschyQ0L5QHQ==",
+            "requires": {
+                "@babel/runtime": "^7.20.13",
+                "use-composed-ref": "^1.3.0",
+                "use-latest": "^1.2.1"
+            }
+        },
         "react-themeable": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/react-themeable/-/react-themeable-1.1.0.tgz",
@@ -28351,6 +28361,24 @@
                 "fast-deep-equal": "^3.1.3"
             }
         },
+        "use-composed-ref": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.3.0.tgz",
+            "integrity": "sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ=="
+        },
+        "use-isomorphic-layout-effect": {
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz",
+            "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA=="
+        },
+        "use-latest": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.2.1.tgz",
+            "integrity": "sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw==",
+            "requires": {
+                "use-isomorphic-layout-effect": "^1.1.1"
+            }
+        },
         "use-memo-one": {
             "version": "1.1.3",
             "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz",
diff --git a/package.json b/package.json
index d013ad74d..d8996f57e 100644
--- a/package.json
+++ b/package.json
@@ -305,6 +305,7 @@
         "react-reveal": "^1.2.2",
         "react-select": "^3.2.0",
         "react-table": "^6.11.5",
+        "react-textarea-autosize": "^8.5.3",
         "react-transition-group": "^4.4.2",
         "react-typist": "^2.0.5",
         "readline": "^1.3.0",
diff --git a/report.20231019.001427.35449.0.001.json b/report.20231019.001427.35449.0.001.json
new file mode 100644
index 000000000..9dbe388b0
--- /dev/null
+++ b/report.20231019.001427.35449.0.001.json
@@ -0,0 +1,1264 @@
+
+{
+  "header": {
+    "reportVersion": 1,
+    "event": "Allocation failed - JavaScript heap out of memory",
+    "trigger": "FatalError",
+    "filename": "report.20231019.001427.35449.0.001.json",
+    "dumpEventTime": "2023-10-19T00:14:27Z",
+    "dumpEventTimeStamp": "1697688867046",
+    "processId": 35449,
+    "cwd": "/Users/smallwhale/Desktop/Projects/Dash-Web",
+    "commandLine": [
+      "/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node",
+      "--max-old-space-size=2048",
+      "/Users/smallwhale/Desktop/Projects/Dash-Web/node_modules/ts-node-dev/lib/wrap.js",
+      "/Users/smallwhale/Desktop/Projects/Dash-Web/node_modules/fork-ts-checker-webpack-plugin/lib/service.js"
+    ],
+    "nodejsVersion": "v12.16.0",
+    "wordSize": 64,
+    "arch": "x64",
+    "platform": "darwin",
+    "componentVersions": {
+      "node": "12.16.0",
+      "v8": "7.8.279.23-node.31",
+      "uv": "1.34.0",
+      "zlib": "1.2.11",
+      "brotli": "1.0.7",
+      "ares": "1.15.0",
+      "modules": "72",
+      "nghttp2": "1.40.0",
+      "napi": "5",
+      "llhttp": "2.0.4",
+      "http_parser": "2.9.3",
+      "openssl": "1.1.1d",
+      "cldr": "35.1",
+      "icu": "64.2",
+      "tz": "2019c",
+      "unicode": "12.1"
+    },
+    "release": {
+      "name": "node",
+      "lts": "Erbium",
+      "headersUrl": "https://nodejs.org/download/release/v12.16.0/node-v12.16.0-headers.tar.gz",
+      "sourceUrl": "https://nodejs.org/download/release/v12.16.0/node-v12.16.0.tar.gz"
+    },
+    "osName": "Darwin",
+    "osRelease": "22.4.0",
+    "osVersion": "Darwin Kernel Version 22.4.0: Mon Mar  6 21:01:02 PST 2023; root:xnu-8796.101.5~3/RELEASE_ARM64_T8112",
+    "osMachine": "x86_64",
+    "cpus": [
+      {
+        "model": "Apple M2",
+        "speed": 2400,
+        "user": 12520700,
+        "nice": 0,
+        "sys": 7484500,
+        "idle": 106635510,
+        "irq": 0
+      },
+      {
+        "model": "Apple M2",
+        "speed": 2400,
+        "user": 11888410,
+        "nice": 0,
+        "sys": 6595360,
+        "idle": 108221120,
+        "irq": 0
+      },
+      {
+        "model": "Apple M2",
+        "speed": 2400,
+        "user": 10596860,
+        "nice": 0,
+        "sys": 5620320,
+        "idle": 110583510,
+        "irq": 0
+      },
+      {
+        "model": "Apple M2",
+        "speed": 2400,
+        "user": 9300520,
+        "nice": 0,
+        "sys": 4805160,
+        "idle": 112784220,
+        "irq": 0
+      },
+      {
+        "model": "Apple M2",
+        "speed": 2400,
+        "user": 6989740,
+        "nice": 0,
+        "sys": 1675740,
+        "idle": 118483920,
+        "irq": 0
+      },
+      {
+        "model": "Apple M2",
+        "speed": 2400,
+        "user": 4302540,
+        "nice": 0,
+        "sys": 1125240,
+        "idle": 121745690,
+        "irq": 0
+      },
+      {
+        "model": "Apple M2",
+        "speed": 2400,
+        "user": 2444020,
+        "nice": 0,
+        "sys": 714220,
+        "idle": 124035440,
+        "irq": 0
+      },
+      {
+        "model": "Apple M2",
+        "speed": 2400,
+        "user": 1720230,
+        "nice": 0,
+        "sys": 536420,
+        "idle": 124948860,
+        "irq": 0
+      }
+    ],
+    "networkInterfaces": [
+      {
+        "name": "lo0",
+        "internal": true,
+        "mac": "00:00:00:00:00:00",
+        "address": "127.0.0.1",
+        "netmask": "255.0.0.0",
+        "family": "IPv4"
+      },
+      {
+        "name": "lo0",
+        "internal": true,
+        "mac": "00:00:00:00:00:00",
+        "address": "::1",
+        "netmask": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
+        "family": "IPv6",
+        "scopeid": 0
+      },
+      {
+        "name": "lo0",
+        "internal": true,
+        "mac": "00:00:00:00:00:00",
+        "address": "fe80::1",
+        "netmask": "ffff:ffff:ffff:ffff::",
+        "family": "IPv6",
+        "scopeid": 1
+      },
+      {
+        "name": "anpi1",
+        "internal": false,
+        "mac": "42:10:d5:5b:93:9f",
+        "address": "fe80::4010:d5ff:fe5b:939f",
+        "netmask": "ffff:ffff:ffff:ffff::",
+        "family": "IPv6",
+        "scopeid": 4
+      },
+      {
+        "name": "anpi0",
+        "internal": false,
+        "mac": "42:10:d5:5b:93:9e",
+        "address": "fe80::4010:d5ff:fe5b:939e",
+        "netmask": "ffff:ffff:ffff:ffff::",
+        "family": "IPv6",
+        "scopeid": 5
+      },
+      {
+        "name": "ap1",
+        "internal": false,
+        "mac": "be:3e:53:8e:59:0e",
+        "address": "fe80::bc3e:53ff:fe8e:590e",
+        "netmask": "ffff:ffff:ffff:ffff::",
+        "family": "IPv6",
+        "scopeid": 11
+      },
+      {
+        "name": "en0",
+        "internal": false,
+        "mac": "9c:3e:53:8e:59:0e",
+        "address": "fe80::1813:6293:c0ab:dc3e",
+        "netmask": "ffff:ffff:ffff:ffff::",
+        "family": "IPv6",
+        "scopeid": 12
+      },
+      {
+        "name": "en0",
+        "internal": false,
+        "mac": "9c:3e:53:8e:59:0e",
+        "address": "10.38.55.120",
+        "netmask": "255.255.192.0",
+        "family": "IPv4"
+      },
+      {
+        "name": "en0",
+        "internal": false,
+        "mac": "9c:3e:53:8e:59:0e",
+        "address": "2620:6e:6000:3100:1005:c105:5692:58fb",
+        "netmask": "ffff:ffff:ffff:ffff::",
+        "family": "IPv6",
+        "scopeid": 0
+      },
+      {
+        "name": "en0",
+        "internal": false,
+        "mac": "9c:3e:53:8e:59:0e",
+        "address": "2620:6e:6000:3100:f817:2dd1:7d87:eac0",
+        "netmask": "ffff:ffff:ffff:ffff::",
+        "family": "IPv6",
+        "scopeid": 0
+      },
+      {
+        "name": "en0",
+        "internal": false,
+        "mac": "9c:3e:53:8e:59:0e",
+        "address": "fd33:5b24:c439:447c:1c8e:ee8a:48d8:e08e",
+        "netmask": "ffff:ffff:ffff:ffff::",
+        "family": "IPv6",
+        "scopeid": 0
+      },
+      {
+        "name": "en0",
+        "internal": false,
+        "mac": "9c:3e:53:8e:59:0e",
+        "address": "fdf0:d80b:58c0:4218:18e3:aed6:fbb3:1296",
+        "netmask": "ffff:ffff:ffff:ffff::",
+        "family": "IPv6",
+        "scopeid": 0
+      },
+      {
+        "name": "awdl0",
+        "internal": false,
+        "mac": "e2:65:e7:ac:a7:5d",
+        "address": "fe80::e065:e7ff:feac:a75d",
+        "netmask": "ffff:ffff:ffff:ffff::",
+        "family": "IPv6",
+        "scopeid": 13
+      },
+      {
+        "name": "llw0",
+        "internal": false,
+        "mac": "e2:65:e7:ac:a7:5d",
+        "address": "fe80::e065:e7ff:feac:a75d",
+        "netmask": "ffff:ffff:ffff:ffff::",
+        "family": "IPv6",
+        "scopeid": 14
+      },
+      {
+        "name": "utun0",
+        "internal": false,
+        "mac": "00:00:00:00:00:00",
+        "address": "fe80::272c:9ded:66c9:4c0e",
+        "netmask": "ffff:ffff:ffff:ffff::",
+        "family": "IPv6",
+        "scopeid": 15
+      },
+      {
+        "name": "utun1",
+        "internal": false,
+        "mac": "00:00:00:00:00:00",
+        "address": "fe80::75dc:d3c9:41be:fe2b",
+        "netmask": "ffff:ffff:ffff:ffff::",
+        "family": "IPv6",
+        "scopeid": 16
+      },
+      {
+        "name": "utun2",
+        "internal": false,
+        "mac": "00:00:00:00:00:00",
+        "address": "fe80::ce81:b1c:bd2c:69e",
+        "netmask": "ffff:ffff:ffff:ffff::",
+        "family": "IPv6",
+        "scopeid": 17
+      }
+    ],
+    "host": "sophies-mbp-612.devices.brown.edu"
+  },
+  "javascriptStack": {
+    "message": "No stack.",
+    "stack": [
+      "Unavailable."
+    ]
+  },
+  "nativeStack": [
+    {
+      "pc": "0x000000010015c8ca",
+      "symbol": "report::TriggerNodeReport(v8::Isolate*, node::Environment*, char const*, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&, v8::Local<v8::String>) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]"
+    },
+    {
+      "pc": "0x0000000100080f3e",
+      "symbol": "node::OnFatalError(char const*, char const*) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]"
+    },
+    {
+      "pc": "0x0000000100185467",
+      "symbol": "v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]"
+    },
+    {
+      "pc": "0x0000000100185403",
+      "symbol": "v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]"
+    },
+    {
+      "pc": "0x000000010030b5f5",
+      "symbol": "v8::internal::Heap::FatalProcessOutOfMemory(char const*) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]"
+    },
+    {
+      "pc": "0x000000010030ccc4",
+      "symbol": "v8::internal::Heap::RecomputeLimits(v8::internal::GarbageCollector) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]"
+    },
+    {
+      "pc": "0x0000000100309b37",
+      "symbol": "v8::internal::Heap::PerformGarbageCollection(v8::internal::GarbageCollector, v8::GCCallbackFlags) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]"
+    },
+    {
+      "pc": "0x0000000100307afd",
+      "symbol": "v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]"
+    },
+    {
+      "pc": "0x00000001003132ba",
+      "symbol": "v8::internal::Heap::AllocateRawWithLightRetry(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]"
+    },
+    {
+      "pc": "0x0000000100313341",
+      "symbol": "v8::internal::Heap::AllocateRawWithRetryOrFail(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]"
+    },
+    {
+      "pc": "0x00000001002e065b",
+      "symbol": "v8::internal::Factory::NewFillerObject(int, bool, v8::internal::AllocationType, v8::internal::AllocationOrigin) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]"
+    },
+    {
+      "pc": "0x0000000100618a18",
+      "symbol": "v8::internal::Runtime_AllocateInYoungGeneration(int, unsigned long*, v8::internal::Isolate*) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]"
+    },
+    {
+      "pc": "0x0000000100950c19",
+      "symbol": "Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]"
+    },
+    {
+      "pc": "0x00002368c975aaed",
+      "symbol": ""
+    }
+  ],
+  "javascriptHeap": {
+    "totalMemory": 2152210432,
+    "totalCommittedMemory": 2150555464,
+    "usedMemory": 2140643800,
+    "availableMemory": 47978928,
+    "memoryLimit": 2197815296,
+    "heapSpaces": {
+      "read_only_space": {
+        "memorySize": 262144,
+        "committedMemory": 33088,
+        "capacity": 32808,
+        "used": 32808,
+        "available": 0
+      },
+      "new_space": {
+        "memorySize": 2097152,
+        "committedMemory": 1158104,
+        "capacity": 1047456,
+        "used": 110920,
+        "available": 936536
+      },
+      "old_space": {
+        "memorySize": 1951547392,
+        "committedMemory": 1951490488,
+        "capacity": 1945024816,
+        "used": 1944634744,
+        "available": 390072
+      },
+      "code_space": {
+        "memorySize": 14585856,
+        "committedMemory": 14251936,
+        "capacity": 12921184,
+        "used": 12921184,
+        "available": 0
+      },
+      "map_space": {
+        "memorySize": 1576960,
+        "committedMemory": 1480920,
+        "capacity": 1273280,
+        "used": 1273280,
+        "available": 0
+      },
+      "large_object_space": {
+        "memorySize": 182091776,
+        "committedMemory": 182091776,
+        "capacity": 181668080,
+        "used": 181668080,
+        "available": 0
+      },
+      "code_large_object_space": {
+        "memorySize": 49152,
+        "committedMemory": 49152,
+        "capacity": 2784,
+        "used": 2784,
+        "available": 0
+      },
+      "new_large_object_space": {
+        "memorySize": 0,
+        "committedMemory": 0,
+        "capacity": 1047456,
+        "used": 0,
+        "available": 1047456
+      }
+    }
+  },
+  "resourceUsage": {
+    "userCpuSeconds": 285.707,
+    "kernelCpuSeconds": 105.722,
+    "cpuConsumptionPercent": 80.0469,
+    "maxRss": 2142366597120,
+    "pageFaults": {
+      "IORequired": 1061,
+      "IONotRequired": 36578633
+    },
+    "fsActivity": {
+      "reads": 0,
+      "writes": 0
+    }
+  },
+  "libuv": [
+  ],
+  "environmentVariables": {
+    "npm_config_save_dev": "",
+    "npm_config_legacy_bundling": "",
+    "npm_config_dry_run": "",
+    "npm_package_dependencies_request": "^2.88.2",
+    "npm_package_dependencies_express_flash": "0.0.2",
+    "npm_package_dependencies__fortawesome_fontawesome_svg_core": "^6.3.0",
+    "NVM_INC": "/Users/smallwhale/.nvm/versions/node/v12.16.0/include/node",
+    "npm_config_viewer": "man",
+    "npm_config_only": "",
+    "npm_config_commit_hooks": "true",
+    "npm_config_browser": "",
+    "npm_package_gitHead": "8a0ae4e42d217a222d6a127ec640d5f1113832a0",
+    "npm_package_dependencies_webpack_dev_middleware": "^5.3.1",
+    "npm_package_dependencies_webpack_cli": "^4.10.0",
+    "npm_package_devDependencies_prettier": "^2.7.1",
+    "npm_package_devDependencies_awesome_typescript_loader": "^5.2.1",
+    "npm_package_devDependencies__types_archiver": "^3.1.1",
+    "MANPATH": "/Users/smallwhale/.nvm/versions/node/v12.16.0/share/man:/Users/smallwhale/.nvm/versions/node/v18.16.0/share/man:/opt/homebrew/share/man:/usr/share/man:/usr/local/share/man:/Users/smallwhale/.nvm/versions/node/v18.16.0/share/man:/opt/homebrew/share/man::",
+    "npm_config_also": "",
+    "npm_package_dependencies_react_jsx_parser": "^1.29.0",
+    "npm_package_dependencies_mongoose": "^5.13.14",
+    "npm_package_dependencies_connect_flash": "^0.1.1",
+    "npm_package_browser_child_process": "false",
+    "npm_config_sign_git_commit": "",
+    "npm_config_rollback": "true",
+    "npm_package_dependencies_material_ui": "^0.20.2",
+    "npm_package_devDependencies__types_sharp": "^0.23.1",
+    "npm_package_devDependencies__types_passport_local": "^1.0.34",
+    "npm_package_devDependencies__types_dotenv": "^6.1.1",
+    "npm_package_devDependencies__types_cookie_parser": "^1.4.2",
+    "TERM_PROGRAM": "vscode",
+    "NODE": "/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node",
+    "npm_config_usage": "",
+    "npm_config_audit": "true",
+    "npm_package_dependencies_reveal_js": "^4.3.0",
+    "npm_package_dependencies_process": "^0.11.10",
+    "npm_package_dependencies_pdfjs": "^2.4.7",
+    "npm_package_dependencies_html_to_image": "^0.1.3",
+    "npm_package_devDependencies_file_loader": "^3.0.1",
+    "npm_package_devDependencies__types_express_flash": "0.0.0",
+    "npm_package_scripts_monitor": "cross-env MONITORED=true NODE_OPTIONS=--max_old_space_size=4096 ts-node src/server/index.ts",
+    "INIT_CWD": "/Users/smallwhale/Desktop/Projects/Dash-Web",
+    "npm_package_dependencies_rehype_raw": "^6.1.1",
+    "npm_package_dependencies_react_audio_waveform": "0.0.5",
+    "npm_package_dependencies_path_browserify": "^1.0.1",
+    "npm_package_dependencies_nodemailer": "^5.1.1",
+    "npm_package_dependencies_axios": "^0.19.2",
+    "npm_package_devDependencies_typescript": "^4.7.4",
+    "NVM_CD_FLAGS": "-q",
+    "npm_config_globalignorefile": "/Users/smallwhale/.nvm/versions/node/v12.16.0/etc/npmignore",
+    "npm_package_dependencies_react_grid_layout": "^1.3.4",
+    "npm_package_dependencies_prosemirror_find_replace": "^0.9.0",
+    "npm_package_dependencies_normalize_css": "^8.0.1",
+    "npm_package_devDependencies_mocha": "^5.2.0",
+    "npm_package_devDependencies__types_express_session": "^1.17.5",
+    "TERM": "xterm-256color",
+    "SHELL": "/bin/zsh",
+    "npm_config_shell": "/bin/zsh",
+    "npm_config_maxsockets": "50",
+    "npm_config_init_author_url": "",
+    "npm_package_dependencies_prosemirror_dev_tools": "^3.1.0",
+    "npm_package_dependencies_p_limit": "^2.2.0",
+    "npm_package_dependencies_bson": "^4.6.1",
+    "npm_package_dependencies__types_dom_speech_recognition": "0.0.1",
+    "npm_package_dependencies__emotion_styled": "^11.11.0",
+    "npm_package_devDependencies_style_loader": "^0.23.1",
+    "npm_package_devDependencies__types_react_datepicker": "^3.1.8",
+    "npm_config_shrinkwrap": "true",
+    "npm_config_parseable": "",
+    "npm_config_metrics_registry": "https://registry.npmjs.org/",
+    "npm_package_dependencies_xregexp": "^4.4.1",
+    "npm_package_dependencies_shelljs": "^0.8.5",
+    "npm_package_dependencies_bezier_curve": "^1.0.0",
+    "npm_package_dependencies__mui_icons_material": "^5.11.16",
+    "npm_package_devDependencies_tslint": "^5.20.1",
+    "npm_package_devDependencies__types_react_transition_group": "^4.4.5",
+    "npm_package_scripts_tsc": "tsc",
+    "HOMEBREW_REPOSITORY": "/opt/homebrew",
+    "TMPDIR": "/var/folders/bv/1xck6d7j7bz14bvhgdsllbj40000gn/T/",
+    "npm_config_timing": "",
+    "npm_config_init_license": "ISC",
+    "npm_package_dependencies_socket_io": "^2.5.0",
+    "npm_package_dependencies_probe_image_size": "^4.0.0",
+    "npm_package_dependencies_canvas": "^2.9.3",
+    "npm_package_dependencies__hig_theme_data": "^2.23.1",
+    "npm_package_devDependencies__types_react_select": "^3.1.2",
+    "npm_package_devDependencies__types_prosemirror_model": "^1.16.1",
+    "npm_config_if_present": "",
+    "npm_package_dependencies_typescript_collections": "^1.3.3",
+    "npm_package_dependencies_rimraf": "^3.0.0",
+    "npm_package_dependencies_react_autosuggest": "^9.4.3",
+    "npm_package_dependencies_flexlayout_react": "^0.3.11",
+    "npm_package_dependencies_find_in_files": "^0.5.0",
+    "npm_package_devDependencies__types_chai": "^4.3.0",
+    "TERM_PROGRAM_VERSION": "1.83.0",
+    "npm_package_dependencies_prosemirror_inputrules": "^1.1.3",
+    "npm_package_dependencies_bcrypt_nodejs": "0.0.3",
+    "npm_package_dependencies_async": "^2.6.2",
+    "npm_config_sign_git_tag": "",
+    "npm_config_init_author_email": "",
+    "npm_config_cache_max": "Infinity",
+    "npm_package_dependencies_uuid": "^3.4.0",
+    "npm_package_dependencies_supercluster": "^7.1.4",
+    "npm_package_dependencies_remark_gfm": "^3.0.1",
+    "npm_package_dependencies_connect_mongo": "^2.0.3",
+    "npm_package_dependencies_browser_assert": "^1.2.1",
+    "npm_package_devDependencies_sass_loader": "^7.3.1",
+    "npm_package_scripts_start_release_debug": "cross-env RELEASE=true USE_AZURE=true NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev --inspect -- src/server/index.ts",
+    "ZDOTDIR": "/Users/smallwhale",
+    "ORIGINAL_XDG_CURRENT_DESKTOP": "undefined",
+    "MallocNanoZone": "0",
+    "npm_config_preid": "",
+    "npm_config_long": "",
+    "npm_config_local_address": "",
+    "npm_config_git_tag_version": "true",
+    "npm_config_cert": "",
+    "npm_package_dependencies_js_datepicker": "^4.6.6",
+    "npm_package_devDependencies__types_webpack_hot_middleware": "^2.25.6",
+    "npm_package_devDependencies__types_mongodb": "^3.6.20",
+    "npm_package_devDependencies__types_mocha": "^5.2.6",
+    "npm_config_registry": "https://registry.npmjs.org/",
+    "npm_config_noproxy": "",
+    "npm_config_fetch_retries": "2",
+    "npm_package_dependencies_react_compound_slider": "^2.5.0",
+    "npm_package_dependencies_prosemirror_history": "^1.2.0",
+    "npm_package_devDependencies__types_react_color": "^2.17.6",
+    "npm_package_devDependencies__types_google_maps_react": "^2.0.5",
+    "npm_package_devDependencies__types_color": "^3.0.3",
+    "npm_package_dependencies_react_dom": "^18.2.0",
+    "npm_package_dependencies_passport_local": "^1.0.0",
+    "npm_package_dependencies__octokit_core": "^4.0.4",
+    "npm_package_devDependencies__types_async": "^2.4.1",
+    "npm_package_scripts_debug": "cross-env  NODE_OPTIONS=--max_old_space_size=8192 ts-node-dev --transpile-only --inspect -- src/server/index.ts",
+    "npm_package_scripts_oldstart": "cross-env NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev --debug -- src/server/index.ts",
+    "npm_config_versions": "",
+    "npm_config_message": "%s",
+    "npm_config_key": "",
+    "npm_package_readmeFilename": "README.md",
+    "npm_package_dependencies_react_refresh_typescript": "^2.0.7",
+    "npm_package_dependencies_react_dropzone": "^14.2.3",
+    "npm_package_dependencies_image_size": "^0.7.5",
+    "npm_package_dependencies_html_to_text": "^5.1.1",
+    "npm_package_dependencies_express_validator": "^5.3.1",
+    "npm_package_devDependencies_eslint_plugin_jsx_a11y": "^6.6.0",
+    "npm_package_node_child_process": "empty",
+    "npm_package_dependencies_react_resizable_rotatable_draggable": "^0.2.0",
+    "npm_package_dependencies_got": "^12.0.1",
+    "npm_package_dependencies__types_d3_color": "^2.0.3",
+    "npm_package_devDependencies_webpack": "^5.69.1",
+    "npm_package_devDependencies__types_nodemailer": "^4.6.6",
+    "npm_package_description": "Install Node.js, then, from the project directory, run",
+    "NVM_DIR": "/Users/smallwhale/.nvm",
+    "USER": "smallwhale",
+    "npm_package_dependencies__types_d3_scale": "^3.3.2",
+    "npm_package_devDependencies_dotenv": "^8.6.0",
+    "npm_package_devDependencies__types_react": "^18.0.15",
+    "npm_package_devDependencies__types_prosemirror_transform": "^1.1.5",
+    "npm_package_devDependencies__types_prosemirror_history": "^1.0.3",
+    "npm_package_dependencies_readline": "^1.3.0",
+    "npm_package_dependencies__types_supercluster": "^7.1.0",
+    "npm_package_dependencies__azure_storage_blob": "^12.14.0",
+    "COMMAND_MODE": "unix2003",
+    "npm_config_globalconfig": "/Users/smallwhale/.nvm/versions/node/v12.16.0/etc/npmrc",
+    "npm_package_dependencies_depcheck": "^0.9.2",
+    "npm_package_dependencies__types_web": "0.0.53",
+    "npm_config_prefer_online": "",
+    "npm_config_logs_max": "10",
+    "npm_config_always_auth": "",
+    "npm_package_dependencies_react_icons": "^4.3.1",
+    "npm_package_dependencies_passport_google_oauth20": "^2.0.0",
+    "npm_package_devDependencies_webpack_dev_server": "^3.11.3",
+    "npm_package_devDependencies__types_brotli": "^1.3.1",
+    "npm_package_dependencies_url_loader": "^1.1.2",
+    "npm_package_dependencies_stream_browserify": "^3.0.0",
+    "npm_package_dependencies_prosemirror_transform": "^1.3.4",
+    "npm_package_dependencies_lodash": "^4.17.21",
+    "npm_package_dependencies_i": "^0.3.7",
+    "npm_package_devDependencies_tslint_loader": "^3.6.0",
+    "SSH_AUTH_SOCK": "/private/tmp/com.apple.launchd.Rzc48vZMGY/Listeners",
+    "npm_package_dependencies_words_to_numbers": "^1.5.1",
+    "npm_package_dependencies_valid_url": "^1.0.9",
+    "npm_package_dependencies_styled_components": "^4.4.1",
+    "npm_package_dependencies_csv_parser": "^3.0.0",
+    "npm_package_dependencies_class_transformer": "^0.2.0",
+    "npm_package_devDependencies_eslint": "^8.36.0",
+    "npm_package_devDependencies__types_prosemirror_inputrules": "^1.0.4",
+    "npm_package_devDependencies__types_express": "^4.17.13",
+    "__CF_USER_TEXT_ENCODING": "0x1F5:0x0:0x0",
+    "npm_execpath": "/Users/smallwhale/.nvm/versions/node/v12.16.0/lib/node_modules/npm/bin/npm-cli.js",
+    "npm_config_global_style": "",
+    "npm_config_cache_lock_retries": "10",
+    "npm_package_dependencies_wikijs": "^6.3.3",
+    "npm_package_dependencies_bluebird": "^3.7.2",
+    "npm_package_devDependencies__types_react_typist": "^2.0.3",
+    "npm_config_update_notifier": "true",
+    "npm_config_cafile": "",
+    "npm_package_dependencies_util": "^0.12.4",
+    "npm_package_dependencies_raw_loader": "^1.0.0",
+    "npm_package_dependencies_https_browserify": "^1.0.0",
+    "npm_package_dependencies_brotli": "^1.3.3",
+    "npm_package_dependencies__mui_material": "^5.13.1",
+    "npm_package_dependencies__fortawesome_react_fontawesome": "^0.2.0",
+    "npm_package_devDependencies__types_passport_google_oauth20": "^2.0.11",
+    "npm_package_dependencies_cors": "^2.8.5",
+    "npm_package_dependencies_bezier_js": "^4.1.1",
+    "npm_package_dependencies__fortawesome_free_brands_svg_icons": "^6.3.0",
+    "npm_config_heading": "npm",
+    "npm_config_audit_level": "low",
+    "npm_package_dependencies_chrome": "^0.1.0",
+    "npm_package_dependencies__react_three_fiber": "^6.2.3",
+    "npm_package_devDependencies_eslint_plugin_prettier": "^4.2.1",
+    "npm_package_devDependencies_copy_webpack_plugin": "^4.6.0",
+    "npm_package_devDependencies__types_react_measure": "^2.0.8",
+    "npm_package_devDependencies__types_react_dom": "^18.0.6",
+    "npm_package_devDependencies__types_mobile_detect": "^1.3.4",
+    "npm_config_searchlimit": "20",
+    "npm_config_read_only": "",
+    "npm_config_offline": "",
+    "npm_config_fetch_retry_mintimeout": "10000",
+    "npm_package_dependencies_react_typist": "^2.0.5",
+    "npm_package_dependencies_mobx_react_devtools": "^6.1.1",
+    "npm_package_dependencies_md5_file": "^5.0.0",
+    "npm_package_dependencies_forever_agent": "^0.6.1",
+    "npm_package_devDependencies__types_xregexp": "^4.4.0",
+    "npm_package_devDependencies__types_typescript": "^2.0.0",
+    "npm_package_devDependencies__types_request": "^2.48.8",
+    "npm_package_devDependencies__types_prosemirror_commands": "^1.0.4",
+    "npm_config_json": "",
+    "npm_config_access": "",
+    "npm_config_argv": "{\"remain\":[],\"cooked\":[\"start\"],\"original\":[\"start\"]}",
+    "npm_package_dependencies__fortawesome_free_solid_svg_icons": "^6.3.0",
+    "npm_package_devDependencies__types_socket_io": "^2.1.13",
+    "PATH": "/Users/smallwhale/.nvm/versions/node/v12.16.0/lib/node_modules/npm/node_modules/npm-lifecycle/node-gyp-bin:/Users/smallwhale/Desktop/Projects/Dash-Web/node_modules/.bin:/Users/smallwhale/.nvm/versions/node/v12.16.0/bin:/Users/smallwhale/.nvm/versions/node/v18.16.0/bin:/Library/Frameworks/Python.framework/Versions/3.11/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Frameworks/Mono.framework/Versions/Current/Commands:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/Users/smallwhale/.nvm/versions/node/v18.16.0/bin:/Library/Frameworks/Python.framework/Versions/3.11/bin:/opt/homebrew/bin:/opt/homebrew/sbin",
+    "npm_config_allow_same_version": "",
+    "npm_package_dependencies_webrtc_adapter": "^7.7.1",
+    "npm_package_dependencies_react_reveal": "^1.2.2",
+    "npm_package_dependencies_prosemirror_schema_list": "^1.1.6",
+    "npm_package_dependencies__material_ui_core": "^4.12.3",
+    "npm_package_devDependencies__types_rimraf": "^2.0.5",
+    "npm_package_devDependencies__types_connect_flash": "0.0.34",
+    "npm_config_https_proxy": "",
+    "npm_config_engine_strict": "",
+    "npm_config_description": "true",
+    "npm_package_dependencies_pug": "^2.0.4",
+    "npm_package_dependencies_prosemirror_keymap": "^1.1.5",
+    "npm_package_dependencies_pdfjs_dist": "^2.14.305",
+    "npm_package_dependencies_mobile_detect": "^1.4.5",
+    "npm_package_dependencies_image_size_stream": "^1.1.0",
+    "npm_package_dependencies_golden_layout": "^1.5.9",
+    "npm_package_dependencies_child_process": "^1.0.2",
+    "npm_package_dependencies__types_d3_axis": "^2.1.3",
+    "_": "/Users/smallwhale/Desktop/Projects/Dash-Web/node_modules/.bin/cross-env",
+    "LaunchInstanceID": "BA61DDCB-E273-45DE-B08F-DCC79F8578DB",
+    "npm_config_userconfig": "/Users/smallwhale/.npmrc",
+    "npm_config_init_module": "/Users/smallwhale/.npm-init.js",
+    "npm_package_dependencies__react_google_maps_api": "^2.7.0",
+    "USER_ZDOTDIR": "/Users/smallwhale",
+    "__CFBundleIdentifier": "com.microsoft.VSCode",
+    "npm_config_cidr": "",
+    "npm_package_dependencies_puppeteer": "^3.3.0",
+    "npm_package_dependencies_prosemirror_view": "^1.26.5",
+    "npm_package_dependencies_mongodb": "^3.7.3",
+    "npm_package_dependencies_google_auth_library": "^4.2.4",
+    "npm_package_dependencies_bootstrap": "^4.6.1",
+    "npm_package_devDependencies_eslint_config_airbnb": "^19.0.4",
+    "PWD": "/Users/smallwhale/Desktop/Projects/Dash-Web",
+    "npm_config_user": "501",
+    "npm_config_node_version": "12.16.0",
+    "npm_package_dependencies_node_sass": "^4.14.1",
+    "npm_package_dependencies_howler": "^2.2.3",
+    "npm_package_dependencies_expressjs": "^1.0.1",
+    "npm_package_dependencies_core_js": "^3.28.0",
+    "npm_package_dependencies_browndash_components": "^0.1.36",
+    "npm_package_devDependencies_eslint_plugin_react_hooks": "^4.6.0",
+    "npm_package_devDependencies__types_lodash": "^4.14.179",
+    "JAVA_HOME": "/opt/homebrew/Cellar/openjdk@17/17.0.8.1/libexec/openjdk.jdk/Contents/Home",
+    "npm_lifecycle_event": "start",
+    "npm_package_dependencies_react_table": "^6.11.5",
+    "npm_package_dependencies_react_loading": "^2.0.3",
+    "npm_package_dependencies_mobx": "^5.15.7",
+    "npm_package_dependencies_babel": "^6.23.0",
+    "npm_package_devDependencies_jsdom": "^15.2.1",
+    "npm_package_devDependencies_chai": "^4.3.6",
+    "npm_config_save": "true",
+    "npm_config_ignore_prepublish": "",
+    "npm_config_editor": "vi",
+    "npm_config_auth_type": "legacy",
+    "npm_package_dependencies_npm": "^6.14.18",
+    "npm_package_dependencies_node_stream_zip": "^1.15.0",
+    "npm_package_dependencies_image_data_uri": "^2.0.1",
+    "npm_package_scripts_start_release": "cross-env RELEASE=true USE_AZURE=false NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev -- src/server/index.ts",
+    "npm_package_name": "dash",
+    "LANG": "en_US.UTF-8",
+    "npm_config_tag": "latest",
+    "npm_config_script_shell": "",
+    "npm_package_dependencies_query_string": "^6.14.1",
+    "npm_package_dependencies_mobx_utils": "^5.6.2",
+    "npm_package_dependencies_file_saver": "^2.0.5",
+    "npm_package_dependencies_body_parser": "^1.19.2",
+    "npm_package_dependencies__types_reveal": "^3.3.33",
+    "npm_package_devDependencies_eslint_plugin_import": "^2.26.0",
+    "npm_package_devDependencies__types_prosemirror_view": "^1.23.1",
+    "npm_config_progress": "true",
+    "npm_config_global": "",
+    "npm_config_before": "",
+    "npm_package_dependencies_xoauth2": "^1.2.0",
+    "npm_package_dependencies_standard_http_error": "^2.0.1",
+    "npm_package_dependencies_react_loader_spinner": "^5.3.4",
+    "npm_package_dependencies_http_browserify": "^1.7.0",
+    "npm_package_dependencies__types_d3_selection": "^2.0.1",
+    "npm_package_dependencies__hig_flyout": "^1.3.1",
+    "npm_package_devDependencies_fork_ts_checker_webpack_plugin": "^1.6.0",
+    "npm_package_scripts_build": "cross-env NODE_OPTIONS=--max_old_space_size=8192 webpack --env production",
+    "npm_package_scripts_start": "cross-env NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev --debug --transpile-only -- src/server/index.ts",
+    "npm_config_searchstaleness": "900",
+    "npm_config_optional": "true",
+    "npm_config_ham_it_up": "",
+    "npm_package_dependencies_sharp": "^0.23.4",
+    "npm_package_dependencies_rc_switch": "^1.9.2",
+    "npm_package_dependencies_googlephotos": "^0.2.5",
+    "npm_package_dependencies_exifr": "^7.1.3",
+    "npm_package_dependencies__types_google_maps": "^3.2.3",
+    "npm_package_dependencies__types_bezier_js": "^4.1.0",
+    "npm_package_dependencies__ffmpeg_core": "0.10.0",
+    "npm_package_devDependencies_ts_loader": "^5.3.3",
+    "npm_package_devDependencies__types_bcrypt_nodejs": "0.0.30",
+    "VSCODE_GIT_ASKPASS_EXTRA_ARGS": "--ms-enable-electron-run-as-node",
+    "XPC_FLAGS": "0x0",
+    "npm_config_save_prod": "",
+    "npm_config_force": "",
+    "npm_config_bin_links": "true",
+    "npm_package_devDependencies__types_youtube": "0.0.39",
+    "npm_config_searchopts": "",
+    "npm_package_dependencies_react_beautiful_dnd": "^13.1.0",
+    "npm_package_dependencies_jszip": "^3.7.1",
+    "npm_package_dependencies_csv_stringify": "^6.3.0",
+    "npm_package_devDependencies__types_react_icons": "^3.0.0",
+    "npm_config_node_gyp": "/Users/smallwhale/.nvm/versions/node/v12.16.0/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js",
+    "npm_config_depth": "Infinity",
+    "npm_package_dependencies_google_maps_react": "^2.0.6",
+    "npm_package_dependencies_express_session": "^1.17.2",
+    "npm_package_devDependencies_eslint_plugin_node": "^11.1.0",
+    "npm_package_devDependencies_eslint_config_prettier": "^8.5.0",
+    "npm_package_main": "index.js",
+    "npm_config_sso_poll_frequency": "500",
+    "npm_config_rebuild_bundle": "true",
+    "npm_package_dependencies_chart_js": "^3.8.0",
+    "npm_package_dependencies__emotion_react": "^11.11.0",
+    "npm_package_devDependencies__types_prosemirror_menu": "^1.0.6",
+    "npm_package_devDependencies__types_prosemirror_keymap": "^1.0.4",
+    "npm_package_devDependencies__types_pdfjs_dist": "^2.10.378",
+    "npm_package_devDependencies__types_exif": "^0.6.3",
+    "npm_package_version": "1.0.0",
+    "XPC_SERVICE_NAME": "0",
+    "npm_config_unicode": "true",
+    "VSCODE_INJECTION": "1",
+    "npm_package_dependencies_typescript_language_server": "^0.4.0",
+    "npm_package_dependencies_prosemirror_model": "^1.18.1",
+    "npm_package_dependencies__ffmpeg_ffmpeg": "0.10.0",
+    "SHLVL": "2",
+    "HOME": "/Users/smallwhale",
+    "npm_config_fetch_retry_maxtimeout": "60000",
+    "npm_package_dependencies_request_promise": "^4.2.6",
+    "npm_package_dependencies_react_markdown": "^8.0.3",
+    "npm_package_dependencies__hig_theme_context": "^2.1.3",
+    "npm_package_devDependencies__types_react_autosuggest": "^9.3.14",
+    "npm_package_devDependencies__types_mongoose": "^5.11.97",
+    "npm_package_devDependencies__types_d3": "^7.4.0",
+    "npm_package_devDependencies__types_animejs": "^2.0.2",
+    "npm_package_scripts_test": "mocha -r ts-node/register test/**/*.ts",
+    "VSCODE_GIT_ASKPASS_MAIN": "/Applications/Visual Studio Code.app/Contents/Resources/app/extensions/git/dist/askpass-main.js",
+    "npm_config_tag_version_prefix": "v",
+    "npm_config_strict_ssl": "true",
+    "npm_config_sso_type": "oauth",
+    "npm_config_scripts_prepend_node_path": "warn-only",
+    "npm_config_save_prefix": "^",
+    "npm_config_loglevel": "notice",
+    "npm_config_ca": "",
+    "npm_package_dependencies_three": "^0.127.0",
+    "npm_package_dependencies_openai": "^3.2.1",
+    "npm_package_dependencies_mobx_react": "^5.4.4",
+    "npm_package_dependencies_google_translate_api_browser": "^3.0.1",
+    "npm_package_dependencies_cookie_parser": "^1.4.6",
+    "npm_package_dependencies_adm_zip": "^0.4.16",
+    "npm_package_devDependencies_eslint_config_node": "^4.1.0",
+    "npm_config_save_exact": "",
+    "npm_config_group": "20",
+    "npm_config_fetch_retry_factor": "10",
+    "npm_config_dev": "",
+    "npm_package_devDependencies_webpack_hot_middleware": "^2.25.1",
+    "npm_package_devDependencies_cross_env": "^5.2.1",
+    "HOMEBREW_PREFIX": "/opt/homebrew",
+    "npm_config_version": "",
+    "npm_config_prefer_offline": "",
+    "npm_config_cache_lock_stale": "60000",
+    "npm_package_devDependencies__types_prosemirror_state": "^1.2.8",
+    "npm_package_devDependencies__types_body_parser": "^1.19.2",
+    "npm_config_otp": "",
+    "npm_config_cache_min": "10",
+    "npm_package_dependencies_react_color": "^2.19.3",
+    "npm_package_dependencies_d3": "^7.6.1",
+    "npm_package_devDependencies_ts_node": "^10.9.1",
+    "npm_package_devDependencies__types_react_grid_layout": "^1.3.2",
+    "npm_config_searchexclude": "",
+    "npm_config_cache": "/Users/smallwhale/.npm",
+    "npm_package_dependencies_tough_cookie": "^4.0.0",
+    "npm_package_dependencies_googleapis": "^40.0.0",
+    "npm_package_devDependencies__types_valid_url": "^1.0.3",
+    "npm_package_devDependencies__types_passport": "^1.0.9",
+    "npm_package_devDependencies__types_adm_zip": "^0.4.34",
+    "LOGNAME": "smallwhale",
+    "npm_lifecycle_script": "cross-env NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev --debug --transpile-only -- src/server/index.ts",
+    "npm_config_color": "true",
+    "npm_package_dependencies_solr_node": "^1.2.1",
+    "npm_package_dependencies_react_transition_group": "^4.4.2",
+    "npm_package_dependencies_iink_js": "^1.5.4",
+    "npm_package_dependencies_html_webpack_plugin": "^5.5.0",
+    "npm_config_proxy": "",
+    "npm_config_package_lock": "true",
+    "npm_package_dependencies_prosemirror_state": "^1.4.1",
+    "npm_package_dependencies_nodemon": "^1.19.4",
+    "npm_package_dependencies_function_plot": "^1.22.8",
+    "npm_package_dependencies_equation_editor_react": "github:bobzel/equation-editor-react#useLocally",
+    "npm_package_devDependencies__types_socket_io_parser": "^3.0.0",
+    "VSCODE_GIT_IPC_HANDLE": "/var/folders/bv/1xck6d7j7bz14bvhgdsllbj40000gn/T/vscode-git-b8c8318074.sock",
+    "npm_config_package_lock_only": "",
+    "npm_config_fund": "true",
+    "npm_package_dependencies_react": "^18.2.0",
+    "npm_package_dependencies_bingmaps_react": "^1.2.10",
+    "npm_package_devDependencies_scss_loader": "0.0.1",
+    "npm_package_devDependencies__types_cookie_session": "^2.0.44",
+    "npm_config_save_optional": "",
+    "npm_package_dependencies_textarea_caret": "^3.1.0",
+    "npm_package_dependencies_react_measure": "^2.5.2",
+    "npm_package_dependencies_exif": "^0.6.0",
+    "NVM_BIN": "/Users/smallwhale/.nvm/versions/node/v12.16.0/bin",
+    "npm_config_ignore_scripts": "",
+    "npm_config_user_agent": "npm/6.13.4 node/v12.16.0 darwin x64",
+    "npm_package_dependencies_react_resizable": "^1.11.1",
+    "npm_package_dependencies_prosemirror_commands": "^1.2.1",
+    "npm_package_dependencies_memorystream": "^0.3.1",
+    "npm_package_dependencies_formidable": "1.2.1",
+    "npm_package_devDependencies__types_uuid": "^3.4.10",
+    "npm_config_cache_lock_wait": "10000",
+    "npm_package_dependencies_socket_io_client": "^2.5.0",
+    "npm_package_dependencies_recharts": "^2.1.12",
+    "npm_package_dependencies_react_chartjs_2": "^4.3.0",
+    "npm_package_dependencies_fluent_ffmpeg": "^2.1.2",
+    "npm_package_dependencies__types_cors": "^2.8.12",
+    "npm_package_devDependencies__types_node": "^10.17.60",
+    "npm_package_devDependencies__types_file_saver": "^2.0.5",
+    "VSCODE_GIT_ASKPASS_NODE": "/Applications/Visual Studio Code.app/Contents/Frameworks/Code Helper (Plugin).app/Contents/MacOS/Code Helper (Plugin)",
+    "GIT_ASKPASS": "/Applications/Visual Studio Code.app/Contents/Resources/app/extensions/git/dist/askpass.sh",
+    "INFOPATH": "/opt/homebrew/share/info:/opt/homebrew/share/info:",
+    "HOMEBREW_CELLAR": "/opt/homebrew/Cellar",
+    "npm_config_production": "",
+    "npm_package_dependencies_jsonschema": "^1.4.0",
+    "npm_package_dependencies_ffmpeg": "0.0.4",
+    "npm_package_dependencies_cookie_session": "^2.0.0",
+    "npm_package_dependencies_color": "^3.2.1",
+    "npm_package_devDependencies__types_webpack": "^4.41.32",
+    "npm_package_devDependencies__types_request_promise": "^4.1.48",
+    "npm_package_devDependencies__types_prosemirror_schema_list": "^1.0.3",
+    "npm_config_send_metrics": "",
+    "npm_config_save_bundle": "",
+    "npm_package_dependencies_web_request": "^1.0.7",
+    "npm_package_dependencies_react_datepicker": "^3.8.0",
+    "npm_package_dependencies_express": "^4.17.3",
+    "npm_package_dependencies_D": "^1.0.0",
+    "npm_package_dependencies__types_formidable": "1.0.31",
+    "npm_package_devDependencies__types_rc_switch": "^1.9.2",
+    "npm_package_devDependencies__types_prosemirror_dev_tools": "^2.1.0",
+    "npm_package_devDependencies__types_jquery": "^3.5.14",
+    "npm_config_umask": "0022",
+    "npm_config_node_options": "",
+    "npm_config_init_version": "1.0.0",
+    "npm_package_dependencies_react_textarea_autosize": "^8.5.3",
+    "npm_package_dependencies_https": "^1.0.0",
+    "npm_package_dependencies_array_batcher": "^1.2.3",
+    "npm_package_dependencies__fortawesome_free_regular_svg_icons": "^6.3.0",
+    "npm_package_devDependencies__types_shelljs": "^0.8.11",
+    "npm_package_devDependencies__types_libxmljs": "^0.18.7",
+    "npm_package_devDependencies__types_express_validator": "^3.0.0",
+    "npm_package_devDependencies__types_bluebird": "^3.5.36",
+    "npm_config_init_author_name": "",
+    "npm_config_git": "git",
+    "npm_config_scope": "",
+    "npm_package_dependencies_react_select": "^3.2.0",
+    "npm_package_dependencies_pdf_parse": "^1.1.1",
+    "npm_package_dependencies_colors": "^1.4.0",
+    "npm_package_dependencies_archiver": "^3.1.1",
+    "npm_package_devDependencies_css_loader": "^2.1.1",
+    "npm_package_devDependencies__types_socket_io_client": "^1.4.36",
+    "SECURITYSESSIONID": "186a4",
+    "npm_config_unsafe_perm": "true",
+    "npm_config_tmp": "/var/folders/bv/1xck6d7j7bz14bvhgdsllbj40000gn/T",
+    "npm_config_onload_script": "",
+    "npm_package_dependencies_serializr": "^1.5.4",
+    "npm_package_dependencies_fit_curve": "^0.1.7",
+    "npm_package_dependencies__webscopeio_react_textarea_autocomplete": "^4.9.1",
+    "npm_package_dependencies__types_three": "^0.126.2",
+    "npm_package_devDependencies_ts_node_dev": "^2.0.0",
+    "npm_node_execpath": "/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node",
+    "npm_config_prefix": "/Users/smallwhale/.nvm/versions/node/v12.16.0",
+    "npm_config_link": "",
+    "npm_config_format_package_lock": "true",
+    "npm_package_dependencies_passport": "^0.4.0",
+    "npm_package_devDependencies_eslint_plugin_react": "^7.30.1",
+    "npm_package_devDependencies__types_react_table": "^6.8.9",
+    "npm_package_devDependencies__types_react_reconciler": "^0.26.4",
+    "COLORTERM": "truecolor",
+    "NODE_OPTIONS": "--max_old_space_size=4096",
+    "TS_NODE_DEV": "true",
+    "_CLIENT_OPENAI_KEY": "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",
+    "_CLIENT_GITHUB_ACCESS_TOKEN": "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",
+    "VIPSHOME": "/usr/local/Cellar/vips/8.8.1",
+    "TYPESCRIPT_PATH": "/Users/smallwhale/Desktop/Projects/Dash-Web/node_modules/typescript/lib/typescript.js",
+    "TSCONFIG": "/Users/smallwhale/Desktop/Projects/Dash-Web/tsconfig.json",
+    "COMPILER_OPTIONS": "{}",
+    "TSLINT": "true",
+    "CONTEXT": "/Users/smallwhale/Desktop/Projects/Dash-Web",
+    "TSLINTAUTOFIX": "false",
+    "ESLINT": "false",
+    "ESLINT_OPTIONS": "{}",
+    "WATCH": "",
+    "WORK_DIVISION": "1",
+    "MEMORY_LIMIT": "2048",
+    "CHECK_SYNTACTIC_ERRORS": "false",
+    "USE_INCREMENTAL_API": "true",
+    "VUE": "false"
+  },
+  "userLimits": {
+    "core_file_size_blocks": {
+      "soft": 0,
+      "hard": "unlimited"
+    },
+    "data_seg_size_kbytes": {
+      "soft": "unlimited",
+      "hard": "unlimited"
+    },
+    "file_size_blocks": {
+      "soft": "unlimited",
+      "hard": "unlimited"
+    },
+    "max_locked_memory_bytes": {
+      "soft": "unlimited",
+      "hard": "unlimited"
+    },
+    "max_memory_size_kbytes": {
+      "soft": "unlimited",
+      "hard": "unlimited"
+    },
+    "open_files": {
+      "soft": 1048575,
+      "hard": "unlimited"
+    },
+    "stack_size_bytes": {
+      "soft": 8372224,
+      "hard": 67092480
+    },
+    "cpu_time_seconds": {
+      "soft": "unlimited",
+      "hard": "unlimited"
+    },
+    "max_user_processes": {
+      "soft": 1333,
+      "hard": 2000
+    },
+    "virtual_memory_kbytes": {
+      "soft": "unlimited",
+      "hard": "unlimited"
+    }
+  },
+  "sharedObjects": [
+    "/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node",
+    "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation",
+    "/usr/lib/libobjc.A.dylib",
+    "/System/Library/PrivateFrameworks/CoreServicesInternal.framework/Versions/A/CoreServicesInternal",
+    "/usr/lib/liboah.dylib",
+    "/usr/lib/libfakelink.dylib",
+    "/usr/lib/libicucore.A.dylib",
+    "/usr/lib/libSystem.B.dylib",
+    "/System/Library/PrivateFrameworks/SoftLinking.framework/Versions/A/SoftLinking",
+    "/usr/lib/libc++abi.dylib",
+    "/usr/lib/libc++.1.dylib",
+    "/usr/lib/system/libcache.dylib",
+    "/usr/lib/system/libcommonCrypto.dylib",
+    "/usr/lib/system/libcompiler_rt.dylib",
+    "/usr/lib/system/libcopyfile.dylib",
+    "/usr/lib/system/libcorecrypto.dylib",
+    "/usr/lib/system/libdispatch.dylib",
+    "/usr/lib/system/libdyld.dylib",
+    "/usr/lib/system/libkeymgr.dylib",
+    "/usr/lib/system/libmacho.dylib",
+    "/usr/lib/system/libquarantine.dylib",
+    "/usr/lib/system/libremovefile.dylib",
+    "/usr/lib/system/libsystem_asl.dylib",
+    "/usr/lib/system/libsystem_blocks.dylib",
+    "/usr/lib/system/libsystem_c.dylib",
+    "/usr/lib/system/libsystem_collections.dylib",
+    "/usr/lib/system/libsystem_configuration.dylib",
+    "/usr/lib/system/libsystem_containermanager.dylib",
+    "/usr/lib/system/libsystem_coreservices.dylib",
+    "/usr/lib/system/libsystem_darwin.dylib",
+    "/usr/lib/system/libsystem_dnssd.dylib",
+    "/usr/lib/system/libsystem_featureflags.dylib",
+    "/usr/lib/system/libsystem_info.dylib",
+    "/usr/lib/system/libsystem_m.dylib",
+    "/usr/lib/system/libsystem_malloc.dylib",
+    "/usr/lib/system/libsystem_networkextension.dylib",
+    "/usr/lib/system/libsystem_notify.dylib",
+    "/usr/lib/system/libsystem_sandbox.dylib",
+    "/usr/lib/system/libsystem_secinit.dylib",
+    "/usr/lib/system/libsystem_kernel.dylib",
+    "/usr/lib/system/libsystem_platform.dylib",
+    "/usr/lib/system/libsystem_pthread.dylib",
+    "/usr/lib/system/libsystem_symptoms.dylib",
+    "/usr/lib/system/libsystem_trace.dylib",
+    "/usr/lib/system/libunwind.dylib",
+    "/usr/lib/system/libxpc.dylib",
+    "/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit",
+    "/System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices",
+    "/usr/lib/libDiagnosticMessagesClient.dylib",
+    "/usr/lib/libenergytrace.dylib",
+    "/usr/lib/libbsm.0.dylib",
+    "/usr/lib/libz.1.dylib",
+    "/usr/lib/system/libkxld.dylib",
+    "/System/Library/Frameworks/CFNetwork.framework/Versions/A/CFNetwork",
+    "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/FSEvents.framework/Versions/A/FSEvents",
+    "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/CarbonCore",
+    "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/Metadata.framework/Versions/A/Metadata",
+    "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/OSServices.framework/Versions/A/OSServices",
+    "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SearchKit.framework/Versions/A/SearchKit",
+    "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/AE.framework/Versions/A/AE",
+    "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/LaunchServices",
+    "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/DictionaryServices.framework/Versions/A/DictionaryServices",
+    "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SharedFileList.framework/Versions/A/SharedFileList",
+    "/System/Library/Frameworks/Security.framework/Versions/A/Security",
+    "/System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration",
+    "/usr/lib/libapple_nghttp2.dylib",
+    "/usr/lib/libcompression.dylib",
+    "/usr/lib/libnetwork.dylib",
+    "/usr/lib/libsqlite3.dylib",
+    "/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation",
+    "/System/Library/Frameworks/Network.framework/Versions/A/Network",
+    "/usr/lib/libCoreEntitlements.dylib",
+    "/System/Library/PrivateFrameworks/MessageSecurity.framework/Versions/A/MessageSecurity",
+    "/System/Library/PrivateFrameworks/ProtocolBuffer.framework/Versions/A/ProtocolBuffer",
+    "/usr/lib/libMobileGestalt.dylib",
+    "/System/Library/PrivateFrameworks/AppleFSCompression.framework/Versions/A/AppleFSCompression",
+    "/usr/lib/libcoretls.dylib",
+    "/usr/lib/libcoretls_cfhelpers.dylib",
+    "/usr/lib/libpam.2.dylib",
+    "/usr/lib/libxar.1.dylib",
+    "/System/Library/PrivateFrameworks/CoreAutoLayout.framework/Versions/A/CoreAutoLayout",
+    "/System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration",
+    "/usr/lib/libarchive.2.dylib",
+    "/usr/lib/libxml2.2.dylib",
+    "/usr/lib/liblangid.dylib",
+    "/System/Library/Frameworks/Combine.framework/Versions/A/Combine",
+    "/usr/lib/swift/libswiftCore.dylib",
+    "/usr/lib/swift/libswiftCoreFoundation.dylib",
+    "/usr/lib/swift/libswiftDarwin.dylib",
+    "/usr/lib/swift/libswiftDispatch.dylib",
+    "/usr/lib/swift/libswiftIOKit.dylib",
+    "/usr/lib/swift/libswiftObjectiveC.dylib",
+    "/usr/lib/swift/libswiftXPC.dylib",
+    "/usr/lib/swift/libswift_Concurrency.dylib",
+    "/usr/lib/swift/libswift_StringProcessing.dylib",
+    "/usr/lib/swift/libswiftos.dylib",
+    "/System/Library/PrivateFrameworks/AppleSystemInfo.framework/Versions/A/AppleSystemInfo",
+    "/System/Library/PrivateFrameworks/IOMobileFramebuffer.framework/Versions/A/IOMobileFramebuffer",
+    "/System/Library/Frameworks/IOSurface.framework/Versions/A/IOSurface",
+    "/usr/lib/libpcap.A.dylib",
+    "/usr/lib/libdns_services.dylib",
+    "/usr/lib/liblzma.5.dylib",
+    "/usr/lib/libbz2.1.0.dylib",
+    "/usr/lib/libiconv.2.dylib",
+    "/usr/lib/libcharset.1.dylib",
+    "/usr/lib/swift/libswift_RegexParser.dylib",
+    "/usr/lib/libheimdal-asn1.dylib",
+    "/usr/lib/libCheckFix.dylib",
+    "/System/Library/PrivateFrameworks/TCC.framework/Versions/A/TCC",
+    "/System/Library/PrivateFrameworks/CoreNLP.framework/Versions/A/CoreNLP",
+    "/System/Library/PrivateFrameworks/MetadataUtilities.framework/Versions/A/MetadataUtilities",
+    "/System/Library/Frameworks/Accelerate.framework/Versions/A/Accelerate",
+    "/usr/lib/libmecab.dylib",
+    "/usr/lib/libCRFSuite.dylib",
+    "/usr/lib/libgermantok.dylib",
+    "/usr/lib/libThaiTokenizer.dylib",
+    "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vImage.framework/Versions/A/vImage",
+    "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/vecLib",
+    "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvMisc.dylib",
+    "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvDSP.dylib",
+    "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib",
+    "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLAPACK.dylib",
+    "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLinearAlgebra.dylib",
+    "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparseBLAS.dylib",
+    "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libQuadrature.dylib",
+    "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBNNS.dylib",
+    "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparse.dylib",
+    "/System/Library/PrivateFrameworks/MIL.framework/Versions/A/MIL",
+    "/System/Library/Frameworks/OpenDirectory.framework/Versions/A/Frameworks/CFOpenDirectory.framework/Versions/A/CFOpenDirectory",
+    "/System/Library/Frameworks/OpenDirectory.framework/Versions/A/OpenDirectory",
+    "/System/Library/PrivateFrameworks/APFS.framework/Versions/A/APFS",
+    "/System/Library/Frameworks/SecurityFoundation.framework/Versions/A/SecurityFoundation",
+    "/usr/lib/libutil.dylib",
+    "/System/Library/PrivateFrameworks/InstalledContentLibrary.framework/Versions/A/InstalledContentLibrary",
+    "/System/Library/PrivateFrameworks/CoreServicesStore.framework/Versions/A/CoreServicesStore",
+    "/usr/lib/libapp_launch_measurement.dylib",
+    "/System/Library/PrivateFrameworks/AppleMobileFileIntegrity.framework/Versions/A/AppleMobileFileIntegrity",
+    "/usr/lib/libmis.dylib",
+    "/System/Library/PrivateFrameworks/MobileSystemServices.framework/Versions/A/MobileSystemServices",
+    "/System/Library/PrivateFrameworks/ConfigProfileHelper.framework/Versions/A/ConfigProfileHelper",
+    "/System/Library/PrivateFrameworks/CoreAnalytics.framework/Versions/A/CoreAnalytics",
+    "/System/Library/PrivateFrameworks/AppleSauce.framework/Versions/A/AppleSauce",
+    "/System/Library/PrivateFrameworks/LanguageModeling.framework/Versions/A/LanguageModeling",
+    "/usr/lib/libxslt.1.dylib",
+    "/usr/lib/libcmph.dylib",
+    "/System/Library/PrivateFrameworks/CoreEmoji.framework/Versions/A/CoreEmoji",
+    "/System/Library/PrivateFrameworks/LinguisticData.framework/Versions/A/LinguisticData",
+    "/System/Library/PrivateFrameworks/Lexicon.framework/Versions/A/Lexicon",
+    "/System/Library/PrivateFrameworks/BackgroundTaskManagement.framework/Versions/A/BackgroundTaskManagement",
+    "/usr/lib/libTLE.dylib",
+    "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices",
+    "/System/Library/Frameworks/CoreGraphics.framework/Versions/A/CoreGraphics",
+    "/System/Library/Frameworks/CoreText.framework/Versions/A/CoreText",
+    "/System/Library/Frameworks/ImageIO.framework/Versions/A/ImageIO",
+    "/System/Library/Frameworks/ColorSync.framework/Versions/A/ColorSync",
+    "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/ATS",
+    "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/HIServices",
+    "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/PrintCore.framework/Versions/A/PrintCore",
+    "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/QD.framework/Versions/A/QD",
+    "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ColorSyncLegacy.framework/Versions/A/ColorSyncLegacy",
+    "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/SpeechSynthesis.framework/Versions/A/SpeechSynthesis",
+    "/System/Library/PrivateFrameworks/SkyLight.framework/Versions/A/SkyLight",
+    "/System/Library/PrivateFrameworks/FontServices.framework/libFontParser.dylib",
+    "/System/Library/PrivateFrameworks/RunningBoardServices.framework/Versions/A/RunningBoardServices",
+    "/System/Library/PrivateFrameworks/IOSurfaceAccelerator.framework/Versions/A/IOSurfaceAccelerator",
+    "/System/Library/PrivateFrameworks/WatchdogClient.framework/Versions/A/WatchdogClient",
+    "/System/Library/Frameworks/CoreDisplay.framework/Versions/A/CoreDisplay",
+    "/System/Library/Frameworks/CoreMedia.framework/Versions/A/CoreMedia",
+    "/System/Library/PrivateFrameworks/IOAccelerator.framework/Versions/A/IOAccelerator",
+    "/System/Library/Frameworks/Metal.framework/Versions/A/Metal",
+    "/System/Library/Frameworks/CoreVideo.framework/Versions/A/CoreVideo",
+    "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/MetalPerformanceShaders",
+    "/System/Library/PrivateFrameworks/MultitouchSupport.framework/Versions/A/MultitouchSupport",
+    "/System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore",
+    "/System/Library/Frameworks/VideoToolbox.framework/Versions/A/VideoToolbox",
+    "/System/Library/PrivateFrameworks/BaseBoard.framework/Versions/A/BaseBoard",
+    "/System/Library/PrivateFrameworks/AppleJPEG.framework/Versions/A/AppleJPEG",
+    "/usr/lib/libexpat.1.dylib",
+    "/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libPng.dylib",
+    "/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libTIFF.dylib",
+    "/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libGIF.dylib",
+    "/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJP2.dylib",
+    "/usr/lib/libate.dylib",
+    "/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJPEG.dylib",
+    "/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libRadiance.dylib",
+    "/System/Library/PrivateFrameworks/GPUWrangler.framework/Versions/A/GPUWrangler",
+    "/System/Library/PrivateFrameworks/IOPresentment.framework/Versions/A/IOPresentment",
+    "/System/Library/PrivateFrameworks/DSExternalDisplay.framework/Versions/A/DSExternalDisplay",
+    "/System/Library/PrivateFrameworks/GPUCompiler.framework/Versions/31001/Libraries/libllvm-flatbuffers.dylib",
+    "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreFSCache.dylib",
+    "/System/Library/PrivateFrameworks/GPUCompiler.framework/Versions/31001/Libraries/libGPUCompilerUtils.dylib",
+    "/System/Library/PrivateFrameworks/CMCaptureCore.framework/Versions/A/CMCaptureCore",
+    "/usr/lib/libspindump.dylib",
+    "/System/Library/Frameworks/CoreAudio.framework/Versions/A/CoreAudio",
+    "/System/Library/Frameworks/ExtensionFoundation.framework/Versions/A/ExtensionFoundation",
+    "/System/Library/PrivateFrameworks/CoreTime.framework/Versions/A/CoreTime",
+    "/System/Library/PrivateFrameworks/AppServerSupport.framework/Versions/A/AppServerSupport",
+    "/System/Library/PrivateFrameworks/perfdata.framework/Versions/A/perfdata",
+    "/System/Library/PrivateFrameworks/AudioToolboxCore.framework/Versions/A/AudioToolboxCore",
+    "/System/Library/PrivateFrameworks/caulk.framework/Versions/A/caulk",
+    "/usr/lib/libAudioStatistics.dylib",
+    "/System/Library/PrivateFrameworks/SystemPolicy.framework/Versions/A/SystemPolicy",
+    "/usr/lib/libSMC.dylib",
+    "/System/Library/Frameworks/CoreMIDI.framework/Versions/A/CoreMIDI",
+    "/usr/lib/libAudioToolboxUtility.dylib",
+    "/System/Library/PrivateFrameworks/OSAServicesClient.framework/Versions/A/OSAServicesClient",
+    "/usr/lib/libperfcheck.dylib",
+    "/System/Library/PrivateFrameworks/PlugInKit.framework/Versions/A/PlugInKit",
+    "/System/Library/PrivateFrameworks/AssertionServices.framework/Versions/A/AssertionServices",
+    "/System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL",
+    "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLU.dylib",
+    "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGFXShared.dylib",
+    "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib",
+    "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLImage.dylib",
+    "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCVMSPluginSupport.dylib",
+    "/usr/lib/libRosetta.dylib",
+    "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreVMClient.dylib",
+    "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSCore.framework/Versions/A/MPSCore",
+    "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSImage.framework/Versions/A/MPSImage",
+    "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSNeuralNetwork.framework/Versions/A/MPSNeuralNetwork",
+    "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSMatrix.framework/Versions/A/MPSMatrix",
+    "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSRayIntersector.framework/Versions/A/MPSRayIntersector",
+    "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSNDArray.framework/Versions/A/MPSNDArray",
+    "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSFunctions.framework/Versions/A/MPSFunctions",
+    "/System/Library/PrivateFrameworks/MetalTools.framework/Versions/A/MetalTools",
+    "/System/Library/PrivateFrameworks/AggregateDictionary.framework/Versions/A/AggregateDictionary",
+    "/usr/lib/libIOReport.dylib",
+    "/System/Library/Frameworks/CoreImage.framework/Versions/A/CoreImage",
+    "/System/Library/PrivateFrameworks/PhotosensitivityProcessing.framework/Versions/A/PhotosensitivityProcessing",
+    "/System/Library/Frameworks/OpenCL.framework/Versions/A/OpenCL",
+    "/System/Library/PrivateFrameworks/GraphVisualizer.framework/Versions/A/GraphVisualizer",
+    "/System/Library/PrivateFrameworks/FontServices.framework/Versions/A/FontServices",
+    "/System/Library/Frameworks/UniformTypeIdentifiers.framework/Versions/A/UniformTypeIdentifiers",
+    "/System/Library/PrivateFrameworks/OTSVG.framework/Versions/A/OTSVG",
+    "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/Resources/libFontRegistry.dylib",
+    "/System/Library/PrivateFrameworks/FontServices.framework/libhvf.dylib",
+    "/System/Library/PrivateFrameworks/FontServices.framework/libXTFontStaticRegistryData.dylib",
+    "/System/Library/PrivateFrameworks/VideoToolboxParavirtualizationSupport.framework/Versions/A/VideoToolboxParavirtualizationSupport",
+    "/System/Library/PrivateFrameworks/AppleVA.framework/Versions/A/AppleVA",
+    "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATSUI.framework/Versions/A/ATSUI",
+    "/usr/lib/libcups.2.dylib",
+    "/System/Library/Frameworks/Kerberos.framework/Versions/A/Kerberos",
+    "/System/Library/Frameworks/GSS.framework/Versions/A/GSS",
+    "/usr/lib/libresolv.9.dylib",
+    "/System/Library/PrivateFrameworks/Heimdal.framework/Versions/A/Heimdal",
+    "/System/Library/Frameworks/Kerberos.framework/Versions/A/Libraries/libHeimdalProxy.dylib",
+    "/System/Library/PrivateFrameworks/CommonAuth.framework/Versions/A/CommonAuth",
+    "/System/Library/Frameworks/AVFAudio.framework/Versions/A/AVFAudio",
+    "/System/Library/PrivateFrameworks/AXCoreUtilities.framework/Versions/A/AXCoreUtilities",
+    "/System/Library/Frameworks/AudioToolbox.framework/Versions/A/AudioToolbox",
+    "/System/Library/PrivateFrameworks/AudioSession.framework/Versions/A/AudioSession",
+    "/System/Library/Frameworks/IOBluetooth.framework/Versions/A/IOBluetooth",
+    "/System/Library/PrivateFrameworks/MediaExperience.framework/Versions/A/MediaExperience",
+    "/System/Library/PrivateFrameworks/AudioSession.framework/libSessionUtility.dylib",
+    "/System/Library/PrivateFrameworks/AudioResourceArbitration.framework/Versions/A/AudioResourceArbitration",
+    "/System/Library/PrivateFrameworks/PowerLog.framework/Versions/A/PowerLog",
+    "/System/Library/Frameworks/CoreData.framework/Versions/A/CoreData",
+    "/System/Library/Frameworks/CoreBluetooth.framework/Versions/A/CoreBluetooth",
+    "/System/Library/Frameworks/AudioUnit.framework/Versions/A/AudioUnit",
+    "/System/Library/PrivateFrameworks/CoreUtils.framework/Versions/A/CoreUtils",
+    "/System/Library/PrivateFrameworks/CoreUtilsExtras.framework/Versions/A/CoreUtilsExtras",
+    "/System/Library/PrivateFrameworks/IO80211.framework/Versions/A/IO80211",
+    "/System/Library/PrivateFrameworks/MobileKeyBag.framework/Versions/A/MobileKeyBag",
+    "/System/Library/PrivateFrameworks/MallocStackLogging.framework/Versions/A/MallocStackLogging"
+  ]
+}
\ No newline at end of file
diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts
index d7e7f07fa..66689f2f6 100644
--- a/src/client/apis/gpt/GPT.ts
+++ b/src/client/apis/gpt/GPT.ts
@@ -26,7 +26,7 @@ const openai = new OpenAIApi(configuration);
 
 const gptTrailSlideCustomization = async (inputText: string) => {
     const prompt =
-        'We are adding customization to a slide in a presentation. Given a natural language input, translate it into a json with the required fields: [title, presentation_transition, presentation_effect, and config_zoom]. title is the title/name of the slide. presentation_transition is a number in milliseconds for how long it should take to transition to a slide. presentation_effect is an effect applied to the slide when we transition to it. Its only possible values are:  [None, Fade in, Flip, Rotate, Bounce, Roll]. config zoom is a number from 0 to 1.0 indicating the percentage we should be zooming into the slide.If the input does not contain info a specific key, please set their value to null. Please only return the json with these keys and their values.';
+        'We are adding customization to a slide in a presentation. Given a natural language input, translate it into a json with the required fields: [title, presentation_transition, presentation_effect, and config_zoom]. title is the title/name of the slide. presentation_transition is a number in milliseconds for how long it should take to transition to a slide. presentation_effect is an effect applied to the slide when we transition to it. Its only possible values are:  [None, Fade in, Flip, Rotate, Bounce, Roll]. config zoom is a number from 0 to 1.0 indicating the percentage we should be zooming into the slide. If the input does not contain info a specific key, please set their value to null. Please only return the json with these keys and their values.';
 
     try {
         const response = await openai.createChatCompletion({
@@ -55,10 +55,6 @@ const gptAPICall = async (inputText: string, callType: GPTCallType) => {
     if (callType === GPTCallType.SUMMARY) inputText += '.';
     const opts: GPTCallOpts = callTypeMap[callType];
     try {
-        // const configuration = new Configuration({
-        //     apiKey: process.env.OPENAI_KEY,
-        // });
-        // const openai = new OpenAIApi(configuration);
         const response = await openai.createCompletion({
             model: opts.model,
             max_tokens: opts.maxTokens,
@@ -74,10 +70,6 @@ const gptAPICall = async (inputText: string, callType: GPTCallType) => {
 
 const gptImageCall = async (prompt: string, n?: number) => {
     try {
-        // const configuration = new Configuration({
-        //     apiKey: process.env.OPENAI_KEY,
-        // });
-        // const openai = new OpenAIApi(configuration);
         const response = await openai.createImage({
             prompt: prompt,
             n: n ?? 1,
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 90ebf5206..eab62c20b 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -966,6 +966,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     };
 
     breakupDictation = () => {
+        console.log('breakup');
         if (this._editorView && this._recordingDictation) {
             this.stopDictation(true);
             this._break = true;
diff --git a/src/client/views/nodes/trails/PresBox.scss b/src/client/views/nodes/trails/PresBox.scss
index ac0d58368..370b155fd 100644
--- a/src/client/views/nodes/trails/PresBox.scss
+++ b/src/client/views/nodes/trails/PresBox.scss
@@ -18,8 +18,8 @@
     .presBox-chatbox {
         position: fixed;
         bottom: 8px;
-        right: 8px;
-        width: 300px;
+        left: 8px;
+        width: calc(100% - 16px);
         min-height: 100px;
         border-radius: 16px;
         padding: 16px;
@@ -31,11 +31,12 @@
         background-color: #ffffff;
         box-shadow: 0 2px 5px #7474748d;
 
-        input {
-            resize: vertical;
-            // min-height: 125px;
-            border: none;
+        .pres-chatbox {
             outline: none;
+            border: none;
+            font-size: 16px;
+            letter-spacing: 1px;
+            color: #404040;
         }
     }
 
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index ebf9c211c..051a1fbfd 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -40,6 +40,9 @@ import { PresEffect, PresEffectDirection, PresMovement, PresStatus } from './Pre
 import { IconButton, Type } from 'browndash-components';
 import { AiOutlineSend } from 'react-icons/ai';
 import { gptTrailSlideCustomization } from '../../../apis/gpt/GPT';
+import { DictationManager } from '../../../util/DictationManager';
+import { BiMicrophone, BiX } from 'react-icons/bi';
+import TextareaAutosize from 'react-textarea-autosize';
 
 export interface pinDataTypes {
     scrollable?: boolean;
@@ -112,6 +115,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @observable chatActive: boolean = false;
     @observable chatInput: string = '';
     public slideToModify: Doc | null = null;
+    @observable isRecording: boolean = false;
 
     @action
     setChatInput = (input: string) => {
@@ -123,6 +127,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         this.chatActive = active;
     };
 
+    @action
+    public setIsRecording = (isRecording: boolean) => {
+        this.isRecording = isRecording;
+    };
+
     @computed
     get isTreeOrStack() {
         return [CollectionViewType.Tree, CollectionViewType.Stacking].includes(StrCast(this.layoutDoc._type_collection) as any);
@@ -260,6 +269,24 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
     };
 
+    // GPT
+
+    recordDictation = () => {
+        DictationManager.Controls.listen({
+            interimHandler: this.setDictationContent,
+            continuous: { indefinite: false },
+        }).then(results => {
+            if (results && [DictationManager.Controls.Infringed].includes(results)) {
+                DictationManager.Controls.stop();
+            }
+        });
+    };
+    stopDictation = (abort: boolean) => DictationManager.Controls.stop(!abort);
+
+    setDictationContent = (value: string) => {
+        this.setChatInput(value);
+    };
+
     @action
     customizeWithGPT = async (input: string) => {
         // const testInput = 'change title to Customized Slide, transition for 2.3s with fade in effect';
@@ -2714,8 +2741,50 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 {/* presbox chatbox */}
                 {this.chatActive && (
                     <div className="presBox-chatbox">
-                        <input placeholder="Customize..." value={this.chatInput} onChange={e => this.setChatInput(e.target.value)} />
                         <div style={{ alignSelf: 'flex-end' }}>
+                            <IconButton
+                                type={Type.PRIM}
+                                color={StrCast(Doc.UserDoc().userVariantColor)}
+                                tooltip="Close"
+                                icon={<BiX size={'16px'} />}
+                                onClick={() => {
+                                    this.setChatActive(false);
+                                }}
+                            />
+                        </div>
+                        <TextareaAutosize
+                            minRows={3}
+                            placeholder="Customize..."
+                            className="pres-chatbox"
+                            autoFocus={true}
+                            value={this.chatInput}
+                            onChange={e => {
+                                this.setChatInput(e.target.value);
+                            }}
+                            onSelect={e => {
+                                this.stopDictation(true);
+                            }}
+                            onKeyDown={e => e.stopPropagation()}
+                            onKeyPress={e => e.stopPropagation()}
+                            onPointerDown={e => e.stopPropagation()}
+                            onClick={e => e.stopPropagation()}
+                            onPointerUp={e => e.stopPropagation()}
+                        />
+                        {/* <input className="chatbox" placeholder="Customize..." value={this.chatInput} onChange={e => this.setChatInput(e.target.value)} /> */}
+                        <div style={{ alignSelf: 'flex-end', display: 'flex', gap: '8px' }}>
+                            <IconButton
+                                type={Type.TERT}
+                                color={this.isRecording ? 'red' : StrCast(Doc.UserDoc().userVariantColor)}
+                                tooltip="Record"
+                                icon={<BiMicrophone size={'16px'} />}
+                                onClick={() => {
+                                    if (!this.isRecording) {
+                                        this.recordDictation();
+                                    } else {
+                                        this.stopDictation(true);
+                                    }
+                                }}
+                            />
                             <IconButton
                                 type={Type.TERT}
                                 color={StrCast(Doc.UserDoc().userVariantColor)}
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 534a0f2aa..482d38f59 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -29,6 +29,7 @@ import { TreeView } from '../../collections/TreeView';
 import { BranchingTrailManager } from '../../../util/BranchingTrailManager';
 import { MultiToggle, Type } from 'browndash-components';
 import { gptTrailSlideCustomization } from '../../../apis/gpt/GPT';
+import { DictationManager } from '../../../util/DictationManager';
 /**
  * This class models the view a document added to presentation will have in the presentation.
  * It involves some functionality for its buttons and options.
@@ -566,6 +567,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     onClick={() => {
                         PresBox.Instance.setChatActive(true);
                         PresBox.Instance.slideToModify = this.rootDoc;
+                        PresBox.Instance.recordDictation();
                         // this.customizeWithGPT('');
                     }}>
                     <FontAwesomeIcon icon={'message'} onPointerDown={e => e.stopPropagation()} />
-- 
cgit v1.2.3-70-g09d2


From 8fccdb8c21015eb9204de7c24a80ece82f338d8e Mon Sep 17 00:00:00 2001
From: Sophie Zhang <sophie_zhang@brown.edu>
Date: Thu, 2 Nov 2023 13:10:48 -0400
Subject: gpt stylization

---
 src/client/apis/gpt/customization.ts               | 51 ++++++++++++++++++++--
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 51 ++++++++++++++++++++++
 src/client/views/nodes/trails/PresBox.tsx          |  2 +-
 src/client/views/nodes/trails/PresElementBox.tsx   | 24 ----------
 4 files changed, 99 insertions(+), 29 deletions(-)

(limited to 'src/client/views/nodes/trails/PresElementBox.tsx')

diff --git a/src/client/apis/gpt/customization.ts b/src/client/apis/gpt/customization.ts
index 3d9a0ead3..20dac0a4e 100644
--- a/src/client/apis/gpt/customization.ts
+++ b/src/client/apis/gpt/customization.ts
@@ -16,6 +16,10 @@ const prompts: { [key: string]: PromptInfo } = {
     },
 };
 
+export const addCustomizationProperty = (type: CustomizationType, name: string, description: string, values?: string[]) => {
+    values ? prompts[type].features.push({ name, description, values }) : prompts[type].features.push({ name, description });
+};
+
 const setupPresSlideCustomization = () => {
     addCustomizationProperty(CustomizationType.PRES_TRAIL_SLIDE, 'title', 'is the title/name of the slide.');
     addCustomizationProperty(CustomizationType.PRES_TRAIL_SLIDE, 'presentation_transition', 'is a number in milliseconds for how long it should take to transition/move to a slide.');
@@ -24,10 +28,6 @@ const setupPresSlideCustomization = () => {
 
 setupPresSlideCustomization();
 
-export const addCustomizationProperty = (type: CustomizationType, name: string, description: string, values?: string[]) => {
-    values ? prompts[type].features.push({ name, description, values }) : prompts[type].features.push({ name, description });
-};
-
 export const gptTrailSlideCustomization = async (inputText: string) => {
     let prompt = prompts.trails.description;
 
@@ -59,3 +59,46 @@ export const gptTrailSlideCustomization = async (inputText: string) => {
         return 'Error connecting with API.';
     }
 };
+
+// palette / styling
+export const generatePalette = async (inputData: any) => {
+    let prompt = 'Dash is a hypermedia web application that allows users to organize documents of different media types into collections. I want you to come up with a cohesive color palette for a collection.';
+    prompt +=
+        'I am going to give you a json object of this format:' +
+        JSON.stringify({ collectionDescription: 'string', documents: 'Document[]' }) +
+        '. collectionDescription is the title of the collection, which you should create a color palette based on. This is the document format:' +
+        JSON.stringify({
+            id: 'number',
+            textSize: 'number',
+            textContent: 'string',
+        }) +
+        '. You are going to create a color palette based mostly on collectionDescription, and loosely on the text content and text size of the documents. Return a json object in this format:' +
+        JSON.stringify({
+            collectionBackgroundColor: 'string',
+            documentsWithColors: 'DocumentWithColor[]',
+        }) +
+        '. collectionBackgroundColor, should be a string hex value for the background color of the collection. documentsWithColors has the same length and order of the input documents. DocumentWithColor has this format:' +
+        JSON.stringify({
+            id: 'number',
+            color: 'string',
+        }) +
+        ", and each element’s color is based on the theme of the overall color palette and also by its document’s textContent. Please pay attention to aesthetics of how each document's color complement the background and each other and choose a variety of colors when appropriate. Respond with only the JSON object.";
+
+    // console.log('Prompt', prompt);
+    try {
+        const response = await openai.createChatCompletion({
+            model: 'gpt-4',
+            messages: [
+                { role: 'system', content: prompt },
+                { role: 'user', content: JSON.stringify(inputData) },
+            ],
+            temperature: 0.1,
+            max_tokens: 2000,
+        });
+        console.log(response.data.choices[0].message?.content);
+        return response.data.choices[0].message?.content;
+    } catch (err) {
+        console.log(err);
+        return 'Error connecting with API.';
+    }
+};
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 0c3033579..002ebf1ae 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -52,6 +52,7 @@ import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCurso
 import './CollectionFreeFormView.scss';
 import { MarqueeView } from './MarqueeView';
 import React = require('react');
+import { generatePalette } from '../../../apis/gpt/customization';
 
 export type collectionFreeformViewProps = {
     NativeWidth?: () => number;
@@ -1573,6 +1574,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     componentDidMount() {
+        this.printDoc(this.rootDoc);
         this.props.setContentView?.(this);
         super.componentDidMount?.();
         this.props.setBrushViewer?.(this.brushView);
@@ -1787,6 +1789,53 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         }
     };
 
+    printDoc = (doc: Doc) => {
+        console.log('Printing keys');
+        Object.keys(doc).forEach(key => {
+            console.log(key, ':', doc[key]);
+        });
+    };
+
+    // gpt styling
+    @action
+    gptStyling = async () => {
+        console.log('Title', this.rootDoc.title);
+        console.log('bgcolor', this.layoutDoc._backgroundColor);
+        // doc.backgroundColor
+        console.log('styling');
+        const inputDocs = this.childDocs.filter(doc => doc.type == 'rich text');
+        const gptInput = inputDocs.map((doc, i) => ({
+            id: i,
+            textContent: (doc.text as RichTextField)?.Text,
+            textSize: 16,
+        }));
+
+        // inputDocs[0].backgroundColor = '#3392ff';
+
+        const collectionDescription = StrCast(this.rootDoc.title);
+
+        console.log({
+            collectionDescription,
+            documents: gptInput,
+        });
+
+        try {
+            const res = await generatePalette({
+                collectionDescription,
+                documents: gptInput,
+            });
+            console.log('done');
+            if (typeof res === 'string') {
+                const resObj = JSON.parse(res);
+                console.log('Result ', resObj);
+                this.rootDoc.backgroundColor = resObj.collectionBackgroundColor;
+                (resObj.documentsWithColors as any[]).forEach((elem, i) => (inputDocs[i].backgroundColor = elem.color));
+            }
+        } catch (err) {
+            console.error(err);
+        }
+    };
+
     onContextMenu = (e: React.MouseEvent) => {
         if (this.props.isAnnotationOverlay || !ContextMenu.Instance) return;
 
@@ -1826,6 +1875,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             optionItems.push({ description: (this._showAnimTimeline ? 'Close' : 'Open') + ' Animation Timeline', event: action(() => (this._showAnimTimeline = !this._showAnimTimeline)), icon: 'eye' });
         this.props.renderDepth && optionItems.push({ description: 'Use Background Color as Default', event: () => (Cast(Doc.UserDoc().emptyCollection, Doc, null)._backgroundColor = StrCast(this.layoutDoc._backgroundColor)), icon: 'palette' });
         this.props.renderDepth && optionItems.push({ description: 'Fit Content Once', event: this.fitContentOnce, icon: 'object-group' });
+        // gpt styling
+        this.props.renderDepth && optionItems.push({ description: 'Style with AI', event: this.gptStyling, icon: 'paint-brush' });
         if (!Doc.noviceMode) {
             optionItems.push({ description: (!Doc.NativeWidth(this.layoutDoc) || !Doc.NativeHeight(this.layoutDoc) ? 'Freeze' : 'Unfreeze') + ' Aspect', event: this.toggleNativeDimensions, icon: 'snowflake' });
         }
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 87ffb0112..19f271246 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -40,10 +40,10 @@ import ReactLoading from 'react-loading';
 import { PresEffect, PresEffectDirection, PresMovement, PresStatus } from './PresEnums';
 import { IconButton, Type } from 'browndash-components';
 import { AiOutlineSend } from 'react-icons/ai';
-import { gptTrailSlideCustomization } from '../../../apis/gpt/GPT';
 import { DictationManager } from '../../../util/DictationManager';
 import { BiMicrophone, BiX } from 'react-icons/bi';
 import TextareaAutosize from 'react-textarea-autosize';
+import { gptTrailSlideCustomization } from '../../../apis/gpt/customization';
 
 export interface pinDataTypes {
     scrollable?: boolean;
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 482d38f59..37f449001 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -26,10 +26,6 @@ import './PresElementBox.scss';
 import { PresMovement } from './PresEnums';
 import React = require('react');
 import { TreeView } from '../../collections/TreeView';
-import { BranchingTrailManager } from '../../../util/BranchingTrailManager';
-import { MultiToggle, Type } from 'browndash-components';
-import { gptTrailSlideCustomization } from '../../../apis/gpt/GPT';
-import { DictationManager } from '../../../util/DictationManager';
 /**
  * This class models the view a document added to presentation will have in the presentation.
  * It involves some functionality for its buttons and options.
@@ -438,25 +434,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     // GPT
 
-    @action
-    customizeWithGPT = async (input: string) => {
-        const testInput = 'change title to Customized Slide, transition for 2.3s with fade in effect';
-
-        try {
-            const res = await gptTrailSlideCustomization(testInput);
-            if (typeof res === 'string') {
-                const resObj = JSON.parse(res);
-                console.log('Result ', resObj);
-
-                for (let key in resObj) {
-                    this.rootDoc[key] = resObj[key];
-                }
-            }
-        } catch (err) {
-            console.error(err);
-        }
-    };
-
     @computed get presButtons() {
         const presBox = this.presBox;
         const presBoxColor = StrCast(presBox?._backgroundColor);
@@ -568,7 +545,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         PresBox.Instance.setChatActive(true);
                         PresBox.Instance.slideToModify = this.rootDoc;
                         PresBox.Instance.recordDictation();
-                        // this.customizeWithGPT('');
                     }}>
                     <FontAwesomeIcon icon={'message'} onPointerDown={e => e.stopPropagation()} />
                 </div>
-- 
cgit v1.2.3-70-g09d2


From eb5746da51bf44aeacb41d6337f666e178fae88e Mon Sep 17 00:00:00 2001
From: Sophie Zhang <sophie_zhang@brown.edu>
Date: Thu, 29 Feb 2024 01:25:37 -0500
Subject: added curr slide info to slide customization

---
 src/client/apis/gpt/customization.ts             | 24 +++++++----
 src/client/views/nodes/trails/PresBox.scss       |  1 +
 src/client/views/nodes/trails/PresBox.tsx        | 53 +++++++++++++++++-------
 src/client/views/nodes/trails/PresElementBox.tsx |  4 +-
 4 files changed, 56 insertions(+), 26 deletions(-)

(limited to 'src/client/views/nodes/trails/PresElementBox.tsx')

diff --git a/src/client/apis/gpt/customization.ts b/src/client/apis/gpt/customization.ts
index db5ef6eb5..946208eeb 100644
--- a/src/client/apis/gpt/customization.ts
+++ b/src/client/apis/gpt/customization.ts
@@ -35,7 +35,7 @@ interface PromptInfo {
 const prompts: { [key: string]: PromptInfo } = {
     trails: {
         description:
-            'We are adding customization to a slide in a presentation. Given a natural language input, translate it into a json with the required fields: [title, presentation_transition, presentation_effect, config_zoom, presentation_effectDirection].',
+            'We are customizing the properties and transition of a slide in a presentation. You are given the current properties of the slide in a json with the fields [title, presentation_transition, presentation_effect, config_zoom, presentation_effectDirection], as well as the prompt for how the user wants to change it. Return a json with the required fields: [title, presentation_transition, presentation_effect, config_zoom, presentation_effectDirection] by applying the changes in the prompt to the current state of the slide.',
         features: [],
     },
 };
@@ -44,19 +44,24 @@ export const addCustomizationProperty = (type: CustomizationType, name: string,
     values ? prompts[type].features.push({ name, description, values }) : prompts[type].features.push({ name, description });
 };
 
+export const gptSlideProperties = ['title', 'presentation_transition', 'presentation_effect', 'presentation_effectDirection', 'config_zoom'];
+
 const setupPresSlideCustomization = () => {
     addCustomizationProperty(CustomizationType.PRES_TRAIL_SLIDE, 'title', 'is the title/name of the slide.');
     addCustomizationProperty(CustomizationType.PRES_TRAIL_SLIDE, 'presentation_transition', 'is a number in milliseconds for how long it should take to transition/move to a slide.');
     addCustomizationProperty(CustomizationType.PRES_TRAIL_SLIDE, 'presentation_effect', 'is an effect applied to the slide when we transition to it.', ['None', 'Fade in', 'Flip', 'Rotate', 'Bounce', 'Roll']);
+    addCustomizationProperty(CustomizationType.PRES_TRAIL_SLIDE, 'presentation_effectDirection', 'is what direction the effect is applied.', ['Enter from left', 'Enter from right', 'Enter from bottom', 'Enter from Top', 'Enter from center']);
+    addCustomizationProperty(CustomizationType.PRES_TRAIL_SLIDE, 'config_zoom', 'is a number from 0 to 1.0 indicating the percentage we should zoom into the slide.');
 };
 
 setupPresSlideCustomization();
 
-export const gptTrailSlideCustomization = async (inputText: string) => {
+export const gptTrailSlideCustomization = async (inputText: string, properties: any) => {
+    console.log('properties', properties);
     let prompt = prompts.trails.description;
 
     prompts.trails.features.forEach(feature => {
-        prompt += feature.name + feature.description;
+        prompt += feature.name + ' ' + feature.description;
         if (feature.values) {
             prompt += `Its only possible values are [${feature.values.join(', ')}].`;
         }
@@ -65,16 +70,21 @@ export const gptTrailSlideCustomization = async (inputText: string) => {
     // prompt +=
     //     'title is the title/name of the slide. presentation_transition is a number in milliseconds for how long it should take to transition/move to a slide. presentation_effect is an effect applied to the slide when we transition to it. Its only possible values are: [None, Fade in, Flip, Rotate, Bounce, Roll]. presentation_effectDirection is what direction the effect is applied. Its only possible values are:  [Enter from left, Enter from right, Enter from bottom, Enter from Top, Enter from center]. config_zoom is a number from 0 to 1.0 indicating the percentage we should zoom into the slide.';
 
-    prompt += 'If the input does not contain info a specific key, please set their value to null. Please only return the json with these keys and their values.';
+    prompt += 'Set unchanged values to null. Please only return the json with these keys and their values.';
+
+    console.log('messages', [
+        { role: 'system', content: prompt },
+        { role: 'user', content: `Prompt: ${inputText}, Current properties: ${JSON.stringify(properties)}` },
+    ]);
 
     try {
         const response = await openai.chat.completions.create({
-            model: 'gpt-3.5-turbo',
+            model: 'gpt-4',
             messages: [
                 { role: 'system', content: prompt },
-                { role: 'user', content: inputText },
+                { role: 'user', content: `Prompt: ${inputText}, Current properties: ${JSON.stringify(properties)}` },
             ],
-            temperature: 0.1,
+            temperature: 0,
             max_tokens: 1000,
         });
         return response.choices[0].message?.content;
diff --git a/src/client/views/nodes/trails/PresBox.scss b/src/client/views/nodes/trails/PresBox.scss
index 1537ad0b8..5fc356299 100644
--- a/src/client/views/nodes/trails/PresBox.scss
+++ b/src/client/views/nodes/trails/PresBox.scss
@@ -34,6 +34,7 @@
         .pres-chatbox {
             outline: none;
             border: none;
+            resize: none;
         }
     }
 
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 1b1b65e46..ba80facce 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -38,10 +38,10 @@ import './PresBox.scss';
 import ReactLoading from 'react-loading';
 import { PresEffect, PresEffectDirection, PresMovement, PresStatus } from './PresEnums';
 import ReactTextareaAutosize from 'react-textarea-autosize';
-import { IconButton, Type } from 'browndash-components';
+import { Dropdown, IconButton, Type } from 'browndash-components';
 import { BiMicrophone, BiX } from 'react-icons/bi';
 import { AiOutlineSend } from 'react-icons/ai';
-import { gptTrailSlideCustomization } from '../../../apis/gpt/customization';
+import { gptSlideProperties, gptTrailSlideCustomization } from '../../../apis/gpt/customization';
 import { DictationManager } from '../../../util/DictationManager';
 export interface pinDataTypes {
     scrollable?: boolean;
@@ -263,7 +263,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
     };
 
-    // GPT
+    // Recording for GPT customization
 
     recordDictation = () => {
         this.setIsRecording(true);
@@ -283,6 +283,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     };
 
     setDictationContent = (value: string) => {
+        console.log('Dictation value', value);
         this.setChatInput(value);
         // // Get the current cursor position
         // if (!this._inputref) return;
@@ -306,24 +307,31 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @action
     customizeWithGPT = async (input: string) => {
-        console.log(this.slideToModify);
+        console.log('Slide to modify', this.slideToModify);
         // const testInput = 'change title to Customized Slide, transition for 2.3s with fade in effect';
         // if (!this.slideToModify) return;
         this.setIsRecording(false);
         this.setIsLoading(true);
+
+        let currSlideProperties: { [key: string]: any } = {};
+        for (const key of gptSlideProperties) {
+            currSlideProperties[key] = this.activeItem[key];
+        }
+        console.log('current slide props', currSlideProperties);
+
         try {
-            const res = await gptTrailSlideCustomization(input);
-            console.log('slide result', res);
-            // if (typeof res === 'string') {
-            //     const resObj = JSON.parse(res);
-            //     console.log('Result ', resObj);
-            //     // this.activeItem
-            //     for (let key in resObj) {
-            //         if (resObj[key]) {
-            //             this.slideToModify[key] = resObj[key];
-            //         }
-            //     }
-            // }
+            const res = await gptTrailSlideCustomization(input, currSlideProperties);
+            console.log('GPT Result:', res);
+            if (typeof res === 'string') {
+                const resObj = JSON.parse(res);
+                console.log('Parsed GPT Result ', resObj);
+                // this.activeItem
+                for (let key in resObj) {
+                    if (resObj[key]) {
+                        this.activeItem[key] = resObj[key];
+                    }
+                }
+            }
         } catch (err) {
             console.error(err);
         }
@@ -1833,6 +1841,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     })}>
                     <div className="ribbon-box">
                         Movement
+                        {/* <Dropdown
+                        color={StrCast(Doc.UserDoc().userColor)}
+                        formLabel={'Type'}
+                        closeOnSelect={true}
+                        items={bugDropdownItems}
+                        selectedVal={this.formData.type}
+                        setSelectedVal={val => {
+                            if (typeof val === 'string') this.setFormData({ ...this.formData, type: val as BugType });
+                        }}
+                        dropdownType={DropdownType.SELECT}
+                        type={Type.TERT}
+                        fillWidth
+                    /> */}
                         <div
                             className="presBox-dropdown"
                             onClick={action(e => {
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index ed2f25fb6..4abfc5a94 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -409,8 +409,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return presBoxDocView ? presBoxDocView._props.PanelWidth() : width ? width : 300;
     }
 
-    // GPT
-
     @computed get presButtons() {
         const presBox = this.presBox;
         const presBoxColor = StrCast(presBox?._backgroundColor);
@@ -520,7 +518,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     className={'slideButton'}
                     onClick={() => {
                         PresBox.Instance.setChatActive(true);
-                        PresBox.Instance.slideToModify = this.rootDoc;
+                        PresBox.Instance.slideToModify = this.Document;
                         PresBox.Instance.recordDictation();
                     }}>
                     <FontAwesomeIcon icon={'message'} onPointerDown={e => e.stopPropagation()} />
-- 
cgit v1.2.3-70-g09d2


From 70b582db9a8cecc742be3c2b5f043a70a15c7991 Mon Sep 17 00:00:00 2001
From: Sophie Zhang <sophie_zhang@brown.edu>
Date: Thu, 18 Apr 2024 11:13:52 -0400
Subject: dropdowns, ui, roll

---
 src/client/views/nodes/DocumentView.tsx          |  11 +-
 src/client/views/nodes/trails/PresBox.scss       |   9 +
 src/client/views/nodes/trails/PresBox.tsx        | 201 ++++++++++++++---------
 src/client/views/nodes/trails/PresElementBox.tsx |   9 +-
 src/client/views/nodes/trails/SlideEffect.tsx    |  51 ++++--
 5 files changed, 174 insertions(+), 107 deletions(-)

(limited to 'src/client/views/nodes/trails/PresElementBox.tsx')

diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index eaa2c6abc..801d96b35 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -997,7 +997,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
      * @returns a function that will wrap a JSX animation element wrapping any JSX element
      */
     public static AnimationEffect(renderDoc: JSX.Element, presEffectDoc: Opt<Doc>, root: Doc) {
-        const dir = presEffectDoc?.presentation_effectDirection ?? presEffectDoc?.followLinkAnimDirection;
+        let dir = presEffectDoc?.presentation_effectDirection ?? presEffectDoc?.followLinkAnimDirection;
         const effectProps = {
             left: dir === PresEffectDirection.Left,
             right: dir === PresEffectDirection.Right,
@@ -1021,21 +1021,24 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
             };
         }
 
+        if (!dir) {
+            dir = PresEffectDirection.Center;
+        }
+
         const transitionTime = presEffectDoc?.presentation_transition ? NumCast(presEffectDoc?.presentation_transition) : 500;
         //prettier-ignore
 
         switch (StrCast(presEffectDoc?.presentation_effect, StrCast(presEffectDoc?.followLinkAnimEffect))) {
             default:
-            // package used: react-awesome-reveal
             case PresEffect.None:       return renderDoc;
             case PresEffect.Zoom:       return <SlideEffect doc={root} delay={transitionTime} dir={dir as PresEffectDirection} presEffect={PresEffect.Zoom} tension={timingConfig.stiffness} friction={timingConfig.damping} mass={timingConfig.mass}>{renderDoc}</SlideEffect>
             case PresEffect.Fade:       return <SlideEffect  doc={root} delay={transitionTime} dir={dir as PresEffectDirection} presEffect={PresEffect.Fade} tension={timingConfig.stiffness} friction={timingConfig.damping} mass={timingConfig.mass}>{renderDoc}</SlideEffect>
             case PresEffect.Flip:       return <SlideEffect doc={root} delay={transitionTime} dir={dir as PresEffectDirection} presEffect={PresEffect.Flip} tension={timingConfig.stiffness} friction={timingConfig.damping} mass={timingConfig.mass}>{renderDoc}</SlideEffect>
             case PresEffect.Rotate:       return <SlideEffect  doc={root} delay={transitionTime} dir={dir as PresEffectDirection} presEffect={PresEffect.Rotate} tension={timingConfig.stiffness} friction={timingConfig.damping} mass={timingConfig.mass}>{renderDoc}</SlideEffect>
-            // Potential change to move in since anything can be "bouncy"
             case PresEffect.Bounce:     return <SlideEffect doc={root} delay={transitionTime} dir={dir as PresEffectDirection} presEffect={PresEffect.Bounce} tension={timingConfig.stiffness} friction={timingConfig.damping} mass={timingConfig.mass}>{renderDoc}</SlideEffect>
             case PresEffect.Roll:       return <SlideEffect doc={root} delay={transitionTime} dir={dir as PresEffectDirection} presEffect={PresEffect.Roll} tension={timingConfig.stiffness} friction={timingConfig.damping} mass={timingConfig.mass}>{renderDoc}</SlideEffect>
-            case PresEffect.Lightspeed: return <SlideEffect doc={root} delay={transitionTime} dir={dir as PresEffectDirection} presEffect={PresEffect.Lightspeed} tension={timingConfig.stiffness} friction={timingConfig.damping} mass={timingConfig.mass}>{renderDoc}</SlideEffect>;
+            // keep as preset, doesn't really make sense with spring config
+            case PresEffect.Lightspeed: return <JackInTheBox {...effectProps}>{renderDoc}</JackInTheBox>;
         }
         // switch (StrCast(presEffectDoc?.presentation_effect, StrCast(presEffectDoc?.followLinkAnimEffect))) {
         //     default:
diff --git a/src/client/views/nodes/trails/PresBox.scss b/src/client/views/nodes/trails/PresBox.scss
index c937730ab..adc59d812 100644
--- a/src/client/views/nodes/trails/PresBox.scss
+++ b/src/client/views/nodes/trails/PresBox.scss
@@ -17,6 +17,7 @@
     display: flex;
     gap: 8px;
 }
+
 .pres-chatbox-container {
     padding: 16px;
     outline: 1px solid #999999;
@@ -37,6 +38,14 @@
 
 // Bezier editor
 
+.presBox-show-hide-dropdown {
+    cursor: pointer;
+    padding: 8px 0;
+    display: flex;
+    align-items: center;
+    gap: 4px;
+}
+
 .presBox-bezier-editor {
     border: 1px solid rgb(221, 221, 221);
     border-radius: 4px;
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index b192dc3db..62458acfe 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -137,15 +137,22 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     };
 
     @action
-    public setChatActive = (active: boolean) => {
-        this.toggleProperties();
-    };
+    public setChatActive = (active: boolean) => {};
 
     @action
     public setIsRecording = (isRecording: boolean) => {
         this.isRecording = isRecording;
     };
 
+    @observable showBezierEditor = false;
+    @action setBezierEditorVisibility = (visible: boolean) => {
+        this.showBezierEditor = visible;
+    };
+    @observable showSpringEditor = true;
+    @action setSpringEditorVisibility = (visible: boolean) => {
+        this.showSpringEditor = visible;
+    };
+
     // Easing function variables
 
     @observable easeDropdownVal = 'ease';
@@ -2098,6 +2105,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                     setSelectedVal={val => {
                                         if (typeof val === 'string') {
                                             if (val !== 'custom') {
+                                                this.setBezierEditorVisibility(true);
                                                 this.setEaseFunc(this.activeItem, val);
                                             } else {
                                                 this.setEaseFunc(this.activeItem, TIMING_DEFAULT_MAPPINGS.ease);
@@ -2108,8 +2116,24 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                     type={Type.TERT}
                                 />
                                 {/* Custom */}
-                                <p className="presBox-submenu-label">Custom Timing Function</p>
-                                <CubicBezierEditor setFunc={this.setBezierControlPoints} currPoints={this.currCPoints} easeFunc={StrCast(this.activeItem.presEaseFunc)} />
+                                <div
+                                    className="presBox-show-hide-dropdown"
+                                    style={{ alignSelf: 'flex-start' }}
+                                    onClick={e => {
+                                        e.stopPropagation();
+                                        this.setBezierEditorVisibility(!this.showBezierEditor);
+                                    }}>
+                                    {`${this.showBezierEditor ? 'Hide' : 'Show'} Timing Editor`}
+                                    <FontAwesomeIcon icon={this.showBezierEditor ? 'chevron-up' : 'chevron-down'} />
+                                </div>
+                                {this.showBezierEditor && (
+                                    <>
+                                        <p className="presBox-submenu-label" style={{ alignSelf: 'flex-start' }}>
+                                            Custom Timing Function
+                                        </p>
+                                        <CubicBezierEditor setFunc={this.setBezierControlPoints} currPoints={this.currCPoints} easeFunc={StrCast(this.activeItem.presEaseFunc)} />
+                                    </>
+                                )}
                             </div>
                         </div>
                         <div className="presBox-option-block" style={{ padding: '16px' }}>
@@ -2235,87 +2259,94 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                     onClick={() => this.updateEffectDirection(PresEffectDirection.Bottom)}
                                 />
                             </div>
-                            {/* <div className="effectDirection" style={{ display: effect === PresEffectDirection.None ? 'none' : 'grid', width: 40 }}>
-                                {presDirection(PresEffectDirection.Left, 'angle-right', 1, 2, {})}
-                                {presDirection(PresEffectDirection.Right, 'angle-left', 3, 2, {})}
-                                {presDirection(PresEffectDirection.Top, 'angle-down', 2, 1, {})}
-                                {presDirection(PresEffectDirection.Bottom, 'angle-up', 2, 3, {})}
-                                {presDirection(PresEffectDirection.Center, '', 2, 2, { width: 10, height: 10, alignSelf: 'center' })}
-                            </div> */}
-                            {/* Effect spring settings */}
-                            <Dropdown
-                                color={StrCast(Doc.UserDoc().userColor)}
-                                formLabel={'Effect Timing'}
-                                closeOnSelect={true}
-                                items={effectTimings}
-                                selectedVal={timingConfig.type}
-                                setSelectedVal={val => {
-                                    console.log('effect timing', val);
-                                    this.updateEffectTiming(activeItem, {
-                                        type: val as SpringType,
-                                        ...springMappings[val],
-                                    });
-                                }}
-                                dropdownType={DropdownType.SELECT}
-                                type={Type.TERT}
-                            />
-                            <div>Tension</div>
+                            {/* Spring settings */}
+                            {/* No spring settings for jackinthebox (lightspeed) */}
                             <div
-                                onPointerDown={e => {
+                                className="presBox-show-hide-dropdown"
+                                onClick={e => {
                                     e.stopPropagation();
+                                    this.setSpringEditorVisibility(!this.showSpringEditor);
                                 }}>
-                                <Slider
-                                    min={1}
-                                    max={1000}
-                                    step={5}
-                                    size="small"
-                                    value={timingConfig.stiffness}
-                                    onChange={(e, val) => {
-                                        if (!timingConfig) return;
-                                        this.updateEffectTiming(activeItem, { ...timingConfig, type: SpringType.CUSTOM, stiffness: val as number });
-                                    }}
-                                    valueLabelDisplay="auto"
-                                />
+                                {`${this.showSpringEditor ? 'Hide' : 'Show'} Spring Settings`}
+                                <FontAwesomeIcon icon={this.showSpringEditor ? 'chevron-up' : 'chevron-down'} />
                             </div>
-                            <div>Damping</div>
-                            <div
-                                onPointerDown={e => {
-                                    e.stopPropagation();
-                                }}>
-                                <Slider
-                                    min={1}
-                                    max={100}
-                                    step={1}
-                                    size="small"
-                                    value={timingConfig.damping}
-                                    onChange={(e, val) => {
-                                        if (!timingConfig) return;
-                                        this.updateEffectTiming(activeItem, { ...timingConfig, type: SpringType.CUSTOM, damping: val as number });
-                                    }}
-                                    valueLabelDisplay="auto"
-                                />
-                            </div>
-                            <div>Mass</div>
-                            <div
-                                onPointerDown={e => {
-                                    e.stopPropagation();
-                                }}>
-                                <Slider
-                                    min={1}
-                                    max={10}
-                                    step={1}
-                                    size="small"
-                                    value={timingConfig.mass}
-                                    onChange={(e, val) => {
-                                        if (!timingConfig) return;
-                                        this.updateEffectTiming(activeItem, { ...timingConfig, type: SpringType.CUSTOM, mass: val as number });
-                                    }}
-                                    valueLabelDisplay="auto"
-                                />
-                            </div>
-                            <SpringAnimationPreview tension={timingConfig.stiffness} friction={timingConfig.damping} mass={timingConfig.mass}>
-                                <div style={{ width: '40px', height: '40px', backgroundColor: '#2e96ff', borderRadius: '4px' }}></div>
-                            </SpringAnimationPreview>
+                            {effect !== PresEffect.Lightspeed && this.showSpringEditor && (
+                                <>
+                                    <Dropdown
+                                        color={StrCast(Doc.UserDoc().userColor)}
+                                        formLabel={'Effect Timing'}
+                                        closeOnSelect={true}
+                                        items={effectTimings}
+                                        selectedVal={timingConfig.type}
+                                        setSelectedVal={val => {
+                                            this.updateEffectTiming(activeItem, {
+                                                type: val as SpringType,
+                                                ...springMappings[val],
+                                            });
+                                        }}
+                                        dropdownType={DropdownType.SELECT}
+                                        type={Type.TERT}
+                                    />
+                                    <div>Tension</div>
+                                    <div
+                                        onPointerDown={e => {
+                                            e.stopPropagation();
+                                        }}>
+                                        <Slider
+                                            min={1}
+                                            max={1000}
+                                            step={5}
+                                            size="small"
+                                            value={timingConfig.stiffness}
+                                            onChange={(e, val) => {
+                                                if (!timingConfig) return;
+                                                this.updateEffectTiming(activeItem, { ...timingConfig, type: SpringType.CUSTOM, stiffness: val as number });
+                                            }}
+                                            valueLabelDisplay="auto"
+                                        />
+                                    </div>
+                                    <div>Damping</div>
+                                    <div
+                                        onPointerDown={e => {
+                                            e.stopPropagation();
+                                        }}>
+                                        <Slider
+                                            min={1}
+                                            max={100}
+                                            step={1}
+                                            size="small"
+                                            value={timingConfig.damping}
+                                            onChange={(e, val) => {
+                                                if (!timingConfig) return;
+                                                this.updateEffectTiming(activeItem, { ...timingConfig, type: SpringType.CUSTOM, damping: val as number });
+                                            }}
+                                            valueLabelDisplay="auto"
+                                        />
+                                    </div>
+                                    <div>Mass</div>
+                                    <div
+                                        onPointerDown={e => {
+                                            e.stopPropagation();
+                                        }}>
+                                        <Slider
+                                            min={1}
+                                            max={10}
+                                            step={1}
+                                            size="small"
+                                            value={timingConfig.mass}
+                                            onChange={(e, val) => {
+                                                if (!timingConfig) return;
+                                                this.updateEffectTiming(activeItem, { ...timingConfig, type: SpringType.CUSTOM, mass: val as number });
+                                            }}
+                                            valueLabelDisplay="auto"
+                                        />
+                                    </div>
+                                    <SpringAnimationPreview tension={timingConfig.stiffness} friction={timingConfig.damping} mass={timingConfig.mass}>
+                                        <div style={{ width: '40px', height: '40px', backgroundColor: '#2e96ff', borderRadius: '4px' }}></div>
+                                    </SpringAnimationPreview>
+                                </>
+                            )}
+                            {/* Effect spring settings */}
                         </div>
                         <div className="ribbon-final-box">
                             <div className="ribbon-final-button-hidden" onClick={() => this.applyTo(this.childDocs)}>
@@ -2734,6 +2765,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     toggleProperties = () => (SettingsManager.Instance.propertiesWidth = SettingsManager.Instance.propertiesWidth > 0 ? 0 : 250);
 
+    @action
+    openProperties = () => {
+        // need to also focus slide
+        SettingsManager.Instance.propertiesWidth = 250;
+    };
+
     @computed get toolbar() {
         const propIcon = SettingsManager.Instance.propertiesWidth > 0 ? 'angle-double-right' : 'angle-double-left';
         const propTitle = SettingsManager.Instance.propertiesWidth > 0 ? 'Close Presentation Panel' : 'Open Presentation Panel';
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 4abfc5a94..449b8e07f 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -513,15 +513,16 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
             </Tooltip>
         );
         items.push(
-            <Tooltip key="customize" title={<div className="dash-tooltip">Customize</div>}>
+            <Tooltip key="edit" title={<div className="dash-tooltip">Edit</div>}>
                 <div
                     className={'slideButton'}
                     onClick={() => {
-                        PresBox.Instance.setChatActive(true);
+                        PresBox.Instance.navigateToActiveItem();
+                        PresBox.Instance.openProperties();
                         PresBox.Instance.slideToModify = this.Document;
-                        PresBox.Instance.recordDictation();
+                        // PresBox.Instance.recordDictation();
                     }}>
-                    <FontAwesomeIcon icon={'message'} onPointerDown={e => e.stopPropagation()} />
+                    <FontAwesomeIcon icon={'edit'} onPointerDown={e => e.stopPropagation()} />
                 </div>
             </Tooltip>
         );
diff --git a/src/client/views/nodes/trails/SlideEffect.tsx b/src/client/views/nodes/trails/SlideEffect.tsx
index d4dfae059..20ff4b0b9 100644
--- a/src/client/views/nodes/trails/SlideEffect.tsx
+++ b/src/client/views/nodes/trails/SlideEffect.tsx
@@ -139,6 +139,7 @@ export default function SpringAnimation({ doc, delay, dir, friction, tension, ma
                     },
                 };
             default:
+                // no movement for center
                 return {
                     from: {
                         opacity: 0,
@@ -165,23 +166,6 @@ export default function SpringAnimation({ doc, delay, dir, friction, tension, ma
         ],
     };
 
-    // Flip
-    // const flipConfig = {
-    //     from: {
-    //         // opacity: 0,
-    //         transform: `perspective(600px) rotateX(0deg)`,
-    //     },
-    //     to: {
-    //         // opacity: 1,
-    //         transform: `perspective(600px) rotateX(180deg)`,
-    //         config: {
-    //             tension: tension,
-    //             friction: friction,
-    //             mass: mass,
-    //         },
-    //     },
-    // };
-
     const flipConfig = {
         from: {
             x: 0,
@@ -196,6 +180,32 @@ export default function SpringAnimation({ doc, delay, dir, friction, tension, ma
         },
     };
 
+    const rollConfig = {
+        from: {
+            opacity: 0,
+            x: -100,
+            y: -120,
+        },
+        to: {
+            opacity: 1,
+            x: 0,
+            y: 0,
+            config: {
+                tension: tension,
+                friction: friction,
+                mass: mass,
+            },
+        },
+    };
+
+    const lightspeedConfig = {
+        from: {
+            opacity: 0,
+        },
+        to: [],
+        loop: true,
+    };
+
     // Switch animation depending on slide effect
     const startAnimation = () => {
         switch (presEffect) {
@@ -215,6 +225,7 @@ export default function SpringAnimation({ doc, delay, dir, friction, tension, ma
                 api.start(flipConfig);
                 break;
             case PresEffect.Roll:
+                api.start(rollConfig);
                 break;
             case PresEffect.Lightspeed:
                 break;
@@ -269,6 +280,12 @@ export default function SpringAnimation({ doc, delay, dir, friction, tension, ma
                         )}
                     </div>
                 );
+            case PresEffect.Roll:
+                return (
+                    <animated.div ref={ref} style={{ opacity: springs.opacity, transform: to([springs.x, springs.y], (val, val2) => `translate3d(${val}%, 0, 0) rotate3d(0, 0, 1, ${val2}deg)`) }}>
+                        {children}
+                    </animated.div>
+                );
             default:
                 return (
                     <animated.div
-- 
cgit v1.2.3-70-g09d2


From 255495874b3fd2644cf05f330d15194318adcc55 Mon Sep 17 00:00:00 2001
From: Sophie Zhang <sophie_zhang@brown.edu>
Date: Tue, 14 May 2024 12:19:29 -0400
Subject: bug fixing

---
 .../views/nodes/trails/CubicBezierEditor.tsx       |  1 -
 src/client/views/nodes/trails/PresBox.tsx          | 32 +++++++++++-----------
 src/client/views/nodes/trails/PresElementBox.tsx   |  3 +-
 3 files changed, 17 insertions(+), 19 deletions(-)

(limited to 'src/client/views/nodes/trails/PresElementBox.tsx')

diff --git a/src/client/views/nodes/trails/CubicBezierEditor.tsx b/src/client/views/nodes/trails/CubicBezierEditor.tsx
index db3eb2e26..e12fca20c 100644
--- a/src/client/views/nodes/trails/CubicBezierEditor.tsx
+++ b/src/client/views/nodes/trails/CubicBezierEditor.tsx
@@ -8,7 +8,6 @@ type Props = {
 };
 
 const ANIMATION_DURATION = 750;
-// const ANIMATION_TIMING_FUNC = "cubic-bezier(.42,.97,.52,1.49)";
 const ANIMATION_TIMING_FUNC = 'cubic-bezier(0.3, .2, .2, 1.4)';
 const CONTAINER_WIDTH = 200;
 const EDITOR_WIDTH = 100;
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 248de5f4b..4890c115b 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -1848,19 +1848,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     </div>
                     {[DocumentType.AUDIO, DocumentType.VID].includes(targetType as any as DocumentType) ? null : (
                         <>
-                            <div className="ribbon-doubleButton presBox-ribbon">
+                            <div className="ribbon-doubleButton">
                                 <div className="presBox-subheading">Slide Duration</div>
                                 <div className="ribbon-property" style={{ border: `solid 1px ${SettingsManager.userColor}` }}>
                                     <input className="presBox-input" type="number" readOnly={true} value={duration} onKeyDown={e => e.stopPropagation()} onChange={e => this.updateDurationTime(e.target.value)} /> s
                                 </div>
-                                <div className="ribbon-propertyUpDown" style={{ color: SettingsManager.userBackgroundColor, background: SettingsManager.userColor }}>
+                                {/* <div className="ribbon-propertyUpDown" style={{ color: SettingsManager.userBackgroundColor, background: SettingsManager.userColor }}>
                                     <div className="ribbon-propertyUpDownItem" onClick={() => this.updateDurationTime(String(duration), 1000)}>
                                         <FontAwesomeIcon icon={'caret-up'} />
                                     </div>
                                     <div className="ribbon-propertyUpDownItem" onClick={() => this.updateDurationTime(String(duration), -1000)}>
                                         <FontAwesomeIcon icon={'caret-down'} />
                                     </div>
-                                </div>
+                                </div> */}
                             </div>
                             {PresBox.inputter('0.1', '0.1', '20', duration, targetType !== DocumentType.AUDIO, this.updateDurationTime)}
                             <div className={'slider-headers'} style={{ display: targetType === DocumentType.AUDIO ? 'none' : 'grid' }}>
@@ -2068,7 +2068,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             />
                         </div>
                     </div>
-
                     {/* Movement */}
                     <div
                         className={`presBox-ribbon ${this._transitionTools && this.layoutDoc.presentation_status === PresStatus.Edit ? 'active' : ''}`}
@@ -2097,7 +2096,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             <div className="ribbon-doubleButton" style={{ display: activeItem.presentation_movement === PresMovement.Zoom ? 'inline-flex' : 'none' }}>
                                 <div className="presBox-subheading">Zoom (% screen filled)</div>
                                 <div className="ribbon-property" style={{ border: `solid 1px ${SettingsManager.userColor}` }}>
-                                    <input className="presBox-input" type="number" readOnly={true} value={zoom} onChange={e => this.updateZoom(e.target.value)} />%
+                                    <input className="presBox-input" readOnly={true} type="number" value={zoom} onChange={e => this.updateZoom(e.target.value)} />%
                                 </div>
                                 {/* <div className="ribbon-propertyUpDown" style={{ color: SettingsManager.userBackgroundColor, background: SettingsManager.userColor }}>
                                     <div className="ribbon-propertyUpDownItem" onClick={() => this.updateZoom(String(zoom), 0.1)}>
@@ -2161,26 +2160,28 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                     {`${this.showBezierEditor ? 'Hide' : 'Show'} Timing Editor`}
                                     <FontAwesomeIcon icon={this.showBezierEditor ? 'chevron-up' : 'chevron-down'} />
                                 </div>
-                                {this.showBezierEditor && (
-                                    <>
-                                        <p className="presBox-submenu-label" style={{ alignSelf: 'flex-start' }}>
-                                            Custom Timing Function
-                                        </p>
-                                        <CubicBezierEditor setFunc={this.setBezierControlPoints} currPoints={this.currCPoints} easeFunc={StrCast(this.activeItem.presEaseFunc)} />
-                                    </>
-                                )}
                             </div>
                         </div>
                     </div>
-
+                    <div className="presBox-option-block">
+                        {this.showBezierEditor && (
+                            <>
+                                <p className="presBox-submenu-label" style={{ alignSelf: 'flex-start' }}>
+                                    Custom Timing Function
+                                </p>
+                                <CubicBezierEditor setFunc={this.setBezierControlPoints} currPoints={this.currCPoints} easeFunc={StrCast(this.activeItem.presEaseFunc)} />
+                            </>
+                        )}
+                    </div>
                     {/* This chatbox is for getting slide effect transition suggestions from gpt and visualizing them */}
                     <div className="presBox-gpt-chat">
+                        Effects
                         <div className="pres-chat">
                             <div className="pres-chatbox-container">
                                 <ReactTextareaAutosize
                                     ref={r => (this._inputref2 = r)}
                                     minRows={1}
-                                    placeholder="Get transition suggestions..."
+                                    placeholder="Get effect suggestions..."
                                     className="pres-chatbox"
                                     autoFocus={true}
                                     value={this.animationChat}
@@ -2217,7 +2218,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             this._openBulletEffectDropdown = false;
                         })}>
                         <div className="presBox-option-block" style={{ padding: '16px' }}>
-                            Effects
                             <div className="presBox-option-block presBox-option-center">
                                 {/* Chat for idea generation */}
 
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 449b8e07f..268b1c803 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -517,10 +517,9 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 <div
                     className={'slideButton'}
                     onClick={() => {
-                        PresBox.Instance.navigateToActiveItem();
+                        this.presBoxView?.regularSelect(this.slideDoc, this._itemRef.current!, this._dragRef.current!, true, false);
                         PresBox.Instance.openProperties();
                         PresBox.Instance.slideToModify = this.Document;
-                        // PresBox.Instance.recordDictation();
                     }}>
                     <FontAwesomeIcon icon={'edit'} onPointerDown={e => e.stopPropagation()} />
                 </div>
-- 
cgit v1.2.3-70-g09d2


From 0201c8b4d52932760d87686424cdc0a4d39e60bd Mon Sep 17 00:00:00 2001
From: Sophie Zhang <sophie_zhang@brown.edu>
Date: Tue, 14 May 2024 13:37:58 -0400
Subject: cleaning, bug fixing

---
 src/client/views/PropertiesView.tsx                |   3 +
 .../views/nodes/trails/CubicBezierEditor.tsx       |  95 ++---
 src/client/views/nodes/trails/PresBox.scss         |  10 +-
 src/client/views/nodes/trails/PresBox.tsx          | 466 ++++++++++-----------
 src/client/views/nodes/trails/PresElementBox.tsx   |   3 +-
 src/client/views/nodes/trails/SlideEffect.scss     |   5 -
 src/client/views/nodes/trails/SlideEffect.tsx      |   5 +-
 .../views/nodes/trails/SlideEffectPreview.tsx      |  94 -----
 8 files changed, 273 insertions(+), 408 deletions(-)
 delete mode 100644 src/client/views/nodes/trails/SlideEffectPreview.tsx

(limited to 'src/client/views/nodes/trails/PresElementBox.tsx')

diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 15974b9a7..a95477749 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -1675,6 +1675,9 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
                     <div className="propertiesView" style={{ width: this._props.width }}>
                         <div className="propertiesView-sectionTitle" style={{ width: this._props.width }}>
                             Presentation
+                            <div className="propertiesView-info" onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/features/trails/')}>
+                                <IconButton icon={<FontAwesomeIcon icon="info-circle" />} color={SettingsManager.userColor} />
+                            </div>
                         </div>
                         <div className="propertiesView-name" style={{ borderBottom: 0 }}>
                             {this.editableTitle}
diff --git a/src/client/views/nodes/trails/CubicBezierEditor.tsx b/src/client/views/nodes/trails/CubicBezierEditor.tsx
index e12fca20c..aba777e55 100644
--- a/src/client/views/nodes/trails/CubicBezierEditor.tsx
+++ b/src/client/views/nodes/trails/CubicBezierEditor.tsx
@@ -1,5 +1,4 @@
 import React, { useEffect, useState } from 'react';
-import { StrCast } from '../../../../fields/Types';
 
 type Props = {
     setFunc: (newPoints: { p1: number[]; p2: number[] }) => void;
@@ -23,15 +22,14 @@ export const TIMING_DEFAULT_MAPPINGS = {
 
 const CubicBezierEditor = ({ setFunc, currPoints, easeFunc }: Props) => {
     const [animating, setAnimating] = useState(false);
-    // Should let parent control state
-    const [cPoints, setCPoints] = useState(
-        currPoints
-            ? currPoints
-            : {
-                  p1: [0.25, 0.1],
-                  p2: [0.25, 1.0],
-              }
-    );
+    // const [cPoints, setCPoints] = useState(
+    //     currPoints
+    //         ? currPoints
+    //         : {
+    //               p1: [0.25, 0.1],
+    //               p2: [0.25, 1.0],
+    //           }
+    // );
     const [c1Down, setC1Down] = useState(false);
     const [c2Down, setC2Down] = useState(false);
 
@@ -73,11 +71,11 @@ const CubicBezierEditor = ({ setFunc, currPoints, easeFunc }: Props) => {
         };
     };
 
-    useEffect(() => {
-        if (!easeFunc.startsWith('cubic')) {
-            setCPoints(convertToPoints(easeFunc));
-        }
-    }, [easeFunc]);
+    // useEffect(() => {
+    //     if (!easeFunc.startsWith('cubic')) {
+    //         setFunc(convertToPoints(easeFunc));
+    //     }
+    // }, [easeFunc]);
 
     useEffect(() => {
         if (animating) {
@@ -93,25 +91,25 @@ const CubicBezierEditor = ({ setFunc, currPoints, easeFunc }: Props) => {
             setC1Down(false);
         });
         const handlePointerMove = (e: PointerEvent) => {
-            const newX = cPoints.p1[0] + e.movementX / EDITOR_WIDTH;
+            const newX = currPoints.p1[0] + e.movementX / EDITOR_WIDTH;
             if (newX < 0 || newX > 1) {
                 return;
             }
 
-            setCPoints(prev => ({
-                ...prev,
-                p1: [roundToHundredth(prev.p1[0] + e.movementX / EDITOR_WIDTH), roundToHundredth(prev.p1[1] - e.movementY / EDITOR_WIDTH)],
-            }));
+            // setCPoints(prev => ({
+            //     ...prev,
+            //     p1: [roundToHundredth(prev.p1[0] + e.movementX / EDITOR_WIDTH), roundToHundredth(prev.p1[1] - e.movementY / EDITOR_WIDTH)],
+            // }));
             setFunc({
-                ...cPoints,
-                p1: [roundToHundredth(cPoints.p1[0] + e.movementX / EDITOR_WIDTH), roundToHundredth(cPoints.p1[1] - e.movementY / EDITOR_WIDTH)],
+                ...currPoints,
+                p1: [roundToHundredth(currPoints.p1[0] + e.movementX / EDITOR_WIDTH), roundToHundredth(currPoints.p1[1] - e.movementY / EDITOR_WIDTH)],
             });
         };
 
         window.addEventListener('pointermove', handlePointerMove);
 
         return () => window.removeEventListener('pointermove', handlePointerMove);
-    }, [c1Down, cPoints]);
+    }, [c1Down, currPoints]);
 
     useEffect(() => {
         if (!c2Down) return;
@@ -119,38 +117,31 @@ const CubicBezierEditor = ({ setFunc, currPoints, easeFunc }: Props) => {
             setC2Down(false);
         });
         const handlePointerMove = (e: PointerEvent) => {
-            const newX = cPoints.p2[0] + e.movementX / EDITOR_WIDTH;
+            const newX = currPoints.p2[0] + e.movementX / EDITOR_WIDTH;
             if (newX < 0 || newX > 1) {
                 return;
             }
 
-            setCPoints(prev => ({
-                ...prev,
-                p2: [roundToHundredth(prev.p2[0] + e.movementX / EDITOR_WIDTH), roundToHundredth(prev.p2[1] - e.movementY / EDITOR_WIDTH)],
-            }));
+            // setCPoints(prev => ({
+            //     ...prev,
+            //     p2: [roundToHundredth(prev.p2[0] + e.movementX / EDITOR_WIDTH), roundToHundredth(prev.p2[1] - e.movementY / EDITOR_WIDTH)],
+            // }));
             setFunc({
-                ...cPoints,
-                p2: [roundToHundredth(cPoints.p2[0] + e.movementX / EDITOR_WIDTH), roundToHundredth(cPoints.p2[1] - e.movementY / EDITOR_WIDTH)],
+                ...currPoints,
+                p2: [roundToHundredth(currPoints.p2[0] + e.movementX / EDITOR_WIDTH), roundToHundredth(currPoints.p2[1] - e.movementY / EDITOR_WIDTH)],
             });
         };
 
         window.addEventListener('pointermove', handlePointerMove);
 
         return () => window.removeEventListener('pointermove', handlePointerMove);
-    }, [c2Down, cPoints]);
+    }, [c2Down, currPoints]);
 
     return (
         <div
-            // onPointerDown={e => {
-            //     e.stopPropagation;
-            // }}
             onPointerMove={e => {
                 e.stopPropagation;
-            }}
-            // onPointerUp={e => {
-            //     e.stopPropagation;
-            // }}
-        >
+            }}>
             <svg className="presBox-bezier-editor" width={`${CONTAINER_WIDTH}`} height={`${CONTAINER_WIDTH}`} xmlns="http://www.w3.org/2000/svg">
                 {/* Outlines */}
                 <line x1={`${0 + OFFSET}`} y1={`${EDITOR_WIDTH + OFFSET}`} x2={`${EDITOR_WIDTH + OFFSET}`} y2={`${0 + OFFSET}`} stroke="#c1c1c1" strokeWidth="1" />
@@ -158,9 +149,9 @@ const CubicBezierEditor = ({ setFunc, currPoints, easeFunc }: Props) => {
                 <rect x={`${0 + OFFSET}`} y={`${0 + OFFSET}`} width={EDITOR_WIDTH} height={EDITOR_WIDTH} stroke="#c5c5c5" fill="transparent" strokeWidth="1" />
                 {/* Editor */}
                 <path
-                    d={`M ${0 + OFFSET} ${EDITOR_WIDTH + OFFSET} C ${cPoints.p1[0] * EDITOR_WIDTH + OFFSET} ${EDITOR_WIDTH - cPoints.p1[1] * EDITOR_WIDTH + OFFSET}, ${
-                        cPoints.p2[0] * EDITOR_WIDTH + OFFSET
-                    } ${EDITOR_WIDTH - cPoints.p2[1] * EDITOR_WIDTH + OFFSET}, ${EDITOR_WIDTH + OFFSET} ${0 + OFFSET}`}
+                    d={`M ${0 + OFFSET} ${EDITOR_WIDTH + OFFSET} C ${currPoints.p1[0] * EDITOR_WIDTH + OFFSET} ${EDITOR_WIDTH - currPoints.p1[1] * EDITOR_WIDTH + OFFSET}, ${
+                        currPoints.p2[0] * EDITOR_WIDTH + OFFSET
+                    } ${EDITOR_WIDTH - currPoints.p2[1] * EDITOR_WIDTH + OFFSET}, ${EDITOR_WIDTH + OFFSET} ${0 + OFFSET}`}
                     stroke="#ffffff"
                     fill="transparent"
                 />
@@ -174,15 +165,15 @@ const CubicBezierEditor = ({ setFunc, currPoints, easeFunc }: Props) => {
                     }}
                     x1={`${0 + OFFSET}`}
                     y1={`${EDITOR_WIDTH + OFFSET}`}
-                    x2={`${cPoints.p1[0] * EDITOR_WIDTH + OFFSET}`}
-                    y2={`${EDITOR_WIDTH - cPoints.p1[1] * EDITOR_WIDTH + OFFSET}`}
+                    x2={`${currPoints.p1[0] * EDITOR_WIDTH + OFFSET}`}
+                    y2={`${EDITOR_WIDTH - currPoints.p1[1] * EDITOR_WIDTH + OFFSET}`}
                     stroke="#00000000"
                     strokeWidth="5"
                 />
-                <line x1={`${0 + OFFSET}`} y1={`${EDITOR_WIDTH + OFFSET}`} x2={`${cPoints.p1[0] * EDITOR_WIDTH + OFFSET}`} y2={`${EDITOR_WIDTH - cPoints.p1[1] * EDITOR_WIDTH + OFFSET}`} stroke="#ffffff" strokeWidth="1" />
+                <line x1={`${0 + OFFSET}`} y1={`${EDITOR_WIDTH + OFFSET}`} x2={`${currPoints.p1[0] * EDITOR_WIDTH + OFFSET}`} y2={`${EDITOR_WIDTH - currPoints.p1[1] * EDITOR_WIDTH + OFFSET}`} stroke="#ffffff" strokeWidth="1" />
                 <circle
-                    cx={`${cPoints.p1[0] * EDITOR_WIDTH + OFFSET}`}
-                    cy={`${EDITOR_WIDTH - cPoints.p1[1] * EDITOR_WIDTH + OFFSET}`}
+                    cx={`${currPoints.p1[0] * EDITOR_WIDTH + OFFSET}`}
+                    cy={`${EDITOR_WIDTH - currPoints.p1[1] * EDITOR_WIDTH + OFFSET}`}
                     r="5"
                     fill={`${c1Down ? '#3fa9ff' : '#ffffff'}`}
                     onPointerDown={e => {
@@ -204,15 +195,15 @@ const CubicBezierEditor = ({ setFunc, currPoints, easeFunc }: Props) => {
                     }}
                     x1={`${EDITOR_WIDTH + OFFSET}`}
                     y1={`${0 + OFFSET}`}
-                    x2={`${cPoints.p2[0] * EDITOR_WIDTH + OFFSET}`}
-                    y2={`${EDITOR_WIDTH - cPoints.p2[1] * EDITOR_WIDTH + OFFSET}`}
+                    x2={`${currPoints.p2[0] * EDITOR_WIDTH + OFFSET}`}
+                    y2={`${EDITOR_WIDTH - currPoints.p2[1] * EDITOR_WIDTH + OFFSET}`}
                     stroke="#00000000"
                     strokeWidth="5"
                 />
-                <line x1={`${EDITOR_WIDTH + OFFSET}`} y1={`${0 + OFFSET}`} x2={`${cPoints.p2[0] * EDITOR_WIDTH + OFFSET}`} y2={`${EDITOR_WIDTH - cPoints.p2[1] * EDITOR_WIDTH + OFFSET}`} stroke="#ffffff" strokeWidth="1" />
+                <line x1={`${EDITOR_WIDTH + OFFSET}`} y1={`${0 + OFFSET}`} x2={`${currPoints.p2[0] * EDITOR_WIDTH + OFFSET}`} y2={`${EDITOR_WIDTH - currPoints.p2[1] * EDITOR_WIDTH + OFFSET}`} stroke="#ffffff" strokeWidth="1" />
                 <circle
-                    cx={`${cPoints.p2[0] * EDITOR_WIDTH + OFFSET}`}
-                    cy={`${EDITOR_WIDTH - cPoints.p2[1] * EDITOR_WIDTH + OFFSET}`}
+                    cx={`${currPoints.p2[0] * EDITOR_WIDTH + OFFSET}`}
+                    cy={`${EDITOR_WIDTH - currPoints.p2[1] * EDITOR_WIDTH + OFFSET}`}
                     r="5"
                     fill={`${c2Down ? '#3fa9ff' : '#ffffff'}`}
                     onPointerDown={e => {
diff --git a/src/client/views/nodes/trails/PresBox.scss b/src/client/views/nodes/trails/PresBox.scss
index 58c9b0b42..60d4e580d 100644
--- a/src/client/views/nodes/trails/PresBox.scss
+++ b/src/client/views/nodes/trails/PresBox.scss
@@ -1,7 +1,7 @@
 @import '../../global/globalCssVariables.module.scss';
 
 .presBox-gpt-chat {
-    padding: 8px;
+    padding: 16px;
     display: flex;
     flex-direction: column;
     gap: 1rem;
@@ -53,13 +53,11 @@
 .presBox-effect-container {
     cursor: pointer;
     overflow: hidden;
-    // position: relative;
     width: 80px;
     height: 80px;
     display: flex;
     justify-content: center;
     align-items: center;
-    /* background-color: #1f2028; */
     border: 1px solid rgb(118, 118, 118);
     border-radius: 8px;
 }
@@ -67,13 +65,9 @@
 .presBox-effect-demo-box {
     width: 40px;
     height: 40px;
-    // position: absolute;
-    // top: 20px;
-    // left: 20px;
     border-radius: 4px;
     // default bg
     background-color: rgb(37, 161, 255);
-    // transform-origin: center;
 }
 
 // Bezier editor
@@ -95,7 +89,7 @@
     display: flex;
     flex-direction: column;
     gap: 1rem;
-    // padding: 8px;
+    padding: 16px;
 }
 
 .presBox-option-center {
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 4890c115b..02233d241 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -47,9 +47,9 @@ import { DictationManager } from '../../../util/DictationManager';
 import CubicBezierEditor, { TIMING_DEFAULT_MAPPINGS } from './CubicBezierEditor';
 import Slider from '@mui/material/Slider';
 import { FaArrowDown, FaArrowLeft, FaArrowRight, FaArrowUp, FaCompressArrowsAlt } from 'react-icons/fa';
-import SpringAnimationPreview from './SlideEffectPreview';
 import { effectTimings, SpringType, springMappings, effectItems, easeItems, movementItems, SpringSettings, presEffectDefaultTimings, AnimationSettings, springPreviewColors } from './SpringUtils';
 import SlideEffect from './SlideEffect';
+import { IoMdInformationCircleOutline } from 'react-icons/io';
 
 export interface pinDataTypes {
     scrollable?: boolean;
@@ -387,24 +387,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     setDictationContent = (value: string) => {
         console.log('Dictation value', value);
         this.setChatInput(value);
-        // // Get the current cursor position
-        // if (!this._inputref) return;
-        // const cursorPosition = this._inputref.selectionStart;
-        // const currentValue = this.chatInput;
-
-        // // split before and after
-        // const textBeforeCursor = currentValue.slice(0, cursorPosition);
-        // const textAfterCursor = currentValue.slice(cursorPosition);
-
-        // // insertion
-        // const updatedText = textBeforeCursor + value + textAfterCursor;
-
-        // // Update the textarea value
-        // this.setChatInput(updatedText);
-
-        // // set new cursor pos
-        // const newCursorPosition = cursorPosition + value.length;
-        // this._inputref.setSelectionRange(newCursorPosition, newCursorPosition);
     };
 
     @action
@@ -415,7 +397,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
         try {
             const res = await getSlideTransitionSuggestions(this.animationChat);
-            console.log('GPT Result:', res);
             if (typeof res === 'string') {
                 const resObj = JSON.parse(res);
                 console.log('Parsed GPT Result ', resObj);
@@ -448,11 +429,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 }
             }
         }
-        console.log('current slide props', currSlideProperties);
+        console.log('current slide props ', currSlideProperties);
 
         try {
             const res = await gptTrailSlideCustomization(input, currSlideProperties);
-            console.log('GPT Result:', res);
             if (typeof res === 'string') {
                 const resObj = JSON.parse(res);
                 console.log('Parsed GPT Result ', resObj);
@@ -1798,46 +1778,51 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             let duration = activeItem.presentation_duration ? NumCast(activeItem.presentation_duration) / 1000 : 0;
             if (activeItem.type === DocumentType.AUDIO) duration = NumCast(activeItem.duration);
             return (
-                <div className="presBox-ribbon">
-                    <div className="presBox-toggles">
-                        <Tooltip title={<div className="dash-tooltip">Hide before presented</div>}>
-                            <div
-                                className={`ribbon-toggle ${activeItem.presentation_hideBefore ? 'active' : ''}`}
-                                style={{ border: `solid 1px ${SettingsManager.userColor}`, color: SettingsManager.userColor, background: activeItem.presentation_hideBefore ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor }}
-                                onClick={() => this.updateHideBefore(activeItem)}>
-                                Hide before
-                            </div>
-                        </Tooltip>
-                        <Tooltip title={<div className="dash-tooltip">{'Hide while presented'}</div>}>
-                            <div
-                                className={`ribbon-toggle ${activeItem.presentation_hide ? 'active' : ''}`}
-                                style={{ border: `solid 1px ${SettingsManager.userColor}`, color: SettingsManager.userColor, background: activeItem.presentation_hide ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor }}
-                                onClick={() => this.updateHide(activeItem)}>
-                                Hide
-                            </div>
-                        </Tooltip>
-                        <Tooltip title={<div className="dash-tooltip">{'Hide after presented'}</div>}>
-                            <div
-                                className={`ribbon-toggle ${activeItem.presentation_hideAfter ? 'active' : ''}`}
-                                style={{ border: `solid 1px ${SettingsManager.userColor}`, color: SettingsManager.userColor, background: activeItem.presentation_hideAfter ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor }}
-                                onClick={() => this.updateHideAfter(activeItem)}>
-                                Hide after
-                            </div>
-                        </Tooltip>
+                <div className="presBox-option-block">
+                    <div className="presBox-ribbon">
+                        <div className="presBox-toggles">
+                            <Tooltip title={<div className="dash-tooltip">Hide before presented</div>}>
+                                <div
+                                    className={`ribbon-toggle ${activeItem.presentation_hideBefore ? 'active' : ''}`}
+                                    style={{
+                                        border: `solid 1px ${SettingsManager.userColor}`,
+                                        color: SettingsManager.userColor,
+                                        background: activeItem.presentation_hideBefore ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor,
+                                    }}
+                                    onClick={() => this.updateHideBefore(activeItem)}>
+                                    Hide before
+                                </div>
+                            </Tooltip>
+                            <Tooltip title={<div className="dash-tooltip">{'Hide while presented'}</div>}>
+                                <div
+                                    className={`ribbon-toggle ${activeItem.presentation_hide ? 'active' : ''}`}
+                                    style={{ border: `solid 1px ${SettingsManager.userColor}`, color: SettingsManager.userColor, background: activeItem.presentation_hide ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor }}
+                                    onClick={() => this.updateHide(activeItem)}>
+                                    Hide
+                                </div>
+                            </Tooltip>
+                            <Tooltip title={<div className="dash-tooltip">{'Hide after presented'}</div>}>
+                                <div
+                                    className={`ribbon-toggle ${activeItem.presentation_hideAfter ? 'active' : ''}`}
+                                    style={{ border: `solid 1px ${SettingsManager.userColor}`, color: SettingsManager.userColor, background: activeItem.presentation_hideAfter ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor }}
+                                    onClick={() => this.updateHideAfter(activeItem)}>
+                                    Hide after
+                                </div>
+                            </Tooltip>
 
-                        <Tooltip title={<div className="dash-tooltip">{'Open in lightbox view'}</div>}>
-                            <div
-                                className="ribbon-toggle"
-                                style={{
-                                    border: `solid 1px ${SettingsManager.userColor}`,
-                                    color: SettingsManager.userColor,
-                                    background: activeItem.presentation_openInLightbox ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor,
-                                }}
-                                onClick={() => this.updateOpenDoc(activeItem)}>
-                                Lightbox
-                            </div>
-                        </Tooltip>
-                        {/* <Tooltip title={<div className="dash-tooltip">Transition movement style</div>}>
+                            <Tooltip title={<div className="dash-tooltip">{'Open in lightbox view'}</div>}>
+                                <div
+                                    className="ribbon-toggle"
+                                    style={{
+                                        border: `solid 1px ${SettingsManager.userColor}`,
+                                        color: SettingsManager.userColor,
+                                        background: activeItem.presentation_openInLightbox ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor,
+                                    }}
+                                    onClick={() => this.updateOpenDoc(activeItem)}>
+                                    Lightbox
+                                </div>
+                            </Tooltip>
+                            {/* <Tooltip title={<div className="dash-tooltip">Transition movement style</div>}>
                             <div
                                 className="ribbon-toggle"
                                 style={{ border: `solid 1px ${SettingsManager.userColor}`, color: SettingsManager.userColor, background: activeItem.presEaseFunc === 'ease' ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor }}
@@ -1845,15 +1830,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 {`${StrCast(activeItem.presEaseFunc, 'ease')}`}
                             </div>
                         </Tooltip> */}
-                    </div>
-                    {[DocumentType.AUDIO, DocumentType.VID].includes(targetType as any as DocumentType) ? null : (
-                        <>
-                            <div className="ribbon-doubleButton">
-                                <div className="presBox-subheading">Slide Duration</div>
-                                <div className="ribbon-property" style={{ border: `solid 1px ${SettingsManager.userColor}` }}>
-                                    <input className="presBox-input" type="number" readOnly={true} value={duration} onKeyDown={e => e.stopPropagation()} onChange={e => this.updateDurationTime(e.target.value)} /> s
-                                </div>
-                                {/* <div className="ribbon-propertyUpDown" style={{ color: SettingsManager.userBackgroundColor, background: SettingsManager.userColor }}>
+                        </div>
+                        {[DocumentType.AUDIO, DocumentType.VID].includes(targetType as any as DocumentType) ? null : (
+                            <>
+                                <div className="ribbon-doubleButton">
+                                    <div className="presBox-subheading">Slide Duration</div>
+                                    <div className="ribbon-property" style={{ border: `solid 1px ${SettingsManager.userColor}` }}>
+                                        <input className="presBox-input" type="number" readOnly={true} value={duration} onKeyDown={e => e.stopPropagation()} onChange={e => this.updateDurationTime(e.target.value)} /> s
+                                    </div>
+                                    {/* <div className="ribbon-propertyUpDown" style={{ color: SettingsManager.userBackgroundColor, background: SettingsManager.userColor }}>
                                     <div className="ribbon-propertyUpDownItem" onClick={() => this.updateDurationTime(String(duration), 1000)}>
                                         <FontAwesomeIcon icon={'caret-up'} />
                                     </div>
@@ -1861,15 +1846,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                         <FontAwesomeIcon icon={'caret-down'} />
                                     </div>
                                 </div> */}
-                            </div>
-                            {PresBox.inputter('0.1', '0.1', '20', duration, targetType !== DocumentType.AUDIO, this.updateDurationTime)}
-                            <div className={'slider-headers'} style={{ display: targetType === DocumentType.AUDIO ? 'none' : 'grid' }}>
-                                <div className="slider-text">Short</div>
-                                <div className="slider-text">Medium</div>
-                                <div className="slider-text">Long</div>
-                            </div>
-                        </>
-                    )}
+                                </div>
+                                {PresBox.inputter('0.1', '0.1', '20', duration, targetType !== DocumentType.AUDIO, this.updateDurationTime)}
+                                <div className={'slider-headers'} style={{ display: targetType === DocumentType.AUDIO ? 'none' : 'grid' }}>
+                                    <div className="slider-text">Short</div>
+                                    <div className="slider-text">Medium</div>
+                                    <div className="slider-text">Long</div>
+                                </div>
+                            </>
+                        )}
+                    </div>
                 </div>
             );
         }
@@ -1887,74 +1873,76 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 </div>
             );
             return (
-                <div className="presBox-ribbon">
-                    <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
-                        <div className="presBox-subheading">Progressivize Collection</div>
-                        <input
-                            className="presBox-checkbox"
-                            style={{ margin: 10, border: `solid 1px ${SettingsManager.userColor}` }}
-                            type="checkbox"
-                            onChange={() => {
-                                activeItem.presentation_indexed = activeItem.presentation_indexed === undefined ? 0 : undefined;
-                                activeItem.presentation_hideBefore = activeItem.presentation_indexed !== undefined;
-                                const tagDoc = PresBox.targetRenderedDoc(this.activeItem);
-                                const type = DocCast(tagDoc?.annotationOn)?.type ?? tagDoc.type;
-                                activeItem.presentation_indexedStart = type === DocumentType.COL ? 1 : 0;
-                                // a progressivized slide doesn't have sub-slides, but rather iterates over the data list of the target being progressivized.
-                                // to avoid creating a new slide to correspond to each of the target's data list, we create a computedField to refernce the target's data list.
-                                let dataField = Doc.LayoutFieldKey(tagDoc);
-                                if (Cast(tagDoc[dataField], listSpec(Doc), null)?.filter(d => d instanceof Doc) === undefined) dataField = dataField + '_annotations';
-
-                                if (DocCast(activeItem.presentation_targetDoc).annotationOn) activeItem.data = ComputedField.MakeFunction(`this.presentation_targetDoc.annotationOn?.["${dataField}"]`);
-                                else activeItem.data = ComputedField.MakeFunction(`this.presentation_targetDoc?.["${dataField}"]`);
-                            }}
-                            checked={Cast(activeItem.presentation_indexed, 'number', null) !== undefined ? true : false}
-                        />
-                    </div>
-                    <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
-                        <div className="presBox-subheading">Progressivize First Bullet</div>
-                        <input
-                            className="presBox-checkbox"
-                            style={{ margin: 10, border: `solid 1px ${SettingsManager.userColor}` }}
-                            type="checkbox"
-                            onChange={() => (activeItem.presentation_indexedStart = activeItem.presentation_indexedStart ? 0 : 1)}
-                            checked={!NumCast(activeItem.presentation_indexedStart)}
-                        />
-                    </div>
-                    <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
-                        <div className="presBox-subheading">Expand Current Bullet</div>
-                        <input
-                            className="presBox-checkbox"
-                            style={{ margin: 10, border: `solid 1px ${SettingsManager.userColor}` }}
-                            type="checkbox"
-                            onChange={() => (activeItem.presBulletExpand = !activeItem.presBulletExpand)}
-                            checked={BoolCast(activeItem.presBulletExpand)}
-                        />
-                    </div>
+                <div className="presBox-option-block">
+                    <div className="presBox-ribbon">
+                        <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
+                            <div className="presBox-subheading">Progressivize Collection</div>
+                            <input
+                                className="presBox-checkbox"
+                                style={{ margin: 10, border: `solid 1px ${SettingsManager.userColor}` }}
+                                type="checkbox"
+                                onChange={() => {
+                                    activeItem.presentation_indexed = activeItem.presentation_indexed === undefined ? 0 : undefined;
+                                    activeItem.presentation_hideBefore = activeItem.presentation_indexed !== undefined;
+                                    const tagDoc = PresBox.targetRenderedDoc(this.activeItem);
+                                    const type = DocCast(tagDoc?.annotationOn)?.type ?? tagDoc.type;
+                                    activeItem.presentation_indexedStart = type === DocumentType.COL ? 1 : 0;
+                                    // a progressivized slide doesn't have sub-slides, but rather iterates over the data list of the target being progressivized.
+                                    // to avoid creating a new slide to correspond to each of the target's data list, we create a computedField to refernce the target's data list.
+                                    let dataField = Doc.LayoutFieldKey(tagDoc);
+                                    if (Cast(tagDoc[dataField], listSpec(Doc), null)?.filter(d => d instanceof Doc) === undefined) dataField = dataField + '_annotations';
+
+                                    if (DocCast(activeItem.presentation_targetDoc).annotationOn) activeItem.data = ComputedField.MakeFunction(`this.presentation_targetDoc.annotationOn?.["${dataField}"]`);
+                                    else activeItem.data = ComputedField.MakeFunction(`this.presentation_targetDoc?.["${dataField}"]`);
+                                }}
+                                checked={Cast(activeItem.presentation_indexed, 'number', null) !== undefined ? true : false}
+                            />
+                        </div>
+                        <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
+                            <div className="presBox-subheading">Progressivize First Bullet</div>
+                            <input
+                                className="presBox-checkbox"
+                                style={{ margin: 10, border: `solid 1px ${SettingsManager.userColor}` }}
+                                type="checkbox"
+                                onChange={() => (activeItem.presentation_indexedStart = activeItem.presentation_indexedStart ? 0 : 1)}
+                                checked={!NumCast(activeItem.presentation_indexedStart)}
+                            />
+                        </div>
+                        <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
+                            <div className="presBox-subheading">Expand Current Bullet</div>
+                            <input
+                                className="presBox-checkbox"
+                                style={{ margin: 10, border: `solid 1px ${SettingsManager.userColor}` }}
+                                type="checkbox"
+                                onChange={() => (activeItem.presBulletExpand = !activeItem.presBulletExpand)}
+                                checked={BoolCast(activeItem.presBulletExpand)}
+                            />
+                        </div>
 
-                    <div className="ribbon-box">
-                        Bullet Effect
-                        <div
-                            className="presBox-dropdown"
-                            onClick={action(e => {
-                                e.stopPropagation();
-                                this._openBulletEffectDropdown = !this._openBulletEffectDropdown;
-                            })}
-                            style={{
-                                color: SettingsManager.userColor,
-                                background: SettingsManager.userVariantColor,
-                                borderBottomLeftRadius: this._openBulletEffectDropdown ? 0 : 5,
-                                border: this._openBulletEffectDropdown ? `solid 2px ${SettingsManager.userVariantColor}` : `solid 1px ${SettingsManager.userColor}`,
-                            }}>
-                            {effect?.toString()}
-                            <FontAwesomeIcon className="presBox-dropdownIcon" style={{ gridColumn: 2, color: this._openBulletEffectDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={'angle-down'} />
+                        <div className="ribbon-box">
+                            Bullet Effect
                             <div
-                                className={'presBox-dropdownOptions'}
-                                style={{ display: this._openBulletEffectDropdown ? 'grid' : 'none', color: SettingsManager.userColor, background: SettingsManager.userBackgroundColor }}
-                                onPointerDown={e => e.stopPropagation()}>
-                                {Object.values(PresEffect)
-                                    .filter(v => isNaN(Number(v)))
-                                    .map(effect => bulletEffect(effect))}
+                                className="presBox-dropdown"
+                                onClick={action(e => {
+                                    e.stopPropagation();
+                                    this._openBulletEffectDropdown = !this._openBulletEffectDropdown;
+                                })}
+                                style={{
+                                    color: SettingsManager.userColor,
+                                    background: SettingsManager.userVariantColor,
+                                    borderBottomLeftRadius: this._openBulletEffectDropdown ? 0 : 5,
+                                    border: this._openBulletEffectDropdown ? `solid 2px ${SettingsManager.userVariantColor}` : `solid 1px ${SettingsManager.userColor}`,
+                                }}>
+                                {effect?.toString()}
+                                <FontAwesomeIcon className="presBox-dropdownIcon" style={{ gridColumn: 2, color: this._openBulletEffectDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={'angle-down'} />
+                                <div
+                                    className={'presBox-dropdownOptions'}
+                                    style={{ display: this._openBulletEffectDropdown ? 'grid' : 'none', color: SettingsManager.userColor, background: SettingsManager.userBackgroundColor }}
+                                    onPointerDown={e => e.stopPropagation()}>
+                                    {Object.values(PresEffect)
+                                        .filter(v => isNaN(Number(v)))
+                                        .map(effect => bulletEffect(effect))}
+                                </div>
                             </div>
                         </div>
                     </div>
@@ -2016,22 +2004,27 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (activeItem && this.targetDoc) {
             const transitionSpeed = activeItem.presentation_transition ? NumCast(activeItem.presentation_transition) / 1000 : 0.5;
             const zoom = NumCast(activeItem.config_zoom, 1) * 100;
-            const effect = activeItem.presentation_effect ? activeItem.presentation_effect : PresEffect.None;
+            const effect = StrCast(activeItem.presentation_effect) ? StrCast(activeItem.presentation_effect) : PresEffect.None;
+            const direction = StrCast(activeItem.presentation_effectDirection);
 
             return (
                 <>
-                    {/* GPT Component */}
-                    {/* This chatbox is for customizing the properties of trails, like transition time, movement type (zoom, pan) */}
+                    {/* This chatbox is for customizing the properties of trails, like transition time, movement type (zoom, pan) using GPT*/}
                     <div className="presBox-gpt-chat">
-                        <p>Customize with GPT</p>
+                        <span style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
+                            Customize Slide Properties{' '}
+                            <div className="propertiesView-info" onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/features/trails/')}>
+                                <IconButton icon={<FontAwesomeIcon icon="info-circle" />} color={SettingsManager.userColor} />
+                            </div>
+                        </span>
                         <div className="pres-chat">
                             <div className="pres-chatbox-container">
                                 <ReactTextareaAutosize
-                                    ref={r => (this._inputref = r)}
-                                    minRows={1}
-                                    placeholder="Customize..."
+                                    // ref={r => (this._inputref = r)}
+                                    // minRows={1}
+                                    placeholder="Describe how you would like to modify the slide properties."
                                     className="pres-chatbox"
-                                    autoFocus={true}
+                                    // autoFocus={true}
                                     value={this.chatInput}
                                     onChange={e => {
                                         this.setChatInput(e.target.value);
@@ -2079,7 +2072,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             this._openEffectDropdown = false;
                             this._openBulletEffectDropdown = false;
                         })}>
-                        <div className="presBox-option-block" style={{ padding: '16px' }}>
+                        <div
+                            className="presBox-option-block"
+                            // style={{ padding: '16px' }}
+                        >
                             Movement
                             <Dropdown
                                 color={StrCast(Doc.UserDoc().userColor)}
@@ -2098,14 +2094,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 <div className="ribbon-property" style={{ border: `solid 1px ${SettingsManager.userColor}` }}>
                                     <input className="presBox-input" readOnly={true} type="number" value={zoom} onChange={e => this.updateZoom(e.target.value)} />%
                                 </div>
-                                {/* <div className="ribbon-propertyUpDown" style={{ color: SettingsManager.userBackgroundColor, background: SettingsManager.userColor }}>
-                                    <div className="ribbon-propertyUpDownItem" onClick={() => this.updateZoom(String(zoom), 0.1)}>
-                                        <FontAwesomeIcon icon={'caret-up'} />
-                                    </div>
-                                    <div className="ribbon-propertyUpDownItem" onClick={() => this.updateZoom(String(zoom), -0.1)}>
-                                        <FontAwesomeIcon icon={'caret-down'} />
-                                    </div>
-                                </div> */}
                             </div>
                             {PresBox.inputter('0', '1', '100', zoom, activeItem.presentation_movement === PresMovement.Zoom, this.updateZoom)}
                             <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
@@ -2113,14 +2101,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 <div className="ribbon-property" style={{ border: `solid 1px ${SettingsManager.userColor}` }}>
                                     <input className="presBox-input" type="number" readOnly={true} value={transitionSpeed} onKeyDown={e => e.stopPropagation()} onChange={action(e => this.updateTransitionTime(e.target.value))} /> s
                                 </div>
-                                {/* <div className="ribbon-propertyUpDown" style={{ color: SettingsManager.userBackgroundColor, background: SettingsManager.userColor }}>
-                                    <div className="ribbon-propertyUpDownItem" onClick={() => this.updateTransitionTime(String(transitionSpeed), 1000)}>
-                                        <FontAwesomeIcon icon={'caret-up'} />
-                                    </div>
-                                    <div className="ribbon-propertyUpDownItem" onClick={() => this.updateTransitionTime(String(transitionSpeed), -1000)}>
-                                        <FontAwesomeIcon icon={'caret-down'} />
-                                    </div>
-                                </div> */}
                             </div>
                             {PresBox.inputter('0.1', '0.1', '10', transitionSpeed, true, this.updateTransitionTime)}
                             <div className={'slider-headers'}>
@@ -2129,61 +2109,59 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 <div className="slider-text">Slow</div>
                             </div>
                             {/* Easing function */}
-                            <div className="presBox-option-block presBox-option-center">
-                                <Dropdown
-                                    color={StrCast(Doc.UserDoc().userColor)}
-                                    formLabel={'Easing Function'}
-                                    closeOnSelect={true}
-                                    items={easeItems}
-                                    selectedVal={this.activeItem.presEaseFunc ? (StrCast(this.activeItem.presEaseFunc).startsWith('cubic') ? 'custom' : StrCast(this.activeItem.presEaseFunc)) : 'ease'}
-                                    setSelectedVal={val => {
-                                        if (typeof val === 'string') {
-                                            if (val !== 'custom') {
-                                                this.setBezierEditorVisibility(true);
-                                                this.setEaseFunc(this.activeItem, val);
-                                            } else {
-                                                this.setEaseFunc(this.activeItem, TIMING_DEFAULT_MAPPINGS.ease);
-                                            }
+                            <Dropdown
+                                color={StrCast(Doc.UserDoc().userColor)}
+                                formLabel={'Easing Function'}
+                                closeOnSelect={true}
+                                items={easeItems}
+                                selectedVal={this.activeItem.presEaseFunc ? (StrCast(this.activeItem.presEaseFunc).startsWith('cubic') ? 'custom' : StrCast(this.activeItem.presEaseFunc)) : 'ease'}
+                                setSelectedVal={val => {
+                                    if (typeof val === 'string') {
+                                        if (val !== 'custom') {
+                                            this.setBezierEditorVisibility(true);
+                                            this.setEaseFunc(this.activeItem, val);
+                                        } else {
+                                            this.setEaseFunc(this.activeItem, TIMING_DEFAULT_MAPPINGS.ease);
                                         }
-                                    }}
-                                    dropdownType={DropdownType.SELECT}
-                                    type={Type.TERT}
-                                />
-                                {/* Custom */}
-                                <div
-                                    className="presBox-show-hide-dropdown"
-                                    style={{ alignSelf: 'flex-start' }}
-                                    onClick={e => {
-                                        e.stopPropagation();
-                                        this.setBezierEditorVisibility(!this.showBezierEditor);
-                                    }}>
-                                    {`${this.showBezierEditor ? 'Hide' : 'Show'} Timing Editor`}
-                                    <FontAwesomeIcon icon={this.showBezierEditor ? 'chevron-up' : 'chevron-down'} />
-                                </div>
+                                    }
+                                }}
+                                dropdownType={DropdownType.SELECT}
+                                type={Type.TERT}
+                            />
+                            {/* Custom */}
+                            <div
+                                className="presBox-show-hide-dropdown"
+                                style={{ alignSelf: 'flex-start' }}
+                                onClick={e => {
+                                    e.stopPropagation();
+                                    this.setBezierEditorVisibility(!this.showBezierEditor);
+                                }}>
+                                {`${this.showBezierEditor ? 'Hide' : 'Show'} Timing Editor`}
+                                <FontAwesomeIcon icon={this.showBezierEditor ? 'chevron-up' : 'chevron-down'} />
                             </div>
                         </div>
                     </div>
-                    <div className="presBox-option-block">
-                        {this.showBezierEditor && (
-                            <>
-                                <p className="presBox-submenu-label" style={{ alignSelf: 'flex-start' }}>
-                                    Custom Timing Function
-                                </p>
-                                <CubicBezierEditor setFunc={this.setBezierControlPoints} currPoints={this.currCPoints} easeFunc={StrCast(this.activeItem.presEaseFunc)} />
-                            </>
-                        )}
-                    </div>
+
+                    {this.showBezierEditor && (
+                        <div className="presBox-option-block" style={{ paddingTop: 0 }}>
+                            <p className="presBox-submenu-label" style={{ alignSelf: 'flex-start' }}>
+                                Custom Timing Function
+                            </p>
+                            <CubicBezierEditor setFunc={this.setBezierControlPoints} currPoints={this.currCPoints} easeFunc={StrCast(this.activeItem.presEaseFunc)} />
+                        </div>
+                    )}
+
                     {/* This chatbox is for getting slide effect transition suggestions from gpt and visualizing them */}
                     <div className="presBox-gpt-chat">
                         Effects
                         <div className="pres-chat">
                             <div className="pres-chatbox-container">
                                 <ReactTextareaAutosize
-                                    ref={r => (this._inputref2 = r)}
-                                    minRows={1}
-                                    placeholder="Get effect suggestions..."
+                                    // ref={r => (this._inputref2 = r)}
+                                    // minRows={1}
+                                    placeholder="Customize prompt for effect suggestions. Leave blank for random results."
                                     className="pres-chatbox"
-                                    autoFocus={true}
+                                    // autoFocus={true}
                                     value={this.animationChat}
                                     onChange={e => {
                                         this.setAnimationChat(e.target.value);
@@ -2217,10 +2195,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             this._openEffectDropdown = false;
                             this._openBulletEffectDropdown = false;
                         })}>
-                        <div className="presBox-option-block" style={{ padding: '16px' }}>
+                        <div className="presBox-option-block">
+                            Click on a box to apply the effect.
                             <div className="presBox-option-block presBox-option-center">
-                                {/* Chat for idea generation */}
-
                                 {/* Preview Animations  */}
                                 <div className="presBox-effects">
                                     {this.generatedAnimations.map((elem, i) => (
@@ -2269,13 +2246,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                         </div>
                                     </div>
                                     <div className="presBox-icon-list">
-                                        {/* <IconButton
-                                                type={Type.TERT}
-                                                color={activeItem.presentation_effectDirection === PresEffectDirection.Center ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor}
-                                                tooltip="Center"
-                                                icon={<FaCompressArrowsAlt size={'16px'} />}
-                                                onClick={() => this.updateEffectDirection(PresEffectDirection.Center)}
-                                            /> */}
                                         <IconButton
                                             type={Type.TERT}
                                             color={activeItem.presentation_effectDirection === PresEffectDirection.Left ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor}
@@ -2395,9 +2365,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                                     valueLabelDisplay="auto"
                                                 />
                                             </div>
-                                            <SpringAnimationPreview tension={timingConfig.stiffness} friction={timingConfig.damping} mass={timingConfig.mass}>
-                                                <div style={{ width: '40px', height: '40px', backgroundColor: '#2e96ff', borderRadius: '4px' }}></div>
-                                            </SpringAnimationPreview>
+                                            Preview Effect
+                                            <div className="presBox-option-block presBox-option-center">
+                                                <div className="presBox-effect-container">
+                                                    <SlideEffect dir={direction as PresEffectDirection} presEffect={effect as PresEffect} tension={timingConfig.stiffness} friction={timingConfig.damping} mass={timingConfig.mass} infinite>
+                                                        <div className="presBox-effect-demo-box" style={{ backgroundColor: springPreviewColors[0] }}></div>
+                                                    </SlideEffect>
+                                                </div>
+                                            </div>
                                         </>
                                     )}
                                 </>
@@ -2405,26 +2380,23 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         </div>
 
                         {/* Toggles */}
-                        <Toggle
-                            formLabel={'Play Audio Annotation'}
-                            toggleType={ToggleType.SWITCH}
-                            toggleStatus={BoolCast(activeItem.presPlayAudio)}
-                            onClick={() => (activeItem.presPlayAudio = !BoolCast(activeItem.presPlayAudio))}
-                            color={SettingsManager.userColor}
-                        />
-                        <Toggle
-                            formLabel={'Zoom Text Selections'}
-                            toggleType={ToggleType.SWITCH}
-                            toggleStatus={BoolCast(activeItem.presentation_zoomText)}
-                            onClick={() => (activeItem.presentation_zoomText = !BoolCast(activeItem.presentation_zoomText))}
-                            color={SettingsManager.userColor}
-                        />
-                        <Button text="Apply to all" type={Type.TERT} color={StrCast(Doc.UserDoc().userVariantColor)} onClick={() => this.applyTo(this.childDocs)} />
-                        {/* <div className="ribbon-final-box">
-                            <div className="ribbon-final-button-hidden" onClick={() => this.applyTo(this.childDocs)}>
-                                Apply to all
-                            </div>
-                        </div> */}
+                        <div className="presBox-option-block">
+                            <Toggle
+                                formLabel={'Play Audio Annotation'}
+                                toggleType={ToggleType.SWITCH}
+                                toggleStatus={BoolCast(activeItem.presPlayAudio)}
+                                onClick={() => (activeItem.presPlayAudio = !BoolCast(activeItem.presPlayAudio))}
+                                color={SettingsManager.userColor}
+                            />
+                            <Toggle
+                                formLabel={'Zoom Text Selections'}
+                                toggleType={ToggleType.SWITCH}
+                                toggleStatus={BoolCast(activeItem.presentation_zoomText)}
+                                onClick={() => (activeItem.presentation_zoomText = !BoolCast(activeItem.presentation_zoomText))}
+                                color={SettingsManager.userColor}
+                            />
+                            <Button text="Apply to all" type={Type.TERT} color={StrCast(Doc.UserDoc().userVariantColor)} onClick={() => this.applyTo(this.childDocs)} />
+                        </div>
                     </div>
                 </>
             );
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 268b1c803..1c6b38aeb 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -513,11 +513,12 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
             </Tooltip>
         );
         items.push(
-            <Tooltip key="edit" title={<div className="dash-tooltip">Edit</div>}>
+            <Tooltip key="customize-slide" title={<div className="dash-tooltip">Customize Slide</div>}>
                 <div
                     className={'slideButton'}
                     onClick={() => {
                         this.presBoxView?.regularSelect(this.slideDoc, this._itemRef.current!, this._dragRef.current!, true, false);
+                        PresBox.Instance.navigateToActiveItem();
                         PresBox.Instance.openProperties();
                         PresBox.Instance.slideToModify = this.Document;
                     }}>
diff --git a/src/client/views/nodes/trails/SlideEffect.scss b/src/client/views/nodes/trails/SlideEffect.scss
index cadda0ccc..cc851354e 100644
--- a/src/client/views/nodes/trails/SlideEffect.scss
+++ b/src/client/views/nodes/trails/SlideEffect.scss
@@ -7,10 +7,6 @@
 
 .flip-side {
     position: absolute;
-    // max-width: 500px;
-    // max-height: 500px;
-    // width: 350px;
-    // height: 200px;
     will-change: transform, opacity;
     backface-visibility: hidden;
 }
@@ -19,6 +15,5 @@
 }
 
 .flip-back {
-    // Get the background color of node instead
     // background-color: rgb(223, 223, 223);
 }
diff --git a/src/client/views/nodes/trails/SlideEffect.tsx b/src/client/views/nodes/trails/SlideEffect.tsx
index e04f53f06..db2ac1ea0 100644
--- a/src/client/views/nodes/trails/SlideEffect.tsx
+++ b/src/client/views/nodes/trails/SlideEffect.tsx
@@ -27,7 +27,10 @@ const infiniteOptions = {
     delay: 500,
 };
 
-// TODO: add visibility detector when the slide comes into view
+/**
+ * This component wraps around the doc to create an effect animation, and also wraps the preview animations
+ * for the effects as well.
+ */
 export default function SpringAnimation({ doc, dir, friction, tension, mass, presEffect, children, infinite }: Props) {
     const [springs, api] = useSpring(
         () => ({
diff --git a/src/client/views/nodes/trails/SlideEffectPreview.tsx b/src/client/views/nodes/trails/SlideEffectPreview.tsx
deleted file mode 100644
index d4c16586a..000000000
--- a/src/client/views/nodes/trails/SlideEffectPreview.tsx
+++ /dev/null
@@ -1,94 +0,0 @@
-import { useSpring, animated, easings } from '@react-spring/web';
-import React, { useEffect, useState } from 'react';
-
-interface Props {
-    friction: number;
-    tension: number;
-    mass: number;
-    children: React.ReactNode;
-}
-
-export default function SpringAnimationPreview({ friction, tension, mass, children }: Props) {
-    const [springs, api] = useSpring(
-        () => ({
-            from: {
-                x: 0,
-                y: 0,
-                opacity: 1,
-                scale: 1,
-            },
-            config: {
-                tension: tension,
-                friction: friction,
-                mass: mass,
-            },
-            onStart: () => {
-                // console.log('started');
-            },
-            onRest: () => {
-                // console.log('resting');
-            },
-        }),
-        [tension, friction, mass]
-    );
-
-    //   Whether the animation is currently playing
-    const [animating, setAnimating] = useState(false);
-
-    const bounceConfig = {
-        from: {
-            x: -50,
-            y: 0,
-        },
-        to: [
-            {
-                x: 50,
-                y: 0,
-                config: {
-                    tension: tension,
-                    friction: friction,
-                    mass: mass,
-                },
-            },
-            {
-                x: -50,
-                y: 0,
-                config: {
-                    duration: 500,
-                    easing: easings.easeInOutCubic,
-                },
-            },
-        ],
-    };
-
-    const animate = () => {
-        api.start(bounceConfig);
-    };
-
-    useEffect(() => {
-        animate();
-    }, []);
-
-    return (
-        <div
-            style={{
-                width: '200px',
-                height: '150px',
-                borderRadius: '4px',
-                border: '1px solid #696969',
-                display: 'flex',
-                justifyContent: 'center',
-                alignItems: 'center',
-            }}
-            onPointerEnter={() => {
-                animate();
-            }}>
-            <animated.div
-                style={{
-                    ...springs,
-                }}>
-                {children}
-            </animated.div>
-        </div>
-    );
-}
-- 
cgit v1.2.3-70-g09d2


From b47af2c9b25a3e2435fd43dd68c4542667f50769 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 15 May 2024 08:01:56 -0400
Subject: fix for multi-select pres box items.

---
 src/client/views/nodes/trails/PresElementBox.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/client/views/nodes/trails/PresElementBox.tsx')

diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index f78e29821..4d5f9bad1 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -176,7 +176,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         e.stopPropagation();
         e.preventDefault();
         if (element && !(e.ctrlKey || e.metaKey || e.button === 2)) {
-            this.presBoxView?.regularSelect(this.slideDoc, this._itemRef.current!, this._dragRef.current!, true, false);
             setupMoveUpEvents(this, e, this.startDrag, emptyFunction, e => {
                 e.stopPropagation();
                 e.preventDefault();
@@ -190,6 +189,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
      * Function to drag and drop the pres element to a diferent location
      */
     startDrag = (e: PointerEvent) => {
+        this.presBoxView?.regularSelect(this.slideDoc, this._itemRef.current!, this._dragRef.current!, true, false);
         const miniView: boolean = this.toolbarWidth <= 100;
         const activeItem = this.slideDoc;
         const dragArray = this.presBoxView?._dragArray ?? [];
-- 
cgit v1.2.3-70-g09d2

