aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2023-08-26 15:52:04 -0400
committerbobzel <zzzman@gmail.com>2023-08-26 15:52:04 -0400
commit8f43a98c363ff541070b100a2d4e10e505df6969 (patch)
treed7bd88f2c2d44e7eb6d9f1b102923b47617cb169
parent2c46608d3207a8463907b0e1904d9b3026d6d1c8 (diff)
parent603e437d4964c2f104a04f4f977802e241347344 (diff)
Merge branch 'master' into UI_Update_Eric_Ma
-rw-r--r--package-lock.json514
-rw-r--r--package.json1
-rw-r--r--report.20230313.165455.65490.0.001.json1294
-rw-r--r--src/client/apis/gpt/GPT.ts29
-rw-r--r--src/client/documents/Documents.ts2
-rw-r--r--src/client/util/CurrentUserUtils.ts14
-rw-r--r--src/client/util/DragManager.ts4
-rw-r--r--src/client/util/RTFMarkup.tsx12
-rw-r--r--src/client/views/ContextMenuItem.tsx25
-rw-r--r--src/client/views/DashboardView.tsx2
-rw-r--r--src/client/views/MainView.tsx10
-rw-r--r--src/client/views/OverlayView.scss1
-rw-r--r--src/client/views/OverlayView.tsx5
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx2
-rw-r--r--src/client/views/collections/TabDocView.tsx6
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx11
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx19
-rw-r--r--src/client/views/global/globalScripts.ts8
-rw-r--r--src/client/views/nodes/DataVizBox/DataVizBox.tsx171
-rw-r--r--src/client/views/nodes/DataVizBox/components/Histogram.tsx173
-rw-r--r--src/client/views/nodes/DataVizBox/components/LineChart.tsx61
-rw-r--r--src/client/views/nodes/DataVizBox/components/PieChart.tsx175
-rw-r--r--src/client/views/nodes/DataVizBox/components/TableBox.tsx270
-rw-r--r--src/client/views/nodes/DocumentView.tsx4
-rw-r--r--src/client/views/nodes/FontIconBox/FontIconBox.tsx15
-rw-r--r--src/client/views/nodes/FontIconBox/TrailsIcon.tsx81
-rw-r--r--src/client/views/nodes/ImageBox.tsx27
-rw-r--r--src/client/views/nodes/WebBoxRenderer.js6
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx63
-rw-r--r--src/client/views/nodes/generativeFill/GenerativeFill.scss97
-rw-r--r--src/client/views/nodes/generativeFill/GenerativeFill.tsx591
-rw-r--r--src/client/views/nodes/generativeFill/GenerativeFillButtons.scss4
-rw-r--r--src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx44
-rw-r--r--src/client/views/nodes/generativeFill/generativeFillUtils/BrushHandler.ts25
-rw-r--r--src/client/views/nodes/generativeFill/generativeFillUtils/GenerativeFillMathHelpers.ts10
-rw-r--r--src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts314
-rw-r--r--src/client/views/nodes/generativeFill/generativeFillUtils/PointerHandler.ts15
-rw-r--r--src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillConstants.ts9
-rw-r--r--src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillInterfaces.ts20
-rw-r--r--src/client/views/pdf/AnchorMenu.tsx103
-rw-r--r--src/client/views/pdf/GPTPopup/GPTPopup.scss66
-rw-r--r--src/client/views/pdf/GPTPopup/GPTPopup.tsx254
-rw-r--r--src/fields/DocSymbols.ts2
43 files changed, 2275 insertions, 2284 deletions
diff --git a/package-lock.json b/package-lock.json
index 093759145..356224577 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -142,24 +142,24 @@
"integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ=="
},
"@babel/core": {
- "version": "7.22.10",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.10.tgz",
- "integrity": "sha512-fTmqbbUBAwCcre6zPzNngvsI0aNrPZe77AeqvDxWM9Nm+04RrJ3CAmGHA9f7lJQY6ZMhRztNemy4uslDxTX4Qw==",
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.11.tgz",
+ "integrity": "sha512-lh7RJrtPdhibbxndr6/xx0w8+CVlY5FJZiaSz908Fpy+G0xkBFTvwLcKJFF4PJxVfGhVWNebikpWGnOoC71juQ==",
"requires": {
"@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.22.10",
"@babel/generator": "^7.22.10",
"@babel/helper-compilation-targets": "^7.22.10",
"@babel/helper-module-transforms": "^7.22.9",
- "@babel/helpers": "^7.22.10",
- "@babel/parser": "^7.22.10",
+ "@babel/helpers": "^7.22.11",
+ "@babel/parser": "^7.22.11",
"@babel/template": "^7.22.5",
- "@babel/traverse": "^7.22.10",
- "@babel/types": "^7.22.10",
+ "@babel/traverse": "^7.22.11",
+ "@babel/types": "^7.22.11",
"convert-source-map": "^1.7.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.2",
- "json5": "^2.2.2",
+ "json5": "^2.2.3",
"semver": "^6.3.1"
},
"dependencies": {
@@ -194,14 +194,14 @@
}
},
"@babel/parser": {
- "version": "7.22.10",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.10.tgz",
- "integrity": "sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ=="
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.11.tgz",
+ "integrity": "sha512-R5zb8eJIBPJriQtbH/htEQy4k7E2dHWlD2Y2VT07JCzwYZHBxV5ZYtM0UhXSNMT74LyxuM+b1jdL7pSesXbC/g=="
},
"@babel/traverse": {
- "version": "7.22.10",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.10.tgz",
- "integrity": "sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig==",
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.11.tgz",
+ "integrity": "sha512-mzAenteTfomcB7mfPtyi+4oe5BZ6MXxWcn4CX+h4IRJ+OOGXBrWU6jDQavkQI9Vuc5P+donFabBfFCcmWka9lQ==",
"requires": {
"@babel/code-frame": "^7.22.10",
"@babel/generator": "^7.22.10",
@@ -209,16 +209,16 @@
"@babel/helper-function-name": "^7.22.5",
"@babel/helper-hoist-variables": "^7.22.5",
"@babel/helper-split-export-declaration": "^7.22.6",
- "@babel/parser": "^7.22.10",
- "@babel/types": "^7.22.10",
+ "@babel/parser": "^7.22.11",
+ "@babel/types": "^7.22.11",
"debug": "^4.1.0",
"globals": "^11.1.0"
}
},
"@babel/types": {
- "version": "7.22.10",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz",
- "integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==",
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz",
+ "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==",
"requires": {
"@babel/helper-string-parser": "^7.22.5",
"@babel/helper-validator-identifier": "^7.22.5",
@@ -278,9 +278,9 @@
},
"dependencies": {
"@babel/types": {
- "version": "7.22.10",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz",
- "integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==",
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz",
+ "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==",
"requires": {
"@babel/helper-string-parser": "^7.22.5",
"@babel/helper-validator-identifier": "^7.22.5",
@@ -322,9 +322,9 @@
}
},
"@babel/helper-create-class-features-plugin": {
- "version": "7.22.10",
- "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.10.tgz",
- "integrity": "sha512-5IBb77txKYQPpOEdUdIhBx8VrZyDCQ+H82H0+5dX1TmuscP5vJKEE3cKurjtIw/vFwzbVH48VweE78kVDBrqjA==",
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.11.tgz",
+ "integrity": "sha512-y1grdYL4WzmUDBRGK0pDbIoFd7UZKoDurDzWEoNMYoj1EL+foGRQNyPWDcC+YyegN5y1DUsFFmzjGijB3nSVAQ==",
"requires": {
"@babel/helper-annotate-as-pure": "^7.22.5",
"@babel/helper-environment-visitor": "^7.22.5",
@@ -557,9 +557,9 @@
},
"dependencies": {
"@babel/types": {
- "version": "7.22.10",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz",
- "integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==",
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz",
+ "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==",
"requires": {
"@babel/helper-string-parser": "^7.22.5",
"@babel/helper-validator-identifier": "^7.22.5",
@@ -569,13 +569,13 @@
}
},
"@babel/helpers": {
- "version": "7.22.10",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.10.tgz",
- "integrity": "sha512-a41J4NW8HyZa1I1vAndrraTlPZ/eZoga2ZgS7fEr0tZJGVU4xqdE80CEm0CcNjha5EZ8fTBYLKHF0kqDUuAwQw==",
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.11.tgz",
+ "integrity": "sha512-vyOXC8PBWaGc5h7GMsNx68OH33cypkEDJCHvYVVgVbbxJDROYVtexSk0gK5iCF1xNjRIN2s8ai7hwkWDq5szWg==",
"requires": {
"@babel/template": "^7.22.5",
- "@babel/traverse": "^7.22.10",
- "@babel/types": "^7.22.10"
+ "@babel/traverse": "^7.22.11",
+ "@babel/types": "^7.22.11"
},
"dependencies": {
"@babel/code-frame": {
@@ -609,14 +609,14 @@
}
},
"@babel/parser": {
- "version": "7.22.10",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.10.tgz",
- "integrity": "sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ=="
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.11.tgz",
+ "integrity": "sha512-R5zb8eJIBPJriQtbH/htEQy4k7E2dHWlD2Y2VT07JCzwYZHBxV5ZYtM0UhXSNMT74LyxuM+b1jdL7pSesXbC/g=="
},
"@babel/traverse": {
- "version": "7.22.10",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.10.tgz",
- "integrity": "sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig==",
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.11.tgz",
+ "integrity": "sha512-mzAenteTfomcB7mfPtyi+4oe5BZ6MXxWcn4CX+h4IRJ+OOGXBrWU6jDQavkQI9Vuc5P+donFabBfFCcmWka9lQ==",
"requires": {
"@babel/code-frame": "^7.22.10",
"@babel/generator": "^7.22.10",
@@ -624,16 +624,16 @@
"@babel/helper-function-name": "^7.22.5",
"@babel/helper-hoist-variables": "^7.22.5",
"@babel/helper-split-export-declaration": "^7.22.6",
- "@babel/parser": "^7.22.10",
- "@babel/types": "^7.22.10",
+ "@babel/parser": "^7.22.11",
+ "@babel/types": "^7.22.11",
"debug": "^4.1.0",
"globals": "^11.1.0"
}
},
"@babel/types": {
- "version": "7.22.10",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz",
- "integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==",
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz",
+ "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==",
"requires": {
"@babel/helper-string-parser": "^7.22.5",
"@babel/helper-validator-identifier": "^7.22.5",
@@ -941,9 +941,9 @@
}
},
"@babel/plugin-transform-async-generator-functions": {
- "version": "7.22.10",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.10.tgz",
- "integrity": "sha512-eueE8lvKVzq5wIObKK/7dvoeKJ+xc6TvRn6aysIjS6pSCeLy7S/eVi7pEQknZqyqvzaNKdDtem8nUNTBgDVR2g==",
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.11.tgz",
+ "integrity": "sha512-0pAlmeRJn6wU84zzZsEOx1JV1Jf8fqO9ok7wofIJwUnplYo247dcd24P+cMJht7ts9xkzdtB0EPHmOb7F+KzXw==",
"requires": {
"@babel/helper-environment-visitor": "^7.22.5",
"@babel/helper-plugin-utils": "^7.22.5",
@@ -1022,11 +1022,11 @@
}
},
"@babel/plugin-transform-class-static-block": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.5.tgz",
- "integrity": "sha512-SPToJ5eYZLxlnp1UzdARpOGeC2GbHvr9d/UV0EukuVx8atktg194oe+C5BqQ8jRTkgLRVOPYeXRSBg1IlMoVRA==",
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.11.tgz",
+ "integrity": "sha512-GMM8gGmqI7guS/llMFk1bJDkKfn3v3C4KHK9Yg1ey5qcHcOlKb0QvcMrgzvxo+T03/4szNh5lghY+fEC98Kq9g==",
"requires": {
- "@babel/helper-create-class-features-plugin": "^7.22.5",
+ "@babel/helper-create-class-features-plugin": "^7.22.11",
"@babel/helper-plugin-utils": "^7.22.5",
"@babel/plugin-syntax-class-static-block": "^7.14.5"
},
@@ -1132,9 +1132,9 @@
}
},
"@babel/plugin-transform-dynamic-import": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.5.tgz",
- "integrity": "sha512-0MC3ppTB1AMxd8fXjSrbPa7LT9hrImt+/fcj+Pg5YMD7UQyWp/02+JWpdnCymmsXwIx5Z+sYn1bwCn4ZJNvhqQ==",
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.11.tgz",
+ "integrity": "sha512-g/21plo58sfteWjaO0ZNVb+uEOkJNjAaHhbejrnBmu011l/eNDScmkbjCC3l4FKb10ViaGU4aOkFznSu2zRHgA==",
"requires": {
"@babel/helper-plugin-utils": "^7.22.5",
"@babel/plugin-syntax-dynamic-import": "^7.8.3"
@@ -1164,9 +1164,9 @@
}
},
"@babel/plugin-transform-export-namespace-from": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.5.tgz",
- "integrity": "sha512-X4hhm7FRnPgd4nDA4b/5V280xCx6oL7Oob5+9qVS5C13Zq4bh1qq7LU0GgRU6b5dBWBvhGaXYVB4AcN6+ol6vg==",
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.11.tgz",
+ "integrity": "sha512-xa7aad7q7OiT8oNZ1mU7NrISjlSkVdMbNxn9IuLZyL9AJEhs1Apba3I+u5riX1dIkdptP5EKDG5XDPByWxtehw==",
"requires": {
"@babel/helper-plugin-utils": "^7.22.5",
"@babel/plugin-syntax-export-namespace-from": "^7.8.3"
@@ -1212,9 +1212,9 @@
}
},
"@babel/plugin-transform-json-strings": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.5.tgz",
- "integrity": "sha512-DuCRB7fu8MyTLbEQd1ew3R85nx/88yMoqo2uPSjevMj3yoN7CDM8jkgrY0wmVxfJZyJ/B9fE1iq7EQppWQmR5A==",
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.11.tgz",
+ "integrity": "sha512-CxT5tCqpA9/jXFlme9xIBCc5RPtdDq3JpkkhgHQqtDdiTnTI0jtZ0QzXhr5DILeYifDPp2wvY2ad+7+hLMW5Pw==",
"requires": {
"@babel/helper-plugin-utils": "^7.22.5",
"@babel/plugin-syntax-json-strings": "^7.8.3"
@@ -1243,9 +1243,9 @@
}
},
"@babel/plugin-transform-logical-assignment-operators": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.5.tgz",
- "integrity": "sha512-MQQOUW1KL8X0cDWfbwYP+TbVbZm16QmQXJQ+vndPtH/BoO0lOKpVoEDMI7+PskYxH+IiE0tS8xZye0qr1lGzSA==",
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.11.tgz",
+ "integrity": "sha512-qQwRTP4+6xFCDV5k7gZBF3C31K34ut0tbEcTKxlX/0KXxm9GLcO14p570aWxFvVzx6QAfPgq7gaeIHXJC8LswQ==",
"requires": {
"@babel/helper-plugin-utils": "^7.22.5",
"@babel/plugin-syntax-logical-assignment-operators": "^7.10.4"
@@ -1290,11 +1290,11 @@
}
},
"@babel/plugin-transform-modules-commonjs": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz",
- "integrity": "sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA==",
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.11.tgz",
+ "integrity": "sha512-o2+bg7GDS60cJMgz9jWqRUsWkMzLCxp+jFDeDUT5sjRlAxcJWZ2ylNdI7QQ2+CH5hWu7OnN+Cv3htt7AkSf96g==",
"requires": {
- "@babel/helper-module-transforms": "^7.22.5",
+ "@babel/helper-module-transforms": "^7.22.9",
"@babel/helper-plugin-utils": "^7.22.5",
"@babel/helper-simple-access": "^7.22.5"
},
@@ -1307,12 +1307,12 @@
}
},
"@babel/plugin-transform-modules-systemjs": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.5.tgz",
- "integrity": "sha512-emtEpoaTMsOs6Tzz+nbmcePl6AKVtS1yC4YNAeMun9U8YCsgadPNxnOPQ8GhHFB2qdx+LZu9LgoC0Lthuu05DQ==",
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.11.tgz",
+ "integrity": "sha512-rIqHmHoMEOhI3VkVf5jQ15l539KrwhzqcBO6wdCNWPWc/JWt9ILNYNUssbRpeq0qWns8svuw8LnMNCvWBIJ8wA==",
"requires": {
"@babel/helper-hoist-variables": "^7.22.5",
- "@babel/helper-module-transforms": "^7.22.5",
+ "@babel/helper-module-transforms": "^7.22.9",
"@babel/helper-plugin-utils": "^7.22.5",
"@babel/helper-validator-identifier": "^7.22.5"
},
@@ -1372,9 +1372,9 @@
}
},
"@babel/plugin-transform-nullish-coalescing-operator": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.5.tgz",
- "integrity": "sha512-6CF8g6z1dNYZ/VXok5uYkkBBICHZPiGEl7oDnAx2Mt1hlHVHOSIKWJaXHjQJA5VB43KZnXZDIexMchY4y2PGdA==",
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.11.tgz",
+ "integrity": "sha512-YZWOw4HxXrotb5xsjMJUDlLgcDXSfO9eCmdl1bgW4+/lAGdkjaEvOnQ4p5WKKdUgSzO39dgPl0pTnfxm0OAXcg==",
"requires": {
"@babel/helper-plugin-utils": "^7.22.5",
"@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3"
@@ -1388,9 +1388,9 @@
}
},
"@babel/plugin-transform-numeric-separator": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.5.tgz",
- "integrity": "sha512-NbslED1/6M+sXiwwtcAB/nieypGw02Ejf4KtDeMkCEpP6gWFMX1wI9WKYua+4oBneCCEmulOkRpwywypVZzs/g==",
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.11.tgz",
+ "integrity": "sha512-3dzU4QGPsILdJbASKhF/V2TVP+gJya1PsueQCxIPCEcerqF21oEcrob4mzjsp2Py/1nLfF5m+xYNMDpmA8vffg==",
"requires": {
"@babel/helper-plugin-utils": "^7.22.5",
"@babel/plugin-syntax-numeric-separator": "^7.10.4"
@@ -1404,12 +1404,12 @@
}
},
"@babel/plugin-transform-object-rest-spread": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.5.tgz",
- "integrity": "sha512-Kk3lyDmEslH9DnvCDA1s1kkd3YWQITiBOHngOtDL9Pt6BZjzqb6hiOlb8VfjiiQJ2unmegBqZu0rx5RxJb5vmQ==",
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.11.tgz",
+ "integrity": "sha512-nX8cPFa6+UmbepISvlf5jhQyaC7ASs/7UxHmMkuJ/k5xSHvDPPaibMo+v3TXwU/Pjqhep/nFNpd3zn4YR59pnw==",
"requires": {
- "@babel/compat-data": "^7.22.5",
- "@babel/helper-compilation-targets": "^7.22.5",
+ "@babel/compat-data": "^7.22.9",
+ "@babel/helper-compilation-targets": "^7.22.10",
"@babel/helper-plugin-utils": "^7.22.5",
"@babel/plugin-syntax-object-rest-spread": "^7.8.3",
"@babel/plugin-transform-parameters": "^7.22.5"
@@ -1439,9 +1439,9 @@
}
},
"@babel/plugin-transform-optional-catch-binding": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.5.tgz",
- "integrity": "sha512-pH8orJahy+hzZje5b8e2QIlBWQvGpelS76C63Z+jhZKsmzfNaPQ+LaW6dcJ9bxTpo1mtXbgHwy765Ro3jftmUg==",
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.11.tgz",
+ "integrity": "sha512-rli0WxesXUeCJnMYhzAglEjLWVDF6ahb45HuprcmQuLidBJFWjNnOzssk2kuc6e33FlLaiZhG/kUIzUMWdBKaQ==",
"requires": {
"@babel/helper-plugin-utils": "^7.22.5",
"@babel/plugin-syntax-optional-catch-binding": "^7.8.3"
@@ -1455,9 +1455,9 @@
}
},
"@babel/plugin-transform-optional-chaining": {
- "version": "7.22.10",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.10.tgz",
- "integrity": "sha512-MMkQqZAZ+MGj+jGTG3OTuhKeBpNcO+0oCEbrGNEaOmiEn+1MzRyQlYsruGiU8RTK3zV6XwrVJTmwiDOyYK6J9g==",
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.11.tgz",
+ "integrity": "sha512-7X2vGqH2ZKu7Imx0C+o5OysRwtF/wzdCAqmcD1N1v2Ww8CtOSC+p+VoV76skm47DLvBZ8kBFic+egqxM9S/p4g==",
"requires": {
"@babel/helper-plugin-utils": "^7.22.5",
"@babel/helper-skip-transparent-expression-wrappers": "^7.22.5",
@@ -1503,12 +1503,12 @@
}
},
"@babel/plugin-transform-private-property-in-object": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.5.tgz",
- "integrity": "sha512-/9xnaTTJcVoBtSSmrVyhtSvO3kbqS2ODoh2juEU72c3aYonNF0OMGiaz2gjukyKM2wBBYJP38S4JiE0Wfb5VMQ==",
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.11.tgz",
+ "integrity": "sha512-sSCbqZDBKHetvjSwpyWzhuHkmW5RummxJBVbYLkGkaiTOWGxml7SXt0iWa03bzxFIx7wOj3g/ILRd0RcJKBeSQ==",
"requires": {
"@babel/helper-annotate-as-pure": "^7.22.5",
- "@babel/helper-create-class-features-plugin": "^7.22.5",
+ "@babel/helper-create-class-features-plugin": "^7.22.11",
"@babel/helper-plugin-utils": "^7.22.5",
"@babel/plugin-syntax-private-property-in-object": "^7.14.5"
},
@@ -1733,12 +1733,12 @@
}
},
"@babel/plugin-transform-typescript": {
- "version": "7.22.10",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.10.tgz",
- "integrity": "sha512-7++c8I/ymsDo4QQBAgbraXLzIM6jmfao11KgIBEYZRReWzNWH9NtNgJcyrZiXsOPh523FQm6LfpLyy/U5fn46A==",
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.11.tgz",
+ "integrity": "sha512-0E4/L+7gfvHub7wsbTv03oRtD69X31LByy44fGmFzbZScpupFByMcgCJ0VbBTkzyjSJKuRoGN8tcijOWKTmqOA==",
"requires": {
"@babel/helper-annotate-as-pure": "^7.22.5",
- "@babel/helper-create-class-features-plugin": "^7.22.10",
+ "@babel/helper-create-class-features-plugin": "^7.22.11",
"@babel/helper-plugin-utils": "^7.22.5",
"@babel/plugin-syntax-typescript": "^7.22.5"
},
@@ -1914,9 +1914,9 @@
"integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg=="
},
"@babel/types": {
- "version": "7.22.10",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz",
- "integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==",
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz",
+ "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==",
"requires": {
"@babel/helper-string-parser": "^7.22.5",
"@babel/helper-validator-identifier": "^7.22.5",
@@ -1961,15 +1961,15 @@
}
},
"@babel/preset-typescript": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.22.5.tgz",
- "integrity": "sha512-YbPaal9LxztSGhmndR46FmAbkJ/1fAsw293tSU+I5E5h+cnJ3d4GTwyUgGYmOXJYdGA+uNePle4qbaRzj2NISQ==",
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.22.11.tgz",
+ "integrity": "sha512-tWY5wyCZYBGY7IlalfKI1rLiGlIfnwsRHZqlky0HVv8qviwQ1Uo/05M6+s+TcTCVa6Bmoo2uJW5TMFX6Wa4qVg==",
"requires": {
"@babel/helper-plugin-utils": "^7.22.5",
"@babel/helper-validator-option": "^7.22.5",
"@babel/plugin-syntax-jsx": "^7.22.5",
- "@babel/plugin-transform-modules-commonjs": "^7.22.5",
- "@babel/plugin-transform-typescript": "^7.22.5"
+ "@babel/plugin-transform-modules-commonjs": "^7.22.11",
+ "@babel/plugin-transform-typescript": "^7.22.11"
},
"dependencies": {
"@babel/helper-plugin-utils": {
@@ -2693,6 +2693,36 @@
"resolve-url": "^0.2.1"
}
},
+ "@floating-ui/core": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.4.1.tgz",
+ "integrity": "sha512-jk3WqquEJRlcyu7997NtR5PibI+y5bi+LS3hPmguVClypenMsCY3CBa3LAQnozRCtCrYWSEtAdiskpamuJRFOQ==",
+ "requires": {
+ "@floating-ui/utils": "^0.1.1"
+ }
+ },
+ "@floating-ui/dom": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.1.tgz",
+ "integrity": "sha512-KwvVcPSXg6mQygvA1TjbN/gh///36kKtllIF8SUm0qpFj8+rvYrpvlYdL1JoA71SHpDqgSSdGOSoQ0Mp3uY5aw==",
+ "requires": {
+ "@floating-ui/core": "^1.4.1",
+ "@floating-ui/utils": "^0.1.1"
+ }
+ },
+ "@floating-ui/react-dom": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.1.tgz",
+ "integrity": "sha512-rZtAmSht4Lry6gdhAJDrCp/6rKN7++JnL1/Anbr/DdeyYXQPxvg/ivrbYvJulbRf4vL8b212suwMM2lxbv+RQA==",
+ "requires": {
+ "@floating-ui/dom": "^1.3.0"
+ }
+ },
+ "@floating-ui/utils": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.1.tgz",
+ "integrity": "sha512-m0G6wlnhm/AX0H12IOWtK8gASEMffnX08RtKkCgTdHb9JpHKGloI7icFfLg9ZmQeavcvR0PKmzxClyuFPSjKWw=="
+ },
"@fortawesome/fontawesome-common-types": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.0.tgz",
@@ -3202,18 +3232,18 @@
}
},
"@mui/styled-engine-sc": {
- "version": "5.12.0",
- "resolved": "https://registry.npmjs.org/@mui/styled-engine-sc/-/styled-engine-sc-5.12.0.tgz",
- "integrity": "sha512-3MgYoY2YG5tx0E5oKqvCv94oL0ABVBr+qpcyvciXW/v0wzPG6bXvuZV80GHYlJfasgnnRa1AbRWf5a9FcX8v6g==",
+ "version": "5.14.6",
+ "resolved": "https://registry.npmjs.org/@mui/styled-engine-sc/-/styled-engine-sc-5.14.6.tgz",
+ "integrity": "sha512-7/KXXdDLjpQAmbmIhUs1x7nzqooEiHkidQOXCIH04NiVa4KRxP4v/bOWV/5GpgZi1Aky5ruf9IVyH3jxYIW3JA==",
"requires": {
- "@babel/runtime": "^7.21.0",
+ "@babel/runtime": "^7.22.10",
"prop-types": "^15.8.1"
},
"dependencies": {
"@babel/runtime": {
- "version": "7.22.10",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.10.tgz",
- "integrity": "sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ==",
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.11.tgz",
+ "integrity": "sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA==",
"requires": {
"regenerator-runtime": "^0.14.0"
}
@@ -6627,14 +6657,15 @@
"integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww=="
},
"@mui/base": {
- "version": "5.0.0-beta.11",
- "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.11.tgz",
- "integrity": "sha512-FdKZGPd8qmC3ZNke7CNhzcEgToc02M6WYZc9hcBsNQ17bgAd3s9F//1bDDYgMVBYxDM71V0sv/hBHlOY4I1ZVA==",
+ "version": "5.0.0-beta.12",
+ "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.12.tgz",
+ "integrity": "sha512-tZjjXNAyUpwSDT1uRliZMhRQkWYzELJ8Qi61EuOMRpi36HIwnK2T7Nr4RI423Sv8G2EEikDAZj7je33eNd73NQ==",
"requires": {
- "@babel/runtime": "^7.22.6",
+ "@babel/runtime": "^7.22.10",
"@emotion/is-prop-valid": "^1.2.1",
+ "@floating-ui/react-dom": "^2.0.1",
"@mui/types": "^7.2.4",
- "@mui/utils": "^5.14.5",
+ "@mui/utils": "^5.14.6",
"@popperjs/core": "^2.11.8",
"clsx": "^2.0.0",
"prop-types": "^15.8.1",
@@ -6642,9 +6673,9 @@
},
"dependencies": {
"@babel/runtime": {
- "version": "7.22.10",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.10.tgz",
- "integrity": "sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ==",
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.11.tgz",
+ "integrity": "sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA==",
"requires": {
"regenerator-runtime": "^0.14.0"
}
@@ -6652,21 +6683,21 @@
}
},
"@mui/core-downloads-tracker": {
- "version": "5.14.5",
- "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.5.tgz",
- "integrity": "sha512-+wpGH1USwPcKMFPMvXqYPC6fEvhxM3FzxC8lyDiNK/imLyyJ6y2DPb1Oue7OGIKJWBmYBqrWWtfovrxd1aJHTA=="
+ "version": "5.14.6",
+ "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.6.tgz",
+ "integrity": "sha512-QZEU3pyGWLuaHbxvOlShol7U1FVgzWBR0OH9H8D7L8w4/vto5N5jJVvlqFQS3T0zbR6YGHxFaiL6Ky87jQg7aw=="
},
"@mui/material": {
- "version": "5.14.5",
- "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.14.5.tgz",
- "integrity": "sha512-4qa4GMfuZH0Ai3mttk5ccXP8a3sf7aPlAJwyMrUSz6h9hPri6BPou94zeu3rENhhmKLby9S/W1y+pmficy8JKA==",
- "requires": {
- "@babel/runtime": "^7.22.6",
- "@mui/base": "5.0.0-beta.11",
- "@mui/core-downloads-tracker": "^5.14.5",
- "@mui/system": "^5.14.5",
+ "version": "5.14.6",
+ "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.14.6.tgz",
+ "integrity": "sha512-C3UgGrmtvcGkQkm0ONBU7bTdapTjQc2Se3b2354xMmU7lgSgW7VM6EP9wIH5XqqoJ60m9l/s9kbTWX0Y+EaWvA==",
+ "requires": {
+ "@babel/runtime": "^7.22.10",
+ "@mui/base": "5.0.0-beta.12",
+ "@mui/core-downloads-tracker": "^5.14.6",
+ "@mui/system": "^5.14.6",
"@mui/types": "^7.2.4",
- "@mui/utils": "^5.14.5",
+ "@mui/utils": "^5.14.6",
"@types/react-transition-group": "^4.4.6",
"clsx": "^2.0.0",
"csstype": "^3.1.2",
@@ -6676,9 +6707,9 @@
},
"dependencies": {
"@babel/runtime": {
- "version": "7.22.10",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.10.tgz",
- "integrity": "sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ==",
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.11.tgz",
+ "integrity": "sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA==",
"requires": {
"regenerator-runtime": "^0.14.0"
}
@@ -6686,19 +6717,19 @@
}
},
"@mui/private-theming": {
- "version": "5.14.5",
- "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.14.5.tgz",
- "integrity": "sha512-cC4C5RrpXpDaaZyH9QwmPhRLgz+f2SYbOty3cPkk4qPSOSfif2ZEcDD9HTENKDDd9deB+xkPKzzZhi8cxIx8Ig==",
+ "version": "5.14.6",
+ "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.14.6.tgz",
+ "integrity": "sha512-3VBLFGizBXfofyk33bwRg6t9L648aKnLmOKPfY1wFuiXq3AEYwobK65iDci/tHKxm/VKbZ6A7PFjLejvB3EvRQ==",
"requires": {
- "@babel/runtime": "^7.22.6",
- "@mui/utils": "^5.14.5",
+ "@babel/runtime": "^7.22.10",
+ "@mui/utils": "^5.14.6",
"prop-types": "^15.8.1"
},
"dependencies": {
"@babel/runtime": {
- "version": "7.22.10",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.10.tgz",
- "integrity": "sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ==",
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.11.tgz",
+ "integrity": "sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA==",
"requires": {
"regenerator-runtime": "^0.14.0"
}
@@ -6706,20 +6737,20 @@
}
},
"@mui/styled-engine": {
- "version": "5.13.2",
- "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.13.2.tgz",
- "integrity": "sha512-VCYCU6xVtXOrIN8lcbuPmoG+u7FYuOERG++fpY74hPpEWkyFQG97F+/XfTQVYzlR2m7nPjnwVUgATcTCMEaMvw==",
+ "version": "5.14.6",
+ "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.14.6.tgz",
+ "integrity": "sha512-I6zeu/OP1Hk4NsX1Oj85TiYl1dER0JMsLJVn76J1Ihl24A5EbiZQKJp3Mn+ufA79ypkdAvM9aQCAQyiVBFcUHg==",
"requires": {
- "@babel/runtime": "^7.21.0",
+ "@babel/runtime": "^7.22.10",
"@emotion/cache": "^11.11.0",
"csstype": "^3.1.2",
"prop-types": "^15.8.1"
},
"dependencies": {
"@babel/runtime": {
- "version": "7.22.10",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.10.tgz",
- "integrity": "sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ==",
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.11.tgz",
+ "integrity": "sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA==",
"requires": {
"regenerator-runtime": "^0.14.0"
}
@@ -6727,24 +6758,24 @@
}
},
"@mui/system": {
- "version": "5.14.5",
- "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.14.5.tgz",
- "integrity": "sha512-mextXZHDeGcR7E1kx43TRARrVXy+gI4wzpUgNv7MqZs1dvTVXQGVeAT6ydj9d6FUqHBPMNLGV/21vJOrpqsL+w==",
+ "version": "5.14.6",
+ "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.14.6.tgz",
+ "integrity": "sha512-/n0ae1MegWjiV1BpRU8jgg4E0zBjeB2VYsT/68ag/xaDuq3/TaDKJeT9REIvyBvwlG3CI3S2O+tRELktxCD1kg==",
"requires": {
- "@babel/runtime": "^7.22.6",
- "@mui/private-theming": "^5.14.5",
- "@mui/styled-engine": "^5.13.2",
+ "@babel/runtime": "^7.22.10",
+ "@mui/private-theming": "^5.14.6",
+ "@mui/styled-engine": "^5.14.6",
"@mui/types": "^7.2.4",
- "@mui/utils": "^5.14.5",
+ "@mui/utils": "^5.14.6",
"clsx": "^2.0.0",
"csstype": "^3.1.2",
"prop-types": "^15.8.1"
},
"dependencies": {
"@babel/runtime": {
- "version": "7.22.10",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.10.tgz",
- "integrity": "sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ==",
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.11.tgz",
+ "integrity": "sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA==",
"requires": {
"regenerator-runtime": "^0.14.0"
}
@@ -6752,11 +6783,11 @@
}
},
"@mui/utils": {
- "version": "5.14.5",
- "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.14.5.tgz",
- "integrity": "sha512-6Hzw63VR9C5xYv+CbjndoRLU6Gntal8rJ5W+GUzkyHrGWIyYPWZPa6AevnyGioySNETATe1H9oXS8f/7qgIHJA==",
+ "version": "5.14.6",
+ "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.14.6.tgz",
+ "integrity": "sha512-AznpqLu6hrFnpHgcvsSSMCG+cDbkcCYfo+daUwBVReNYv4l+NQ8+wvBAF4aUMi155N7xWbbgh0cyKs6Wdsm3aA==",
"requires": {
- "@babel/runtime": "^7.22.6",
+ "@babel/runtime": "^7.22.10",
"@types/prop-types": "^15.7.5",
"@types/react-is": "^18.2.1",
"prop-types": "^15.8.1",
@@ -6764,9 +6795,9 @@
},
"dependencies": {
"@babel/runtime": {
- "version": "7.22.10",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.10.tgz",
- "integrity": "sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ==",
+ "version": "7.22.11",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.11.tgz",
+ "integrity": "sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA==",
"requires": {
"regenerator-runtime": "^0.14.0"
}
@@ -7063,97 +7094,12 @@
"strip-ansi": "^7.0.1"
}
},
- "string-width-cjs": {
- "version": "npm:string-width@4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "requires": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
- "dependencies": {
- "ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
- },
- "emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
- },
- "strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "requires": {
- "ansi-regex": "^5.0.1"
- }
- }
- }
- },
"strip-ansi": {
"version": "7.1.0",
"bundled": true,
"requires": {
"ansi-regex": "^6.0.1"
}
- },
- "strip-ansi-cjs": {
- "version": "npm:strip-ansi@6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "requires": {
- "ansi-regex": "^5.0.1"
- },
- "dependencies": {
- "ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
- }
- }
- },
- "wrap-ansi-cjs": {
- "version": "npm:wrap-ansi@7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
- "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
- "requires": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- },
- "dependencies": {
- "ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
- },
- "emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
- },
- "string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "requires": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- }
- },
- "strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "requires": {
- "ansi-regex": "^5.0.1"
- }
- }
- }
}
}
},
@@ -8658,7 +8604,7 @@
}
},
"string-width-cjs": {
- "version": "npm:string-width-cjs@4.2.3",
+ "version": "npm:string-width@4.2.3",
"bundled": true,
"requires": {
"emoji-regex": "^8.0.0",
@@ -8681,7 +8627,7 @@
}
},
"strip-ansi-cjs": {
- "version": "npm:strip-ansi-cjs@6.0.1",
+ "version": "npm:strip-ansi@6.0.1",
"bundled": true,
"requires": {
"ansi-regex": "^5.0.1"
@@ -8840,7 +8786,7 @@
}
},
"wrap-ansi-cjs": {
- "version": "npm:wrap-ansi-cjs@7.0.0",
+ "version": "npm:wrap-ansi@7.0.0",
"bundled": true,
"requires": {
"ansi-styles": "^4.0.0",
@@ -10055,9 +10001,9 @@
"integrity": "sha512-TKiyTVZxJGhsTszLuzb+6vUZSjVOAhClszBr2Ta2k9IwtNBT/4dzmL6aywt0HCgEZlmwJzXJd8yNiob6HgwTRg=="
},
"electron-to-chromium": {
- "version": "1.4.497",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.497.tgz",
- "integrity": "sha512-9cvj6XkrgyxDySKJWYVIyq7p9bOAkH3M3jANgvWNX/F2jIAfbBN4oBNLJg1i68I8wAKVuih2IL4y1n9hqbL3Aw=="
+ "version": "1.4.501",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.501.tgz",
+ "integrity": "sha512-NCF5hZUg73MEP0guvIM+BjPs9W07UeAuc5XCNqRZZTKJxLjE0ZS/Zo5UsV8bbs2y/jeKRPFPzdWdBfOGEZTXKg=="
}
}
},
@@ -24366,6 +24312,55 @@
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
},
+ "react-loader-spinner": {
+ "version": "5.3.4",
+ "resolved": "https://registry.npmjs.org/react-loader-spinner/-/react-loader-spinner-5.3.4.tgz",
+ "integrity": "sha512-G2vw4ssX+RDZ/vfaeva06yfNqyFViv/u+tVZ3kFLy5TKNlNx2DbuwreBSpRtPespQA+VxinxUJsigwLwG9erOg==",
+ "requires": {
+ "react-is": "^18.2.0",
+ "styled-components": "^5.3.5",
+ "styled-tools": "^1.7.2"
+ },
+ "dependencies": {
+ "css-to-react-native": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz",
+ "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==",
+ "requires": {
+ "camelize": "^1.0.0",
+ "css-color-keywords": "^1.0.0",
+ "postcss-value-parser": "^4.0.2"
+ }
+ },
+ "postcss-value-parser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
+ },
+ "react-is": {
+ "version": "18.2.0",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
+ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
+ },
+ "styled-components": {
+ "version": "5.3.11",
+ "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.11.tgz",
+ "integrity": "sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==",
+ "requires": {
+ "@babel/helper-module-imports": "^7.0.0",
+ "@babel/traverse": "^7.4.5",
+ "@emotion/is-prop-valid": "^1.1.0",
+ "@emotion/stylis": "^0.8.4",
+ "@emotion/unitless": "^0.7.4",
+ "babel-plugin-styled-components": ">= 1.12.0",
+ "css-to-react-native": "^3.0.0",
+ "hoist-non-react-statics": "^3.0.0",
+ "shallowequal": "^1.1.0",
+ "supports-color": "^5.5.0"
+ }
+ }
+ }
+ },
"react-loading": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/react-loading/-/react-loading-2.0.3.tgz",
@@ -26772,6 +26767,11 @@
}
}
},
+ "styled-tools": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/styled-tools/-/styled-tools-1.7.2.tgz",
+ "integrity": "sha512-IjLxzM20RMwAsx8M1QoRlCG/Kmq8lKzCGyospjtSXt/BTIIcvgTonaxQAsKnBrsZNwhpHzO9ADx5te0h76ILVg=="
+ },
"stylis": {
"version": "3.5.4",
"resolved": "https://registry.npmjs.org/stylis/-/stylis-3.5.4.tgz",
diff --git a/package.json b/package.json
index 233759056..576c95192 100644
--- a/package.json
+++ b/package.json
@@ -294,6 +294,7 @@
"react-grid-layout": "^1.3.4",
"react-icons": "^4.3.1",
"react-jsx-parser": "^1.29.0",
+ "react-loader-spinner": "^5.3.4",
"react-loading": "^2.0.3",
"react-markdown": "^8.0.3",
"react-measure": "^2.5.2",
diff --git a/report.20230313.165455.65490.0.001.json b/report.20230313.165455.65490.0.001.json
deleted file mode 100644
index 689fcf9eb..000000000
--- a/report.20230313.165455.65490.0.001.json
+++ /dev/null
@@ -1,1294 +0,0 @@
-
-{
- "header": {
- "reportVersion": 1,
- "event": "Allocation failed - JavaScript heap out of memory",
- "trigger": "FatalError",
- "filename": "report.20230313.165455.65490.0.001.json",
- "dumpEventTime": "2023-03-13T16:54:55Z",
- "dumpEventTimeStamp": "1678740895169",
- "processId": 65490,
- "cwd": "/Users/sarah/Desktop/dash/Dash-Web",
- "commandLine": [
- "/Users/sarah/.nvm/versions/node/v12.16.0/bin/node",
- "--max-old-space-size=2048",
- "/Users/sarah/Desktop/dash/Dash-Web/node_modules/ts-node-dev/lib/wrap.js",
- "/Users/sarah/Desktop/dash/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": "20.4.0",
- "osVersion": "Darwin Kernel Version 20.4.0: Fri Mar 5 01:14:14 PST 2021; root:xnu-7195.101.1~3/RELEASE_X86_64",
- "osMachine": "x86_64",
- "cpus": [
- {
- "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
- "speed": 2300,
- "user": 95972310,
- "nice": 0,
- "sys": 69742110,
- "idle": 1132989220,
- "irq": 0
- },
- {
- "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
- "speed": 2300,
- "user": 1235990,
- "nice": 0,
- "sys": 1442360,
- "idle": 1293767630,
- "irq": 0
- },
- {
- "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
- "speed": 2300,
- "user": 77301330,
- "nice": 0,
- "sys": 43426390,
- "idle": 1175757970,
- "irq": 0
- },
- {
- "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
- "speed": 2300,
- "user": 1270620,
- "nice": 0,
- "sys": 1351190,
- "idle": 1293823030,
- "irq": 0
- },
- {
- "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
- "speed": 2300,
- "user": 56966210,
- "nice": 0,
- "sys": 28908780,
- "idle": 1210608910,
- "irq": 0
- },
- {
- "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
- "speed": 2300,
- "user": 1291530,
- "nice": 0,
- "sys": 1265830,
- "idle": 1293886140,
- "irq": 0
- },
- {
- "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
- "speed": 2300,
- "user": 46074390,
- "nice": 0,
- "sys": 22963990,
- "idle": 1227443730,
- "irq": 0
- },
- {
- "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
- "speed": 2300,
- "user": 1311400,
- "nice": 0,
- "sys": 1200930,
- "idle": 1293929770,
- "irq": 0
- },
- {
- "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
- "speed": 2300,
- "user": 36059780,
- "nice": 0,
- "sys": 17309680,
- "idle": 1243110810,
- "irq": 0
- },
- {
- "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
- "speed": 2300,
- "user": 1303400,
- "nice": 0,
- "sys": 1134770,
- "idle": 1294002540,
- "irq": 0
- },
- {
- "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
- "speed": 2300,
- "user": 28665300,
- "nice": 0,
- "sys": 12769180,
- "idle": 1255043860,
- "irq": 0
- },
- {
- "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
- "speed": 2300,
- "user": 1287310,
- "nice": 0,
- "sys": 1073720,
- "idle": 1294078280,
- "irq": 0
- },
- {
- "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
- "speed": 2300,
- "user": 22112060,
- "nice": 0,
- "sys": 9084160,
- "idle": 1265280170,
- "irq": 0
- },
- {
- "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
- "speed": 2300,
- "user": 1256320,
- "nice": 0,
- "sys": 1011250,
- "idle": 1294170360,
- "irq": 0
- },
- {
- "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
- "speed": 2300,
- "user": 17120610,
- "nice": 0,
- "sys": 6395650,
- "idle": 1272958030,
- "irq": 0
- },
- {
- "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
- "speed": 2300,
- "user": 1227540,
- "nice": 0,
- "sys": 963060,
- "idle": 1294245890,
- "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": "en0",
- "internal": false,
- "mac": "88:66:5a:29:28:77",
- "address": "fe80::cc1:cf3b:afa2:144f",
- "netmask": "ffff:ffff:ffff:ffff::",
- "family": "IPv6",
- "scopeid": 6
- },
- {
- "name": "en0",
- "internal": false,
- "mac": "88:66:5a:29:28:77",
- "address": "2620:6e:6000:3100:148e:201a:1a33:145d",
- "netmask": "ffff:ffff:ffff:ffff::",
- "family": "IPv6",
- "scopeid": 0
- },
- {
- "name": "en0",
- "internal": false,
- "mac": "88:66:5a:29:28:77",
- "address": "2620:6e:6000:3100:31e4:88bf:1195:6926",
- "netmask": "ffff:ffff:ffff:ffff::",
- "family": "IPv6",
- "scopeid": 0
- },
- {
- "name": "en0",
- "internal": false,
- "mac": "88:66:5a:29:28:77",
- "address": "fdac:89a:4f49:41ac:83d:26cd:abc5:e973",
- "netmask": "ffff:ffff:ffff:ffff::",
- "family": "IPv6",
- "scopeid": 0
- },
- {
- "name": "en0",
- "internal": false,
- "mac": "88:66:5a:29:28:77",
- "address": "fdac:89a:4f49:41ac:15b2:4a9e:88a9:34c3",
- "netmask": "ffff:ffff:ffff:ffff::",
- "family": "IPv6",
- "scopeid": 0
- },
- {
- "name": "en0",
- "internal": false,
- "mac": "88:66:5a:29:28:77",
- "address": "10.38.53.246",
- "netmask": "255.255.192.0",
- "family": "IPv4"
- },
- {
- "name": "en0",
- "internal": false,
- "mac": "88:66:5a:29:28:77",
- "address": "fd8c:23f:4de7:4523:cc7:18bd:a001:a86b",
- "netmask": "ffff:ffff:ffff:ffff::",
- "family": "IPv6",
- "scopeid": 0
- },
- {
- "name": "en0",
- "internal": false,
- "mac": "88:66:5a:29:28:77",
- "address": "fd8c:23f:4de7:4523:6cbc:dfb0:da3f:fa06",
- "netmask": "ffff:ffff:ffff:ffff::",
- "family": "IPv6",
- "scopeid": 0
- },
- {
- "name": "en0",
- "internal": false,
- "mac": "88:66:5a:29:28:77",
- "address": "fdc9:5cae:17e4:4c54:c1e:4892:ccc4:93ae",
- "netmask": "ffff:ffff:ffff:ffff::",
- "family": "IPv6",
- "scopeid": 0
- },
- {
- "name": "en0",
- "internal": false,
- "mac": "88:66:5a:29:28:77",
- "address": "fdc9:5cae:17e4:4c54:307f:315d:52f7:511f",
- "netmask": "ffff:ffff:ffff:ffff::",
- "family": "IPv6",
- "scopeid": 0
- },
- {
- "name": "en0",
- "internal": false,
- "mac": "88:66:5a:29:28:77",
- "address": "fdd3:9b36:2480:4c6f:cff:ec25:4ee6:54c",
- "netmask": "ffff:ffff:ffff:ffff::",
- "family": "IPv6",
- "scopeid": 0
- },
- {
- "name": "en0",
- "internal": false,
- "mac": "88:66:5a:29:28:77",
- "address": "fdd3:9b36:2480:4c6f:9dba:7458:3932:be33",
- "netmask": "ffff:ffff:ffff:ffff::",
- "family": "IPv6",
- "scopeid": 0
- },
- {
- "name": "awdl0",
- "internal": false,
- "mac": "a6:20:c0:51:e8:8f",
- "address": "fe80::a420:c0ff:fe51:e88f",
- "netmask": "ffff:ffff:ffff:ffff::",
- "family": "IPv6",
- "scopeid": 12
- },
- {
- "name": "llw0",
- "internal": false,
- "mac": "a6:20:c0:51:e8:8f",
- "address": "fe80::a420:c0ff:fe51:e88f",
- "netmask": "ffff:ffff:ffff:ffff::",
- "family": "IPv6",
- "scopeid": 13
- },
- {
- "name": "utun0",
- "internal": false,
- "mac": "00:00:00:00:00:00",
- "address": "fe80::15ac:b094:e48b:b227",
- "netmask": "ffff:ffff:ffff:ffff::",
- "family": "IPv6",
- "scopeid": 14
- },
- {
- "name": "utun1",
- "internal": false,
- "mac": "00:00:00:00:00:00",
- "address": "fe80::97ec:93db:83a3:75ce",
- "netmask": "ffff:ffff:ffff:ffff::",
- "family": "IPv6",
- "scopeid": 15
- },
- {
- "name": "en5",
- "internal": false,
- "mac": "ac:de:48:00:11:22",
- "address": "fe80::aede:48ff:fe00:1122",
- "netmask": "ffff:ffff:ffff:ffff::",
- "family": "IPv6",
- "scopeid": 5
- }
- ],
- "host": "sarahs-mbp.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/sarah/.nvm/versions/node/v12.16.0/bin/node]"
- },
- {
- "pc": "0x0000000100080f3e",
- "symbol": "node::OnFatalError(char const*, char const*) [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]"
- },
- {
- "pc": "0x0000000100185467",
- "symbol": "v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]"
- },
- {
- "pc": "0x0000000100185403",
- "symbol": "v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]"
- },
- {
- "pc": "0x000000010030b5f5",
- "symbol": "v8::internal::Heap::FatalProcessOutOfMemory(char const*) [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]"
- },
- {
- "pc": "0x000000010030ccc4",
- "symbol": "v8::internal::Heap::RecomputeLimits(v8::internal::GarbageCollector) [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]"
- },
- {
- "pc": "0x0000000100309b37",
- "symbol": "v8::internal::Heap::PerformGarbageCollection(v8::internal::GarbageCollector, v8::GCCallbackFlags) [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]"
- },
- {
- "pc": "0x0000000100307afd",
- "symbol": "v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [/Users/sarah/.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/sarah/.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/sarah/.nvm/versions/node/v12.16.0/bin/node]"
- },
- {
- "pc": "0x00000001002e065b",
- "symbol": "v8::internal::Factory::NewFillerObject(int, bool, v8::internal::AllocationType, v8::internal::AllocationOrigin) [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]"
- },
- {
- "pc": "0x0000000100618a18",
- "symbol": "v8::internal::Runtime_AllocateInYoungGeneration(int, unsigned long*, v8::internal::Isolate*) [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]"
- },
- {
- "pc": "0x0000000100950c19",
- "symbol": "Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]"
- }
- ],
- "javascriptHeap": {
- "totalMemory": 2151882752,
- "totalCommittedMemory": 2149824464,
- "usedMemory": 2139828064,
- "availableMemory": 48165544,
- "memoryLimit": 2197815296,
- "heapSpaces": {
- "read_only_space": {
- "memorySize": 262144,
- "committedMemory": 33088,
- "capacity": 32808,
- "used": 32808,
- "available": 0
- },
- "new_space": {
- "memorySize": 2097152,
- "committedMemory": 1076448,
- "capacity": 1047456,
- "used": 28792,
- "available": 1018664
- },
- "old_space": {
- "memorySize": 1958100992,
- "committedMemory": 1957877576,
- "capacity": 1951020152,
- "used": 1950853272,
- "available": 166880
- },
- "code_space": {
- "memorySize": 15372288,
- "committedMemory": 14957376,
- "capacity": 13767968,
- "used": 13767968,
- "available": 0
- },
- "map_space": {
- "memorySize": 1576960,
- "committedMemory": 1406760,
- "capacity": 1236000,
- "used": 1236000,
- "available": 0
- },
- "large_object_space": {
- "memorySize": 174424064,
- "committedMemory": 174424064,
- "capacity": 173906440,
- "used": 173906440,
- "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": 302.098,
- "kernelCpuSeconds": 7.74276,
- "cpuConsumptionPercent": 10.3834,
- "maxRss": 2286298333184,
- "pageFaults": {
- "IORequired": 35,
- "IONotRequired": 2289110
- },
- "fsActivity": {
- "reads": 0,
- "writes": 0
- }
- },
- "libuv": [
- ],
- "environmentVariables": {
- "npm_config_save_dev": "",
- "npm_config_legacy_bundling": "",
- "npm_config_dry_run": "",
- "npm_package_dependencies_translate_google_api": "^1.0.4",
- "npm_package_dependencies_request": "^2.88.2",
- "npm_package_dependencies_express_flash": "0.0.2",
- "npm_package_dependencies__fortawesome_fontawesome_svg_core": "^1.3.0",
- "NVM_INC": "/Users/sarah/.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": "4c2584baf8bae0cde714c832b0768d3c08864422",
- "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",
- "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": "Apple_Terminal",
- "NODE": "/Users/sarah/.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/sarah/Desktop/dash/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/sarah/.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",
- "SHELL": "/bin/zsh",
- "TERM": "xterm-256color",
- "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_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_devDependencies_tslint": "^5.20.1",
- "npm_package_devDependencies__types_react_transition_group": "^4.4.5",
- "npm_package_scripts_tsc": "tsc",
- "TMPDIR": "/var/folders/yk/p_39q8jn673c5p8_66mcxm7r0000gn/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",
- "CONDA_SHLVL": "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",
- "CONDA_PROMPT_MODIFIER": "(base) ",
- "TERM_PROGRAM_VERSION": "440",
- "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_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",
- "TERM_SESSION_ID": "BF3A3D73-8B2D-4041-BAFA-CCC983EE3D05",
- "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_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/sarah/.nvm",
- "USER": "sarah",
- "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_config_globalconfig": "/Users/sarah/.nvm/versions/node/v12.16.0/etc/npmrc",
- "npm_package_dependencies_depcheck": "^0.9.2",
- "npm_package_dependencies__types_web": "0.0.53",
- "CONDA_EXE": "/Users/sarah/miniconda3/bin/conda",
- "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_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.TKuATvqs9j/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_class_transformer": "^0.2.0",
- "npm_package_devDependencies_eslint": "^8.18.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/sarah/.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_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__fortawesome_react_fontawesome": "^0.1.19",
- "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": "^5.15.4",
- "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",
- "_CE_CONDA": "",
- "npm_config_searchlimit": "20",
- "npm_config_read_only": "",
- "npm_config_offline": "",
- "npm_config_fetch_retry_mintimeout": "10000",
- "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": "^5.15.4",
- "npm_package_devDependencies__types_socket_io": "^2.1.13",
- "PATH": "/Users/sarah/.nvm/versions/node/v12.16.0/lib/node_modules/npm/node_modules/npm-lifecycle/node-gyp-bin:/Users/sarah/Desktop/dash/Dash-Web/node_modules/.bin:/Users/sarah/.nvm/versions/node/v12.16.0/bin:/Users/sarah/miniconda3/bin:/Users/sarah/miniconda3/condabin:/Users/sarah/.elan/bin:/Library/Frameworks/Python.framework/Versions/3.9/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/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/sarah/Desktop/dash/Dash-Web/node_modules/.bin/cross-env",
- "npm_config_userconfig": "/Users/sarah/.npmrc",
- "npm_config_init_module": "/Users/sarah/.npm-init.js",
- "npm_package_dependencies__react_google_maps_api": "^2.7.0",
- "CONDA_PREFIX": "/Users/sarah/miniconda3",
- "__CFBundleIdentifier": "com.apple.Terminal",
- "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/sarah/desktop/dash/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.0.22",
- "npm_package_devDependencies_eslint_plugin_react_hooks": "^4.6.0",
- "npm_package_devDependencies__types_lodash": "^4.14.179",
- "JAVA_HOME": "/Library/Java/JavaVirtualMachines/jdk1.8.0_341.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 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_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",
- "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_devDependencies__types_react_icons": "^3.0.0",
- "npm_config_node_gyp": "/Users/sarah/.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_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",
- "_CE_M": "",
- "XPC_SERVICE_NAME": "0",
- "npm_config_unicode": "true",
- "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/sarah",
- "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_animejs": "^2.0.2",
- "npm_package_scripts_test": "mocha -r ts-node/register test/**/*.ts",
- "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_mobx_react": "^5.4.4",
- "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",
- "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_devDependencies_ts_node": "^10.9.1",
- "npm_package_devDependencies__types_react_grid_layout": "^1.3.2",
- "npm_config_searchexclude": "",
- "npm_config_cache": "/Users/sarah/.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",
- "CONDA_PYTHON_EXE": "/Users/sarah/miniconda3/bin/python",
- "LOGNAME": "sarah",
- "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",
- "CLASSPATH": "/Users/sarah/Downloads/cs15/*:.",
- "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/sarah/.nvm/versions/node/v12.16.0/bin",
- "CONDA_DEFAULT_ENV": "base",
- "npm_config_ignore_scripts": "",
- "npm_config_user_agent": "npm/6.14.7 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_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",
- "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_https": "^1.0.0",
- "npm_package_dependencies_array_batcher": "^1.2.3",
- "npm_package_dependencies__fortawesome_free_regular_svg_icons": "^5.15.4",
- "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",
- "npm_config_unsafe_perm": "true",
- "npm_config_tmp": "/var/folders/yk/p_39q8jn673c5p8_66mcxm7r0000gn/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/sarah/.nvm/versions/node/v12.16.0/bin/node",
- "npm_config_prefix": "/Users/sarah/.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",
- "NODE_OPTIONS": "--max_old_space_size=4096",
- "TS_NODE_DEV": "true",
- "VIPSHOME": "/usr/local/Cellar/vips/8.8.1",
- "TYPESCRIPT_PATH": "/Users/sarah/Desktop/dash/Dash-Web/node_modules/typescript/lib/typescript.js",
- "TSCONFIG": "/Users/sarah/Desktop/dash/Dash-Web/tsconfig.json",
- "COMPILER_OPTIONS": "{}",
- "TSLINT": "true",
- "CONTEXT": "/Users/sarah/Desktop/dash/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": 8388608,
- "hard": 67104768
- },
- "cpu_time_seconds": {
- "soft": "unlimited",
- "hard": "unlimited"
- },
- "max_user_processes": {
- "soft": 2784,
- "hard": 4176
- },
- "virtual_memory_kbytes": {
- "soft": "unlimited",
- "hard": "unlimited"
- }
- },
- "sharedObjects": [
- "/Users/sarah/.nvm/versions/node/v12.16.0/bin/node",
- "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation",
- "/usr/lib/libSystem.B.dylib",
- "/usr/lib/libc++.1.dylib",
- "/usr/lib/libobjc.A.dylib",
- "/usr/lib/liboah.dylib",
- "/usr/lib/libfakelink.dylib",
- "/usr/lib/libicucore.A.dylib",
- "/System/Library/PrivateFrameworks/SoftLinking.framework/Versions/A/SoftLinking",
- "/usr/lib/libc++abi.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/liblaunch.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_product_info_filter.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/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/ColorSyncLegacy.framework/Versions/A/ColorSyncLegacy",
- "/System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices",
- "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/HIServices",
- "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/LangAnalysis.framework/Versions/A/LangAnalysis",
- "/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/SpeechSynthesis.framework/Versions/A/SpeechSynthesis",
- "/System/Library/PrivateFrameworks/SkyLight.framework/Versions/A/SkyLight",
- "/System/Library/PrivateFrameworks/FontServices.framework/libFontParser.dylib",
- "/System/Library/Frameworks/Accelerate.framework/Versions/A/Accelerate",
- "/System/Library/Frameworks/IOSurface.framework/Versions/A/IOSurface",
- "/usr/lib/libxml2.2.dylib",
- "/System/Library/Frameworks/CFNetwork.framework/Versions/A/CFNetwork",
- "/usr/lib/libz.1.dylib",
- "/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation",
- "/System/Library/PrivateFrameworks/RunningBoardServices.framework/Versions/A/RunningBoardServices",
- "/usr/lib/libMobileGestalt.dylib",
- "/System/Library/PrivateFrameworks/WatchdogClient.framework/Versions/A/WatchdogClient",
- "/usr/lib/libcompression.dylib",
- "/usr/lib/libDiagnosticMessagesClient.dylib",
- "/System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration",
- "/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/IOKit.framework/Versions/A/IOKit",
- "/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/Security.framework/Versions/A/Security",
- "/System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore",
- "/usr/lib/libbsm.0.dylib",
- "/System/Library/PrivateFrameworks/CoreAnalytics.framework/Versions/A/CoreAnalytics",
- "/System/Library/Frameworks/VideoToolbox.framework/Versions/A/VideoToolbox",
- "/System/Library/PrivateFrameworks/BaseBoard.framework/Versions/A/BaseBoard",
- "/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",
- "/usr/lib/libapple_nghttp2.dylib",
- "/usr/lib/libnetwork.dylib",
- "/usr/lib/libsqlite3.dylib",
- "/usr/lib/libenergytrace.dylib",
- "/usr/lib/system/libkxld.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/liblangid.dylib",
- "/usr/lib/libCRFSuite.dylib",
- "/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",
- "/System/Library/PrivateFrameworks/AppleSystemInfo.framework/Versions/A/AppleSystemInfo",
- "/System/Library/PrivateFrameworks/IOMobileFramebuffer.framework/Versions/A/IOMobileFramebuffer",
- "/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",
- "/usr/lib/libmecabra.dylib",
- "/System/Library/Frameworks/MLCompute.framework/Versions/A/MLCompute",
- "/usr/lib/libmecab.dylib",
- "/usr/lib/libgermantok.dylib",
- "/usr/lib/libThaiTokenizer.dylib",
- "/usr/lib/libChineseTokenizer.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/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/PrivateFrameworks/MetalTools.framework/Versions/A/MetalTools",
- "/System/Library/PrivateFrameworks/AggregateDictionary.framework/Versions/A/AggregateDictionary",
- "/System/Library/PrivateFrameworks/AppleSauce.framework/Versions/A/AppleSauce",
- "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreFSCache.dylib",
- "/System/Library/PrivateFrameworks/LanguageModeling.framework/Versions/A/LanguageModeling",
- "/System/Library/PrivateFrameworks/CoreEmoji.framework/Versions/A/CoreEmoji",
- "/System/Library/PrivateFrameworks/LinguisticData.framework/Versions/A/LinguisticData",
- "/System/Library/PrivateFrameworks/Lexicon.framework/Versions/A/Lexicon",
- "/usr/lib/libcmph.dylib",
- "/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",
- "/usr/lib/libapp_launch_measurement.dylib",
- "/System/Library/PrivateFrameworks/CoreServicesStore.framework/Versions/A/CoreServicesStore",
- "/System/Library/Frameworks/ServiceManagement.framework/Versions/A/ServiceManagement",
- "/usr/lib/libxslt.1.dylib",
- "/System/Library/PrivateFrameworks/BackgroundTaskManagement.framework/Versions/A/BackgroundTaskManagement",
- "/System/Library/PrivateFrameworks/PersistentConnection.framework/Versions/A/PersistentConnection",
- "/System/Library/PrivateFrameworks/ProtocolBuffer.framework/Versions/A/ProtocolBuffer",
- "/System/Library/PrivateFrameworks/CommonUtilities.framework/Versions/A/CommonUtilities",
- "/System/Library/PrivateFrameworks/Bom.framework/Versions/A/Bom",
- "/usr/lib/libate.dylib",
- "/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libRadiance.dylib",
- "/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJPEG.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/libexpat.1.dylib",
- "/System/Library/PrivateFrameworks/AppleJPEG.framework/Versions/A/AppleJPEG",
- "/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/CMCaptureCore.framework/Versions/A/CMCaptureCore",
- "/usr/lib/libspindump.dylib",
- "/System/Library/Frameworks/CoreAudio.framework/Versions/A/CoreAudio",
- "/System/Library/PrivateFrameworks/AppServerSupport.framework/Versions/A/AppServerSupport",
- "/System/Library/PrivateFrameworks/perfdata.framework/Versions/A/perfdata",
- "/System/Library/PrivateFrameworks/AssertionServices.framework/Versions/A/AssertionServices",
- "/System/Library/PrivateFrameworks/AudioToolboxCore.framework/Versions/A/AudioToolboxCore",
- "/System/Library/PrivateFrameworks/caulk.framework/Versions/A/caulk",
- "/System/Library/PrivateFrameworks/SystemPolicy.framework/Versions/A/SystemPolicy",
- "/usr/lib/libIOReport.dylib",
- "/usr/lib/libSMC.dylib",
- "/usr/lib/libAudioToolboxUtility.dylib",
- "/usr/lib/libmis.dylib",
- "/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",
- "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreVMClient.dylib",
- "/System/Library/Frameworks/CoreImage.framework/Versions/A/CoreImage",
- "/System/Library/Frameworks/OpenCL.framework/Versions/A/OpenCL",
- "/System/Library/PrivateFrameworks/GraphVisualizer.framework/Versions/A/GraphVisualizer",
- "/System/Library/PrivateFrameworks/FaceCore.framework/Versions/A/FaceCore",
- "/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/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/PrivateFrameworks/NetAuth.framework/Versions/A/NetAuth",
- "/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/Frameworks/Network.framework/Versions/A/Network",
- "/usr/lib/libheimdal-asn1.dylib",
- "/System/Library/PrivateFrameworks/CommonAuth.framework/Versions/A/CommonAuth",
- "/System/Library/PrivateFrameworks/login.framework/Versions/A/Frameworks/loginsupport.framework/Versions/A/loginsupport",
- "/System/Library/Frameworks/AudioToolbox.framework/Versions/A/AudioToolbox",
- "/System/Library/PrivateFrameworks/AudioSession.framework/Versions/A/AudioSession",
- "/usr/lib/libAudioStatistics.dylib",
- "/System/Library/PrivateFrameworks/MediaExperience.framework/Versions/A/MediaExperience",
- "/System/Library/PrivateFrameworks/AudioSession.framework/libSessionUtility.dylib",
- "/usr/lib/libperfcheck.dylib",
- "/System/Library/PrivateFrameworks/AudioResourceArbitration.framework/Versions/A/AudioResourceArbitration",
- "/System/Library/Frameworks/CoreData.framework/Versions/A/CoreData",
- "/Users/sarah/Desktop/dash/Dash-Web/node_modules/fsevents/build/Release/fse.node"
- ]
-} \ No newline at end of file
diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts
index 4b3960902..6bde7989b 100644
--- a/src/client/apis/gpt/GPT.ts
+++ b/src/client/apis/gpt/GPT.ts
@@ -14,7 +14,7 @@ type GPTCallOpts = {
};
const callTypeMap: { [type: string]: GPTCallOpts } = {
- summary: { model: 'text-davinci-003', maxTokens: 256, temp: 0.5, prompt: 'Summarize this text briefly: ' },
+ summary: { model: 'text-davinci-003', maxTokens: 256, temp: 0.5, prompt: 'Summarize this text in simpler terms: ' },
edit: { model: 'text-davinci-003', maxTokens: 256, temp: 0.5, prompt: 'Reword this: ' },
completion: { model: 'text-davinci-003', maxTokens: 256, temp: 0.5, prompt: '' },
};
@@ -39,7 +39,6 @@ const gptAPICall = async (inputText: string, callType: GPTCallType) => {
temperature: opts.temp,
prompt: `${opts.prompt}${inputText}`,
});
- console.log(response.data.choices[0]);
return response.data.choices[0].text;
} catch (err) {
console.log(err);
@@ -47,7 +46,7 @@ const gptAPICall = async (inputText: string, callType: GPTCallType) => {
}
};
-const gptImageCall = async (prompt: string) => {
+const gptImageCall = async (prompt: string, n?: number) => {
try {
const configuration = new Configuration({
apiKey: process.env.OPENAI_KEY,
@@ -55,33 +54,15 @@ const gptImageCall = async (prompt: string) => {
const openai = new OpenAIApi(configuration);
const response = await openai.createImage({
prompt: prompt,
- n: 1,
+ n: n ?? 1,
size: '1024x1024',
});
- return response.data.data[0].url;
+ return response.data.data.map(data => data.url);
+ // return response.data.data[0].url;
} catch (err) {
console.error(err);
return;
}
};
-// const gptEditCall = async (selectedText: string, fullText: string) => {
-// try {
-// const configuration = new Configuration({
-// apiKey: process.env.OPENAI_KEY,
-// });
-// const openai = new OpenAIApi(configuration);
-// const response = await openai.createCompletion({
-// model: 'text-davinci-003',
-// max_tokens: 256,
-// temperature: 0.1,
-// prompt: `Replace the phrase ${selectedText} inside of ${fullText}.`,
-// });
-// return response.data.choices[0].text.trim();
-// } catch (err) {
-// console.log(err);
-// return 'Error connecting with API.';
-// }
-// };
-
export { gptAPICall, gptImageCall, GPTCallType };
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index e6a6cdf58..af8cc07ed 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -686,7 +686,7 @@ export namespace Docs {
DocumentType.DATAVIZ,
{
layout: { view: DataVizBox, dataField: defaultDataKey },
- options: { dataViz_title: '', _layout_fitWidth: true, nativeDimModifiable: true },
+ options: { dataViz_title: '', dataViz: 'table', _layout_fitWidth: true, nativeDimModifiable: true },
},
],
[
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index d9aad5a6f..672f7d99f 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -629,12 +629,14 @@ export class CurrentUserUtils {
}
static viewTools(): Button[] {
return [
- { title: "Snap", icon: "th", toolTip: "Show Snap Lines", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"snaplines", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
- { title: "Grid", icon: "border-all", toolTip: "Show Grid", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"grid", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
- { title: "View All", icon: "object-group", toolTip: "Fit all Docs to View", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"viewAll", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
- { title: "Clusters", icon: "braille", toolTip: "Show Doc Clusters", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"clusters", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
- { title: "Cards", icon: "brain", toolTip: "Flashcards", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"flashcards", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
- { title: "Arrange",icon:"arrow-down-short-wide",toolTip:"Toggle Auto Arrange", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"arrange", funcs: {hidden: 'IsNoviceMode()'}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
+ { title: "Snap", icon: "th", toolTip: "Show Snap Lines", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"snaplines", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
+ { title: "Grid", icon: "border-all", toolTip: "Show Grid", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"grid", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
+ { title: "View All", icon: "object-group", toolTip: "Fit all Docs to View",btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"viewAll", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
+ // want the same style as toggle button, but don't want it to act as an actual toggle, so set disableToggle to true,
+ { title: "Fit All", icon: "arrows-left-right", toolTip: "Fit all Docs to View (persistent)", btnType: ButtonType.ClickButton, ignoreClick: false, expertMode: false, toolType:"viewAllPersist", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
+ { title: "Clusters", icon: "braille", toolTip: "Show Doc Clusters", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"clusters", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
+ { title: "Cards", icon: "brain", toolTip: "Flashcards", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"flashcards", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
+ { title: "Arrange", icon:"arrow-down-short-wide",toolTip:"Toggle Auto Arrange", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"arrange", funcs: {hidden: 'IsNoviceMode()'}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
]
}
static textTools():Button[] {
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index f4ff38515..489c9df4a 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -195,7 +195,7 @@ export namespace DragManager {
}
// drag a document and drop it (or make an embed/copy on drop)
- export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions, dropEvent?: () => any) {
+ export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions, onDropCompleted?: (e?: DragCompleteEvent) => any) {
const addAudioTag = (dropDoc: any) => {
dropDoc && !dropDoc.author_date && (dropDoc.author_date = new DateField());
dropDoc instanceof Doc && DocUtils.MakeLinkToActiveAudio(() => dropDoc);
@@ -203,7 +203,7 @@ export namespace DragManager {
};
const finishDrag = async (e: DragCompleteEvent) => {
const docDragData = e.docDragData;
- dropEvent?.(); // glr: optional additional function to be called - in this case with presentation trails
+ onDropCompleted?.(e); // glr: optional additional function to be called - in this case with presentation trails
if (docDragData && !docDragData.droppedDocuments.length) {
docDragData.dropAction = dragData.userDropAction || dragData.dropAction;
docDragData.droppedDocuments = (
diff --git a/src/client/util/RTFMarkup.tsx b/src/client/util/RTFMarkup.tsx
index a0fc617ab..78069d323 100644
--- a/src/client/util/RTFMarkup.tsx
+++ b/src/client/util/RTFMarkup.tsx
@@ -3,6 +3,8 @@ import { observer } from 'mobx-react';
import * as React from 'react';
import { MainViewModal } from '../views/MainViewModal';
import { SettingsManager } from './SettingsManager';
+import { Doc } from '../../fields/Doc';
+import { StrCast } from '../../fields/Types';
@observer
export class RTFMarkup extends React.Component<{}> {
@@ -133,6 +135,14 @@ export class RTFMarkup extends React.Component<{}> {
}
render() {
- return <MainViewModal contents={this.cheatSheet} isDisplayed={this.isOpen} interactive={true} closeOnExternalClick={this.close} />;
+ return (
+ <MainViewModal
+ dialogueBoxStyle={{ backgroundColor: StrCast(Doc.UserDoc().userBackgroundColor), color: StrCast(Doc.UserDoc().userColor), padding: '16px' }}
+ contents={this.cheatSheet}
+ isDisplayed={this.isOpen}
+ interactive={true}
+ closeOnExternalClick={this.close}
+ />
+ );
}
}
diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx
index daa2c152a..b53379435 100644
--- a/src/client/views/ContextMenuItem.tsx
+++ b/src/client/views/ContextMenuItem.tsx
@@ -6,12 +6,13 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { UndoManager } from '../util/UndoManager';
import { Doc } from '../../fields/Doc';
import { StrCast } from '../../fields/Types';
+import { SettingsManager } from '../util/SettingsManager';
export interface OriginalMenuProps {
description: string;
event: (stuff?: any) => void;
undoable?: boolean;
- icon: IconProp; //maybe should be optional (icon?)
+ icon: IconProp | JSX.Element; //maybe should be optional (icon?)
closeMenu?: () => void;
}
@@ -82,19 +83,20 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
);
};
+ isJSXElement(val: any): val is JSX.Element {
+ return React.isValidElement(val);
+ }
+
render() {
if ('event' in this.props) {
return (
<div className={'contextMenu-item' + (this.props.selected ? ' contextMenu-itemSelected' : '')} onPointerDown={this.handleEvent}>
- {this.props.icon ? (
- <span className="contextMenu-item-icon-background">
- <FontAwesomeIcon icon={this.props.icon} size="sm" />
- </span>
- ) : null}
+ {this.props.icon ? <span className="contextMenu-item-icon-background">{this.isJSXElement(this.props.icon) ? this.props.icon : <FontAwesomeIcon icon={this.props.icon} size="sm" />}</span> : null}
<div className="contextMenu-description">{this.props.description.replace(':', '')}</div>
- <div className={`contextMenu-item-background`}
+ <div
+ className={`contextMenu-item-background`}
style={{
- background: StrCast(Doc.UserDoc().userColor)
+ background: SettingsManager.Instance.userColor,
}}
/>
</div>
@@ -110,7 +112,7 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
style={{
marginLeft: window.innerHeight - this._overPosX - 50 > 0 ? '90%' : '20%',
marginTop,
- background: StrCast(Doc.UserDoc().userBackgroundColor)
+ background: SettingsManager.Instance.userBackgroundColor,
}}>
{this._items.map(prop => (
<ContextMenuItem {...prop} key={prop.description} closeMenu={this.props.closeMenu} />
@@ -141,9 +143,10 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
{this.props.description}
<FontAwesomeIcon icon={'angle-right'} size="lg" style={{ position: 'absolute', right: '10px' }} />
</div>
- <div className={`contextMenu-item-background`}
+ <div
+ className={`contextMenu-item-background`}
style={{
- background: StrCast(Doc.UserDoc().userColor)
+ background: SettingsManager.Instance.userColor,
}}
/>
{submenu}
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 5a821faee..231a2d5fb 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -176,7 +176,7 @@ export class DashboardView extends React.Component {
</div>
<div className="all-dashboards">
{this.getDashboards(this.selectedDashboardGroup).map(dashboard => {
- const href = ImageCast(dashboard.thumb)?.url.href;
+ const href = ImageCast(dashboard.thumb)?.url?.href;
const shared = Object.keys(dashboard[DocAcl])
.filter(key => key !== `acl-${Doc.CurrentUserEmailNormalized}` && !['acl-Me', 'acl-Guest'].includes(key))
.some(key => dashboard[DocAcl][key] !== AclPrivate);
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index b088157e5..344a401f3 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -55,6 +55,8 @@ import { DocumentView, DocumentViewInternal, OpenWhere, OpenWhereMod } from './n
import { DashFieldViewMenu } from './nodes/formattedText/DashFieldView';
import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
import { RichTextMenu } from './nodes/formattedText/RichTextMenu';
+import GenerativeFill from './nodes/generativeFill/GenerativeFill';
+import { ImageBox } from './nodes/ImageBox';
import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup';
import { LinkDocPreview } from './nodes/LinkDocPreview';
import { MapAnchorMenu } from './nodes/MapBox/MapAnchorMenu';
@@ -62,6 +64,7 @@ import { RadialMenu } from './nodes/RadialMenu';
import { TaskCompletionBox } from './nodes/TaskCompletedBox';
import { OverlayView } from './OverlayView';
import { AnchorMenu } from './pdf/AnchorMenu';
+import { GPTPopup } from './pdf/GPTPopup/GPTPopup';
import { PreviewCursor } from './PreviewCursor';
import { PropertiesView } from './PropertiesView';
import { DashboardStyleProvider, DefaultStyleProvider } from './StyleProvider';
@@ -73,6 +76,7 @@ export class MainView extends React.Component {
public static Instance: MainView;
public static Live: boolean = false;
private _docBtnRef = React.createRef<HTMLDivElement>();
+
@observable public LastButton: Opt<Doc>;
@observable private _windowWidth: number = 0;
@observable private _windowHeight: number = 0;
@@ -340,6 +344,7 @@ export class MainView extends React.Component {
fa.faMousePointer,
fa.faMusic,
fa.faObjectGroup,
+ fa.faArrowsLeftRight,
fa.faPause,
fa.faPen,
fa.faPenNib,
@@ -443,6 +448,7 @@ export class MainView extends React.Component {
fa.faSortUp,
fa.faSortDown,
fa.faTable,
+ fa.faTableColumns,
fa.faTh,
fa.faThList,
fa.faProjectDiagram,
@@ -1008,12 +1014,14 @@ export class MainView extends React.Component {
<MapAnchorMenu/>
<DashFieldViewMenu />
<MarqueeOptionsMenu />
- <OverlayView />
<TimelineMenu />
<RichTextMenu />
<InkTranscription />
{this.snapLines}
<LightboxView key="lightbox" PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} />
+ <OverlayView />
+ <GPTPopup key="gptpopup" />
+ <GenerativeFill imageEditorOpen={ImageBox.imageEditorOpen} imageEditorSource={ImageBox.imageEditorSource} imageRootDoc={ImageBox.imageRootDoc} addDoc={ImageBox.addDoc} />
{/* <NewLightboxView key="newLightbox" PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} /> */}
</div>
);
diff --git a/src/client/views/OverlayView.scss b/src/client/views/OverlayView.scss
index 5362bf9f0..9b10d1cf7 100644
--- a/src/client/views/OverlayView.scss
+++ b/src/client/views/OverlayView.scss
@@ -4,6 +4,7 @@
top: 0;
width: 100vw;
height: 100vh;
+ z-index: 1001; // shouold be greater than LightboxView's z-index so that link lines and the presentation mini player appear
/* background-color: pink; */
}
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 339507f65..62aa4d1d4 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -1,3 +1,4 @@
+
import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import { computedFn } from 'mobx-utils';
@@ -7,14 +8,12 @@ import { Doc, DocListCast } from '../../fields/Doc';
import { Height, Width } from '../../fields/DocSymbols';
import { Id } from '../../fields/FieldSymbols';
import { NumCast } from '../../fields/Types';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, Utils } from '../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnTrue, setupMoveUpEvents, Utils } from '../../Utils';
import { DocumentType } from '../documents/DocumentTypes';
-import { DocumentManager } from '../util/DocumentManager';
import { DragManager } from '../util/DragManager';
import { Transform } from '../util/Transform';
import { CollectionFreeFormLinksView } from './collections/collectionFreeForm/CollectionFreeFormLinksView';
import { LightboxView } from './LightboxView';
-import { MainView } from './MainView';
import { DocumentView, DocumentViewInternal } from './nodes/DocumentView';
import './OverlayView.scss';
import { DefaultStyleProvider } from './StyleProvider';
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 95f88f14e..e15d57306 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -551,7 +551,7 @@ export class CollectionDockingView extends CollectionSubView() {
};
render() {
- const href = ImageCast(this.rootDoc.thumb)?.url.href;
+ const href = ImageCast(this.rootDoc.thumb)?.url?.href;
return this.props.renderDepth > -1 ? (
<div>
{href ? (
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index ea473d5cf..f379d09ff 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -137,7 +137,11 @@ export class TabDocView extends React.Component<TabDocViewProps> {
setupMoveUpEvents(
this,
e,
- e => !e.defaultPrevented && DragManager.StartDocumentDrag([iconWrap], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY),
+ e =>
+ !e.defaultPrevented &&
+ DragManager.StartDocumentDrag([iconWrap], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY, undefined, () => {
+ CollectionDockingView.CloseSplit(doc);
+ }),
returnFalse,
action(e => {
if (this.view) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index fd9aa3fa5..89deb733a 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -21,6 +21,8 @@ export interface CollectionFreeFormLinkViewProps {
LinkDocs: Doc[];
}
+// props.screentolocatransform
+
@observer
export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFormLinkViewProps> {
@observable _opacity: number = 0;
@@ -235,11 +237,12 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
bActive,
textX,
textY,
- // pt1,
- // pt2,
+ // fully connected
+ pt1,
+ pt2,
// this code adds space between links
- pt1: [pt1[0] + pt1normalized[0] * 13, pt1[1] + pt1normalized[1] * 13],
- pt2: [pt2[0] + pt2normalized[0] * 13, pt2[1] + pt2normalized[1] * 13],
+ // pt1: [pt1[0] + pt1normalized[0] * 13, pt1[1] + pt1normalized[1] * 13],
+ // pt2: [pt2[0] + pt2normalized[0] * 13, pt2[1] + pt2normalized[1] * 13],
};
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index ffcf0999c..f5cc1eb53 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -237,7 +237,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
onChildClickHandler = () => this.props.childClickScript || ScriptCast(this.Document.onChildClick);
onChildDoubleClickHandler = () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick);
elementFunc = () => this._layoutElements;
- shrinkWrap = () => {
+ fitContentOnce = () => {
if (this.props.DocumentView?.().nativeWidth) return;
const vals = this.fitToContentVals;
this.layoutDoc._freeform_panX = vals.bounds.cx;
@@ -1595,6 +1595,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
elements => (this._layoutElements = elements || []),
{ fireImmediately: true, name: 'doLayout' }
);
+
+ this._disposers.fitContent = reaction(
+ () => this.rootDoc.fitContentOnce,
+ fitContentOnce => {
+ if (fitContentOnce) this.fitContentOnce();
+ this.rootDoc.fitContentOnce = undefined;
+ },
+ { fireImmediately: true, name: 'fitContent' }
+ );
})
);
}
@@ -1785,6 +1794,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
!Doc.noviceMode &&
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',
+ });
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/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index 256377758..38bf1042d 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -85,10 +85,10 @@ ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) {
selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log('[FontIconBox.tsx] toggleOverlay failed');
});
-ScriptingGlobals.add(function showFreeform(attr: 'flashcards' | 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll', checkResult?: boolean) {
+ScriptingGlobals.add(function showFreeform(attr: 'flashcards' | 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll' | 'viewAllPersist', checkResult?: boolean) {
const selected = SelectionManager.Docs().lastElement();
// prettier-ignore
- const map: Map<'flashcards' | 'grid' | 'snaplines' | 'clusters' | 'arrange'| 'viewAll', { waitForRender?: boolean, checkResult: (doc:Doc) => any; setDoc: (doc:Doc) => void;}> = new Map([
+ const map: Map<'flashcards' | 'grid' | 'snaplines' | 'clusters' | 'arrange'| 'viewAll' | 'viewAllPersist', { waitForRender?: boolean, checkResult: (doc:Doc) => any; setDoc: (doc:Doc) => void;}> = new Map([
['grid', {
checkResult: (doc:Doc) => BoolCast(doc._freeform_backgroundGrid, false),
setDoc: (doc:Doc) => doc._freeform_backgroundGrid = !doc._freeform_backgroundGrid,
@@ -101,6 +101,10 @@ ScriptingGlobals.add(function showFreeform(attr: 'flashcards' | 'grid' | 'snapli
checkResult: (doc:Doc) => BoolCast(doc._freeform_fitContentsToBox, false),
setDoc: (doc:Doc) => doc._freeform_fitContentsToBox = !doc._freeform_fitContentsToBox,
}],
+ ['viewAllPersist', {
+ checkResult: (doc:Doc) => false,
+ setDoc: (doc:Doc) => doc.fitContentOnce = true
+ }],
['clusters', {
waitForRender: true, // flags that undo batch should terminate after a re-render giving the script the chance to fire
checkResult: (doc:Doc) => BoolCast(doc._freeform_useClusters, false),
diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
index c07ab5ba1..f9f241234 100644
--- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx
+++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
@@ -1,3 +1,4 @@
+import { Toggle, ToggleType, Type } from 'browndash-components';
import { action, computed, ObservableMap } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
@@ -9,12 +10,11 @@ import { Docs } from '../../../documents/Documents';
import { ViewBoxAnnotatableComponent } from '../../DocComponent';
import { FieldView, FieldViewProps } from '../FieldView';
import { PinProps } from '../trails';
+import { Histogram } from './components/Histogram';
import { LineChart } from './components/LineChart';
+import { PieChart } from './components/PieChart';
import { TableBox } from './components/TableBox';
import './DataVizBox.scss';
-import { Histogram } from './components/Histogram';
-import { PieChart } from './components/PieChart';
-import { Toggle, ToggleType, Type } from 'browndash-components';
export enum DataVizView {
TABLE = 'table',
@@ -29,19 +29,29 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
return FieldView.LayoutString(DataVizBox, fieldStr);
}
- // all data
- static pairSet = new ObservableMap<string, { [key: string]: string }[]>();
- @computed.struct get pairs() {
- return DataVizBox.pairSet.get(CsvCast(this.rootDoc[this.fieldKey]).url.href);
- }
+ // all datasets that have been retrieved from the server stored as a map from the dataset url to an array of records
+ static dataset = new ObservableMap<string, { [key: string]: string }[]>();
+ private _vizRenderer: LineChart | Histogram | PieChart | undefined;
- private _chartRenderer: LineChart | Histogram | PieChart | undefined;
+ // all CSV records in the dataset
+ @computed.struct get records() {
+ return DataVizBox.dataset.get(CsvCast(this.rootDoc[this.fieldKey]).url.href);
+ }
- // current displayed chart type
+ // currently chosen visualization type: line, pie, histogram, table
@computed get dataVizView(): DataVizView {
return StrCast(this.layoutDoc._dataViz, 'table') as DataVizView;
}
+ @computed get dataUrl() {
+ return Cast(this.dataDoc[this.fieldKey], CsvField);
+ }
+ @computed.struct get axes() {
+ return StrListCast(this.layoutDoc.dataViz_axes);
+ }
+
+ selectAxes = (axes: string[]) => (this.layoutDoc.dataViz_axes = new List<string>(axes));
+
@action // pinned / linked anchor doc includes selected rows, graph titles, and graph colors
restoreView = (data: Doc) => {
const changedView = this.dataVizView !== data.config_dataViz && (this.layoutDoc._dataViz = data.config_dataViz);
@@ -55,7 +65,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
this.layoutDoc['_' + key] = data[key];
}
});
- const func = () => this._chartRenderer?.restoreView(data);
+ const func = () => this._vizRenderer?.restoreView(data);
if (changedView || changedAxes) {
setTimeout(func, 100);
return true;
@@ -65,7 +75,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
getAnchor = (addAsAnnotation?: boolean, pinProps?: PinProps) => {
const anchor = !pinProps
? this.rootDoc
- : this._chartRenderer?.getAnchor(pinProps) ??
+ : this._vizRenderer?.getAnchor(pinProps) ??
Docs.Create.ConfigDocument({
// when we clear selection -> we should have it so chartBox getAnchor returns undefined
// this is for when we want the whole doc (so when the chartBox getAnchor returns without a marker)
@@ -86,87 +96,78 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
return anchor;
};
- @computed.struct get axes() {
- return StrListCast(this.layoutDoc.dataViz_axes);
+ componentDidMount() {
+ this.props.setContentView?.(this);
+ if (!DataVizBox.dataset.has(CsvCast(this.rootDoc[this.fieldKey]).url.href)) this.fetchData();
}
- selectAxes = (axes: string[]) => (this.layoutDoc.dataViz_axes = new List<string>(axes));
+
+ fetchData = () => {
+ DataVizBox.dataset.set(CsvCast(this.rootDoc[this.fieldKey]).url.href, []); // assign temporary dataset as a lock to prevent duplicate server requests
+ fetch('/csvData?uri=' + this.dataUrl?.url.href) //
+ .then(res => res.json().then(action(res => !res.errno && DataVizBox.dataset.set(CsvCast(this.rootDoc[this.fieldKey]).url.href, res))));
+ };
// toggles for user to decide which chart type to view the data in
- @computed get selectView() {
+ renderVizView = () => {
const width = this.props.PanelWidth() * 0.9;
const height = (this.props.PanelHeight() - 32) /* height of 'change view' button */ * 0.9;
const margin = { top: 10, right: 25, bottom: 75, left: 45 };
- if (!this.pairs) return 'no data';
- switch (this.dataVizView) {
- case DataVizView.TABLE:
- return <TableBox layoutDoc={this.layoutDoc} pairs={this.pairs} axes={this.axes} height={height} width={width} margin={margin} rootDoc={this.rootDoc} docView={this.props.DocumentView} selectAxes={this.selectAxes} />;
- case DataVizView.LINECHART:
- return (
- <LineChart
- layoutDoc={this.layoutDoc}
- ref={r => (this._chartRenderer = r ?? undefined)}
- height={height}
- width={width}
- fieldKey={this.fieldKey}
- margin={margin}
- rootDoc={this.rootDoc}
- axes={this.axes}
- pairs={this.pairs}
- dataDoc={this.dataDoc}
- />
- );
- case DataVizView.HISTOGRAM:
- return (
- <Histogram
- layoutDoc={this.layoutDoc}
- ref={r => (this._chartRenderer = r ?? undefined)}
- height={height}
- width={width}
- fieldKey={this.fieldKey}
- margin={margin}
- rootDoc={this.rootDoc}
- axes={this.axes}
- pairs={this.pairs}
- dataDoc={this.dataDoc}
- />
- );
- case DataVizView.PIECHART:
- return (
- <PieChart
- layoutDoc={this.layoutDoc}
- ref={r => (this._chartRenderer = r ?? undefined)}
- height={height}
- width={width}
- fieldKey={this.fieldKey}
- margin={margin}
- rootDoc={this.rootDoc}
- axes={this.axes}
- pairs={this.pairs}
- dataDoc={this.dataDoc}
- />
- );
+ if (this.records) {
+ switch (this.dataVizView) {
+ case DataVizView.TABLE:
+ return <TableBox layoutDoc={this.layoutDoc} records={this.records} axes={this.axes} height={height} width={width} margin={margin} rootDoc={this.rootDoc} docView={this.props.DocumentView} selectAxes={this.selectAxes} />;
+ case DataVizView.LINECHART:
+ return (
+ <LineChart
+ layoutDoc={this.layoutDoc}
+ ref={r => (this._vizRenderer = r ?? undefined)}
+ height={height}
+ width={width}
+ fieldKey={this.fieldKey}
+ margin={margin}
+ rootDoc={this.rootDoc}
+ axes={this.axes}
+ records={this.records}
+ dataDoc={this.dataDoc}
+ />
+ );
+ case DataVizView.HISTOGRAM:
+ return (
+ <Histogram
+ layoutDoc={this.layoutDoc}
+ ref={r => (this._vizRenderer = r ?? undefined)}
+ height={height}
+ width={width}
+ fieldKey={this.fieldKey}
+ margin={margin}
+ rootDoc={this.rootDoc}
+ axes={this.axes}
+ records={this.records}
+ dataDoc={this.dataDoc}
+ />
+ );
+ case DataVizView.PIECHART:
+ return (
+ <PieChart
+ layoutDoc={this.layoutDoc}
+ ref={r => (this._vizRenderer = r ?? undefined)}
+ height={height}
+ width={width}
+ fieldKey={this.fieldKey}
+ margin={margin}
+ rootDoc={this.rootDoc}
+ axes={this.axes}
+ records={this.records}
+ dataDoc={this.dataDoc}
+ />
+ );
+ }
}
- }
-
- @computed get dataUrl() {
- return Cast(this.dataDoc[this.fieldKey], CsvField);
- }
-
- componentDidMount() {
- this.props.setContentView?.(this);
- this.fetchData();
- if (!this.layoutDoc._dataViz) this.layoutDoc._dataViz = this.dataVizView;
- }
-
- fetchData() {
- if (DataVizBox.pairSet.has(CsvCast(this.rootDoc[this.fieldKey]).url.href)) return;
- DataVizBox.pairSet.set(CsvCast(this.rootDoc[this.fieldKey]).url.href, []);
- fetch('/csvData?uri=' + this.dataUrl?.url.href) //
- .then(res => res.json().then(action(res => !res.errno && DataVizBox.pairSet.set(CsvCast(this.rootDoc[this.fieldKey]).url.href, res))));
- }
+ return 'no data/visualization';
+ };
render() {
- return !this.pairs?.length ? (
+ return !this.records?.length ? (
// displays how to get data into the DataVizBox if its empty
<div className="start-message">To create a DataViz box, either import / drag a CSV file into your canvas or copy a data table and use the command 'ctrl + p' to bring the data table to your canvas.</div>
) : (
@@ -189,7 +190,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
<Toggle text={'HISTOGRAM'} toggleType={ToggleType.BUTTON} type={Type.SEC} color={'black'} onClick={e => (this.layoutDoc._dataViz = DataVizView.HISTOGRAM)} toggleStatus={this.layoutDoc._dataViz == DataVizView.HISTOGRAM} />
<Toggle text={'PIE CHART'} toggleType={ToggleType.BUTTON} type={Type.SEC} color={'black'} onClick={e => (this.layoutDoc._dataViz = DataVizView.PIECHART)} toggleStatus={this.layoutDoc._dataViz == DataVizView.PIECHART} />
</div>
- {this.selectView}
+ {this.renderVizView()}
</div>
);
}
diff --git a/src/client/views/nodes/DataVizBox/components/Histogram.tsx b/src/client/views/nodes/DataVizBox/components/Histogram.tsx
index b3bdccbbb..50facf03e 100644
--- a/src/client/views/nodes/DataVizBox/components/Histogram.tsx
+++ b/src/client/views/nodes/DataVizBox/components/Histogram.tsx
@@ -1,26 +1,26 @@
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { ColorPicker, EditableText, IconButton, Size, Type } from 'browndash-components';
+import * as d3 from 'd3';
+import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
-import { Doc, StrListCast } from '../../../../../fields/Doc';
import * as React from 'react';
-import * as d3 from 'd3';
-import { IReactionDisposer, action, computed, observable, reaction } from 'mobx';
-import { LinkManager } from '../../../../util/LinkManager';
-import { Cast, DocCast, StrCast } from '../../../../../fields/Types';
-import { PinProps, PresBox } from '../../trails';
-import { Docs } from '../../../../documents/Documents';
-import { List } from '../../../../../fields/List';
-import './Chart.scss';
-import { ColorPicker, EditableText, IconButton, Size, Type } from 'browndash-components';
import { FaFillDrip } from 'react-icons/fa';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Doc, NumListCast, StrListCast } from '../../../../../fields/Doc';
+import { List } from '../../../../../fields/List';
import { listSpec } from '../../../../../fields/Schema';
+import { Cast, DocCast, StrCast } from '../../../../../fields/Types';
+import { Docs } from '../../../../documents/Documents';
+import { LinkManager } from '../../../../util/LinkManager';
+import { undoable } from '../../../../util/UndoManager';
+import { PinProps, PresBox } from '../../trails';
import { scaleCreatorNumerical, yAxisCreator } from '../utils/D3Utils';
-import { undoBatch, undoable } from '../../../../util/UndoManager';
+import './Chart.scss';
export interface HistogramProps {
rootDoc: Doc;
layoutDoc: Doc;
axes: string[];
- pairs: { [key: string]: any }[];
+ records: { [key: string]: any }[];
width: number;
height: number;
dataDoc: Doc;
@@ -46,44 +46,47 @@ export class Histogram extends React.Component<HistogramProps> {
private selectedData: any = undefined; // Selection of selected bar
private hoverOverData: any = undefined; // Selection of bar being hovered over
+ @computed get _tableDataIds() {
+ return !this.parentViz ? this.props.records.map((rec, i) => i) : NumListCast(this.parentViz.dataViz_selectedRows);
+ }
+ // returns all the data records that will be rendered by only returning those records that have been selected by the parent visualization (or all records if there is no parent)
+ @computed get _tableData() {
+ return !this.parentViz ? this.props.records : this._tableDataIds.map(rowId => this.props.records[rowId]);
+ }
// filters all data to just display selected data if brushed (created from an incoming link)
@computed get _histogramData() {
- var guids = StrListCast(this.props.layoutDoc.dataViz_rowGuids);
if (this.props.axes.length < 1) return [];
if (this.props.axes.length < 2) {
var ax0 = this.props.axes[0];
- if (/\d/.test(this.props.pairs[0][ax0])) {
+ if (/\d/.test(this.props.records[0][ax0])) {
this.numericalXData = true;
}
- return this.props.pairs
- ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.dataViz_selectedRows && StrListCast(this.incomingLinks[0].dataViz_selectedRows).includes(guids[this.props.pairs.indexOf(pair)])))
- .map(pair => ({ [ax0]: pair[this.props.axes[0]] }));
+ return this._tableData.map(record => ({ [ax0]: record[this.props.axes[0]] }));
}
var ax0 = this.props.axes[0];
var ax1 = this.props.axes[1];
- if (/\d/.test(this.props.pairs[0][ax0])) {
+ if (/\d/.test(this.props.records[0][ax0])) {
this.numericalXData = true;
}
- if (/\d/.test(this.props.pairs[0][ax1])) {
+ if (/\d/.test(this.props.records[0][ax1])) {
this.numericalYData = true;
}
- return this.props.pairs
- ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.dataViz_selectedRows && StrListCast(this.incomingLinks[0].dataViz_selectedRows).includes(guids[this.props.pairs.indexOf(pair)])))
- .map(pair => ({ [ax0]: pair[this.props.axes[0]], [ax1]: pair[this.props.axes[1]] }));
+ return this._tableData.map(record => ({ [ax0]: record[this.props.axes[0]], [ax1]: record[this.props.axes[1]] }));
}
@computed get defaultGraphTitle() {
var ax0 = this.props.axes[0];
var ax1 = this.props.axes.length > 1 ? this.props.axes[1] : undefined;
- if (this.props.axes.length < 2 || !ax1 || !/\d/.test(this.props.pairs[0][ax1]) || !this.numericalYData) {
+ if (this.props.axes.length < 2 || !ax1 || !/\d/.test(this.props.records[0][ax1]) || !this.numericalYData) {
return ax0 + ' Histogram';
} else return ax0 + ' by ' + ax1 + ' Histogram';
}
- @computed get incomingLinks() {
- return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links
- .filter(link => link.link_anchor_1 == this.props.rootDoc.draggedFrom) // get links where this chart doc is the target of the link
- .map(link => DocCast(link.link_anchor_1)); // then return the source of the link
+ @computed get parentViz() {
+ return DocCast(this.props.rootDoc.dataViz_parentViz);
+ // return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links
+ // .filter(link => link.link_anchor_1 == this.props.rootDoc.dataViz_parentViz) // get links where this chart doc is the target of the link
+ // .map(link => DocCast(link.link_anchor_1)); // then return the source of the link
}
@computed get rangeVals(): { xMin?: number; xMax?: number; yMin?: number; yMax?: number } {
@@ -100,11 +103,7 @@ export class Histogram extends React.Component<HistogramProps> {
componentDidMount = () => {
this._disposers.chartData = reaction(
() => ({ dataSet: this._histogramData, w: this.width, h: this.height }),
- ({ dataSet, w, h }) => {
- if (dataSet!.length > 0) {
- this.drawChart(dataSet, w, h);
- }
- },
+ ({ dataSet, w, h }) => dataSet!.length > 0 && this.drawChart(dataSet, w, h),
{ fireImmediately: true }
);
};
@@ -114,7 +113,6 @@ export class Histogram extends React.Component<HistogramProps> {
// create a document anchor that stores whatever is needed to reconstruct the viewing state (selection,zoom,etc)
getAnchor = (pinProps?: PinProps) => {
const anchor = Docs.Create.ConfigDocument({
- //
title: 'histogram doc selection' + this._currSelected,
});
PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.rootDoc);
@@ -131,21 +129,15 @@ export class Histogram extends React.Component<HistogramProps> {
// cleans data by converting numerical data to numbers and taking out empty cells
data = (dataSet: any) => {
- var validData = dataSet.filter((d: { [x: string]: unknown }) => {
- var valid = true;
- Object.keys(dataSet[0]).map(key => {
- if (!d[key] || Number.isNaN(d[key])) valid = false;
- });
- return valid;
- });
- var field = dataSet[0] ? Object.keys(dataSet[0])[0] : undefined;
- const data = validData.map((d: { [x: string]: any }) => {
- if (this.numericalXData) {
- return +d[field!].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '');
- }
- return d[field!];
- });
- return data;
+ var validData = dataSet.filter((d: { [x: string]: unknown }) => !Object.keys(dataSet[0]).some(key => !d[key] || Number.isNaN(d[key])));
+ const field = dataSet[0] ? Object.keys(dataSet[0])[0] : undefined;
+ return !field
+ ? []
+ : validData.map((d: { [x: string]: any }) =>
+ !this.numericalXData //
+ ? d[field]
+ : +d[field!].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '')
+ );
};
// outlines the bar selected / hovered over
@@ -185,24 +177,18 @@ export class Histogram extends React.Component<HistogramProps> {
d3.select(this._histogramRef.current).select('svg').remove();
d3.select(this._histogramRef.current).select('.tooltip').remove();
- var data = this.data(dataSet);
- var xAxisTitle = Object.keys(dataSet[0])[0];
- var yAxisTitle = this.numericalYData ? Object.keys(dataSet[0])[1] : 'frequency';
- let uniqueArr: unknown[] = [...new Set(data)];
+ const data = this.data(dataSet);
+ const xAxisTitle = Object.keys(dataSet[0])[0];
+ const yAxisTitle = this.numericalYData ? Object.keys(dataSet[0])[1] : 'frequency';
+ const uniqueArr: unknown[] = [...new Set(data)];
var numBins = this.numericalXData && Number.isInteger(data[0]) ? this.rangeVals.xMax! - this.rangeVals.xMin! : uniqueArr.length;
var translateXAxis = !this.numericalXData || numBins < this.maxBins ? width / (numBins + 1) / 2 : 0;
if (numBins > this.maxBins) numBins = this.maxBins;
- var startingPoint = this.numericalXData ? this.rangeVals.xMin! : 0;
- var endingPoint = this.numericalXData ? this.rangeVals.xMax! : numBins;
+ const startingPoint = this.numericalXData ? this.rangeVals.xMin! : 0;
+ const endingPoint = this.numericalXData ? this.rangeVals.xMax! : numBins;
// converts data into Objects
- var histDataSet = dataSet.filter((d: { [x: string]: unknown }) => {
- var valid = true;
- Object.keys(dataSet[0]).map(key => {
- if (!d[key] || Number.isNaN(d[key])) valid = false;
- });
- return valid;
- });
+ var histDataSet = dataSet.filter((d: { [x: string]: unknown }) => !Object.keys(dataSet[0]).some(key => !d[key] || Number.isNaN(d[key])));
if (!this.numericalXData) {
var histStringDataSet: { [x: string]: unknown }[] = [];
if (this.numericalYData) {
@@ -321,7 +307,7 @@ export class Histogram extends React.Component<HistogramProps> {
// click/hover
const onPointClick = action((e: any) => this.highlightSelectedBar(true, svg, eachRectWidth, d3.pointer(e)[0], xAxisTitle, yAxisTitle, histDataSet));
const onHover = action((e: any) => {
- const selected = this.highlightSelectedBar(false, svg, eachRectWidth, d3.pointer(e)[0], xAxisTitle, yAxisTitle, histDataSet);
+ this.highlightSelectedBar(false, svg, eachRectWidth, d3.pointer(e)[0], xAxisTitle, yAxisTitle, histDataSet);
updateHighlights();
});
const mouseOut = action((e: any) => {
@@ -360,10 +346,10 @@ export class Histogram extends React.Component<HistogramProps> {
'transform',
this.numericalYData
? function (d) {
- var eachData = histDataSet.filter((data: { [x: string]: number }) => {
+ const eachData = histDataSet.filter((data: { [x: string]: number }) => {
return data[xAxisTitle] == d[0];
});
- var length = eachData.length ? eachData[0][yAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '') : 0;
+ const length = eachData.length ? eachData[0][yAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '') : 0;
return 'translate(' + x(d.x0!) + ',' + y(length) + ')';
}
: function (d) {
@@ -374,10 +360,10 @@ export class Histogram extends React.Component<HistogramProps> {
'height',
this.numericalYData
? function (d) {
- var eachData = histDataSet.filter((data: { [x: string]: number }) => {
+ const eachData = histDataSet.filter((data: { [x: string]: number }) => {
return data[xAxisTitle] == d[0];
});
- var length = eachData.length ? eachData[0][yAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '') : 0;
+ const length = eachData.length ? eachData[0][yAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '') : 0;
return height - y(length);
}
: function (d) {
@@ -389,7 +375,7 @@ export class Histogram extends React.Component<HistogramProps> {
'class',
selected
? function (d) {
- return selected && selected[0] == d[0] ? 'histogram-bar hover' : 'histogram-bar';
+ return selected && selected[0] === d[0] ? 'histogram-bar hover' : 'histogram-bar';
}
: function (d) {
return 'histogram-bar';
@@ -397,11 +383,11 @@ export class Histogram extends React.Component<HistogramProps> {
)
.attr('fill', d => {
var barColor;
- var barColors = StrListCast(this.props.layoutDoc.histogramBarColors).map(each => each.split('::'));
- barColors.map(each => {
+ const barColors = StrListCast(this.props.layoutDoc.histogramBarColors).map(each => each.split('::'));
+ barColors.forEach(each => {
if (d[0] && d[0].toString() && each[0] == d[0].toString()) barColor = each[1];
else {
- var range = StrCast(each[0]).split(' to ');
+ const range = StrCast(each[0]).split(' to ');
if (Number(range[0]) <= d[0] && d[0] <= Number(range[1])) barColor = each[1];
}
});
@@ -411,23 +397,19 @@ export class Histogram extends React.Component<HistogramProps> {
@action changeSelectedColor = (color: string) => {
this.curBarSelected.attr('fill', color);
- var barName = StrCast(this._currSelected[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''));
+ const barName = StrCast(this._currSelected[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''));
const barColors = Cast(this.props.layoutDoc.histogramBarColors, listSpec('string'), null);
- barColors.map(each => {
- if (each.split('::')[0] == barName) barColors.splice(barColors.indexOf(each), 1);
- });
+ barColors.forEach(each => each.split('::')[0] === barName && barColors.splice(barColors.indexOf(each), 1));
barColors.push(StrCast(barName + '::' + color));
};
@action eraseSelectedColor = () => {
this.curBarSelected.attr('fill', this.props.layoutDoc.defaultHistogramColor);
- var barName = StrCast(this._currSelected[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''));
+ const barName = StrCast(this._currSelected[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''));
const barColors = Cast(this.props.layoutDoc.histogramBarColors, listSpec('string'), null);
- barColors.map(each => {
- if (each.split('::')[0] == barName) barColors.splice(barColors.indexOf(each), 1);
- });
+ barColors.forEach(each => each.split('::')[0] === barName && barColors.splice(barColors.indexOf(each), 1));
};
render() {
@@ -439,25 +421,22 @@ export class Histogram extends React.Component<HistogramProps> {
if (!this.props.layoutDoc[titleAccessor]) this.props.layoutDoc[titleAccessor] = this.defaultGraphTitle;
if (!this.props.layoutDoc.defaultHistogramColor) this.props.layoutDoc.defaultHistogramColor = '#69b3a2';
if (!this.props.layoutDoc.histogramBarColors) this.props.layoutDoc.histogramBarColors = new List<string>();
- var selected: string;
+ var selected = 'none';
if (this._currSelected) {
curSelectedBarName = StrCast(this._currSelected![this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''));
selected = '{ ';
- Object.keys(this._currSelected).map(key => {
- key != '' ? (selected += key + ': ' + this._currSelected[key] + ', ') : '';
- });
- selected = selected.substring(0, selected.length - 2);
- selected += ' }';
- } else selected = 'none';
+ Object.keys(this._currSelected).forEach(key =>
+ key //
+ ? (selected += key + ': ' + this._currSelected[key] + ', ')
+ : ''
+ );
+ selected = selected.substring(0, selected.length - 2) + ' }';
+ }
var selectedBarColor;
var barColors = StrListCast(this.props.layoutDoc.histogramBarColors).map(each => each.split('::'));
- barColors.map(each => {
- if (each[0] == curSelectedBarName!) selectedBarColor = each[1];
- });
+ barColors.forEach(each => each[0] === curSelectedBarName && (selectedBarColor = each[1]));
- this.componentDidMount();
-
- if (this._histogramData.length > 0 || (!this.incomingLinks || this.incomingLinks.length==0)) {
+ if (this._histogramData.length > 0 || !this.parentViz) {
return this.props.axes.length >= 1 ? (
<div className="chart-container">
<div className="graph-title">
@@ -514,10 +493,8 @@ export class Histogram extends React.Component<HistogramProps> {
) : (
<span className="chart-container"> {'first use table view to select a column to graph'}</span>
);
- } else
- return (
- // when it is a brushed table and the incoming table doesn't have any rows selected
- <div className="chart-container">Selected rows of data from the incoming DataVizBox to display.</div>
- );
+ }
+ // when it is a brushed table and the incoming table doesn't have any rows selected
+ return <div className="chart-container">Selected rows of data from the incoming DataVizBox to display.</div>;
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
index 46cf27705..6c9922c0a 100644
--- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx
+++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
@@ -2,7 +2,7 @@ import { action, computed, IReactionDisposer, observable, reaction } from 'mobx'
import { observer } from 'mobx-react';
import * as React from 'react';
import * as d3 from 'd3';
-import { Doc, DocListCast, StrListCast } from '../../../../../fields/Doc';
+import { Doc, DocListCast, NumListCast, StrListCast } from '../../../../../fields/Doc';
import { Id } from '../../../../../fields/FieldSymbols';
import { List } from '../../../../../fields/List';
import { listSpec } from '../../../../../fields/Schema';
@@ -28,7 +28,7 @@ export interface LineChartProps {
rootDoc: Doc;
layoutDoc: Doc;
axes: string[];
- pairs: { [key: string]: any }[];
+ records: { [key: string]: any }[];
width: number;
height: number;
dataDoc: Doc;
@@ -49,32 +49,35 @@ export class LineChart extends React.Component<LineChartProps> {
@observable _currSelected: SelectedDataPoint | undefined = undefined;
// TODO: nda - some sort of mapping that keeps track of the annotated points so we can easily remove when annotations list updates
+ @computed get _tableDataIds() {
+ return !this.parentViz ? this.props.records.map((rec, i) => i) : NumListCast(this.parentViz.dataViz_selectedRows);
+ }
+ // returns all the data records that will be rendered by only returning those records that have been selected by the parent visualization (or all records if there is no parent)
+ @computed get _tableData() {
+ return !this.parentViz ? this.props.records : this._tableDataIds.map(rowId => this.props.records[rowId]);
+ }
@computed get _lineChartData() {
- var guids = StrListCast(this.props.layoutDoc.dataViz_rowGuids);
+ var guids = StrListCast(this.props.layoutDoc.dataViz_rowIds);
if (this.props.axes.length <= 1) return [];
- return this.props.pairs
- ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.dataViz_selectedRows && StrListCast(this.incomingLinks[0].dataViz_selectedRows).includes(guids[this.props.pairs.indexOf(pair)])))
- .map(pair => ({ x: Number(pair[this.props.axes[0]]), y: Number(pair[this.props.axes[1]]) }))
- .sort((a, b) => (a.x < b.x ? -1 : 1));
+ return this._tableData.map(record => ({ x: Number(record[this.props.axes[0]]), y: Number(record[this.props.axes[1]]) })).sort((a, b) => (a.x < b.x ? -1 : 1));
}
@computed get graphTitle() {
return this.props.axes[1] + ' vs. ' + this.props.axes[0] + ' Line Chart';
}
- @computed get incomingLinks() {
- return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links
- .filter(link => {
- return link.link_anchor_1 == this.props.rootDoc.draggedFrom;
- }) // get links where this chart doc is the target of the link
- .map(link => DocCast(link.link_anchor_1)); // then return the source of the link
+ @computed get parentViz() {
+ return DocCast(this.props.rootDoc.dataViz_parentViz);
+ // return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links
+ // .filter(link => {
+ // return link.link_anchor_1 == this.props.rootDoc.dataViz_parentViz;
+ // }) // get links where this chart doc is the target of the link
+ // .map(link => DocCast(link.link_anchor_1)); // then return the source of the link
}
- @computed get incomingSelected() {
+ @computed get incomingHighlited() {
// return selected x and y axes
// otherwise, use the selection of whatever is linked to us
- return this.incomingLinks // all links that are pointing to this node
- .map(anchor => DocumentManager.Instance.getFirstDocumentView(anchor)?.ComponentView as DataVizBox) // get their data viz boxes
- .filter(dvb => dvb)
- .map(dvb => dvb.pairs?.filter(pair => pair['select' + dvb.rootDoc[Id]])) // get all the datapoints they have selected field set by incoming anchor
- .lastElement();
+ const incomingVizBox = DocumentManager.Instance.getFirstDocumentView(this.parentViz)?.ComponentView as DataVizBox;
+ const highlitedRowIds = NumListCast(incomingVizBox.rootDoc.dataViz_highlitedRows);
+ return this._tableData.filter((record, i) => highlitedRowIds.includes(this._tableDataIds[i])); // get all the datapoints they have selected field set by incoming anchor
}
@computed get rangeVals(): { xMin?: number; xMax?: number; yMin?: number; yMax?: number } {
return minMaxRange([this._lineChartData]);
@@ -91,7 +94,7 @@ export class LineChart extends React.Component<LineChartProps> {
// redraw annotations when the chart data has changed, or the local or inherited selection has changed
this.clearAnnotations();
this._currSelected && this.drawAnnotations(Number(this._currSelected.x), Number(this._currSelected.y), true);
- this.incomingSelected?.forEach((pair: any) => this.drawAnnotations(Number(pair[this.props.axes[0]]), Number(pair[this.props.axes[1]])));
+ this.incomingHighlited?.forEach((record: any) => this.drawAnnotations(Number(record[this.props.axes[0]]), Number(record[this.props.axes[1]])));
}
},
{ fireImmediately: true }
@@ -111,13 +114,13 @@ export class LineChart extends React.Component<LineChartProps> {
this._disposers.highlights = reaction(
() => ({
selected: this._currSelected,
- incomingSelected: this.incomingSelected,
+ incomingHighlited: this.incomingHighlited,
}),
- ({ selected, incomingSelected }) => {
+ ({ selected, incomingHighlited }) => {
// redraw annotations when the chart data has changed, or the local or inherited selection has changed
this.clearAnnotations();
selected && this.drawAnnotations(Number(selected.x), Number(selected.y), true);
- incomingSelected?.forEach((pair: any) => this.drawAnnotations(Number(pair[this.props.axes[0]]), Number(pair[this.props.axes[1]])));
+ incomingHighlited?.forEach((record: any) => this.drawAnnotations(Number(record[this.props.axes[0]]), Number(record[this.props.axes[1]])));
},
{ fireImmediately: true }
);
@@ -193,7 +196,7 @@ export class LineChart extends React.Component<LineChartProps> {
@computed get defaultGraphTitle() {
var ax0 = this.props.axes[0];
var ax1 = this.props.axes.length > 1 ? this.props.axes[1] : undefined;
- if (this.props.axes.length < 2 || !/\d/.test(this.props.pairs[0][ax0]) || !ax1) {
+ if (this.props.axes.length < 2 || !/\d/.test(this.props.records[0][ax0]) || !ax1) {
return ax0 + ' Line Chart';
} else return ax1 + ' by ' + ax0 + ' Line Chart';
}
@@ -217,7 +220,7 @@ export class LineChart extends React.Component<LineChartProps> {
// TODO: nda - get rid of svg element in the list?
if (this._currSelected && this._currSelected.x == x && this._currSelected.y == y) this._currSelected = undefined;
else this._currSelected = x !== undefined && y !== undefined ? { x, y } : undefined;
- this.props.pairs.forEach(pair => pair[this.props.axes[0]] === x && pair[this.props.axes[1]] === y && (pair.selected = true));
+ this.props.records.forEach(record => record[this.props.axes[0]] === x && record[this.props.axes[1]] === y && (record.selected = true));
}
drawDataPoints(data: DataPoint[], idx: number, xScale: d3.ScaleLinear<number, number, never>, yScale: d3.ScaleLinear<number, number, never>) {
@@ -358,10 +361,10 @@ export class LineChart extends React.Component<LineChartProps> {
else if (this.props.axes.length > 0) titleAccessor = 'dataViz_title_lineChart_' + this.props.axes[0];
if (!this.props.layoutDoc[titleAccessor]) this.props.layoutDoc[titleAccessor] = this.defaultGraphTitle;
const selectedPt = this._currSelected ? `{ ${this.props.axes[0]}: ${this._currSelected.x} ${this.props.axes[1]}: ${this._currSelected.y} }` : 'none';
- if (this._lineChartData.length>0 || (!this.incomingLinks || this.incomingLinks.length==0)){
- return this.props.axes.length>=2 && /\d/.test(this.props.pairs[0][this.props.axes[0]]) && /\d/.test(this.props.pairs[0][this.props.axes[1]]) ? (
- <div className="chart-container" >
- <div className="graph-title">
+ if (this._lineChartData.length > 0 || !this.parentViz || this.parentViz.length == 0) {
+ return this.props.axes.length >= 2 && /\d/.test(this.props.records[0][this.props.axes[0]]) && /\d/.test(this.props.records[0][this.props.axes[1]]) ? (
+ <div className="chart-container">
+ <div className="graph-title">
<EditableText
val={StrCast(this.props.layoutDoc[titleAccessor])}
setVal={undoable(
diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx
index 213baa8a4..a8aa51897 100644
--- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx
+++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx
@@ -1,24 +1,24 @@
+import { ColorPicker, EditableText, Size, Type } from 'browndash-components';
+import * as d3 from 'd3';
+import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
-import { Doc, StrListCast } from '../../../../../fields/Doc';
import * as React from 'react';
-import * as d3 from 'd3';
-import { IReactionDisposer, action, computed, observable, reaction } from 'mobx';
-import { LinkManager } from '../../../../util/LinkManager';
-import { Cast, DocCast, StrCast } from '../../../../../fields/Types';
-import { PinProps, PresBox } from '../../trails';
-import { Docs } from '../../../../documents/Documents';
-import { List } from '../../../../../fields/List';
-import './Chart.scss';
-import { ColorPicker, EditableText, Size, Type } from 'browndash-components';
import { FaFillDrip } from 'react-icons/fa';
+import { Doc, NumListCast, StrListCast } from '../../../../../fields/Doc';
+import { List } from '../../../../../fields/List';
import { listSpec } from '../../../../../fields/Schema';
+import { Cast, DocCast, StrCast } from '../../../../../fields/Types';
+import { Docs } from '../../../../documents/Documents';
+import { LinkManager } from '../../../../util/LinkManager';
import { undoable } from '../../../../util/UndoManager';
+import { PinProps, PresBox } from '../../trails';
+import './Chart.scss';
export interface PieChartProps {
rootDoc: Doc;
layoutDoc: Doc;
axes: string[];
- pairs: { [key: string]: any }[];
+ records: { [key: string]: any }[];
width: number;
height: number;
dataDoc: Doc;
@@ -36,47 +36,53 @@ export class PieChart extends React.Component<PieChartProps> {
private _disposers: { [key: string]: IReactionDisposer } = {};
private _piechartRef: React.RefObject<HTMLDivElement> = React.createRef();
private _piechartSvg: d3.Selection<SVGGElement, unknown, null, undefined> | undefined;
- private byCategory: boolean = true; // whether the data is organized by category or by specified number percentages/ratios
- @observable _currSelected: any | undefined = undefined; // Object of selected slice
private curSliceSelected: any = undefined; // d3 data of selected slice
private selectedData: any = undefined; // Selection of selected slice
private hoverOverData: any = undefined; // Selection of slice being hovered over
+ @observable _currSelected: any | undefined = undefined; // Object of selected slice
+ @computed get _tableDataIds() {
+ return !this.parentViz ? this.props.records.map((rec, i) => i) : NumListCast(this.parentViz.dataViz_selectedRows);
+ }
+ // returns all the data records that will be rendered by only returning those records that have been selected by the parent visualization (or all records if there is no parent)
+ @computed get _tableData() {
+ return !this.parentViz ? this.props.records : this._tableDataIds.map(rowId => this.props.records[rowId]);
+ }
+
+ // organized by specified number percentages/ratios if one column is selected and it contains numbers
+ // otherwise, assume data is organized by categories
+ @computed get byCategory() {
+ if (this.props.axes.length === 1) {
+ return !/\d/.test(this.props.records[0][this.props.axes[0]]);
+ }
+ return true;
+ }
// filters all data to just display selected data if brushed (created from an incoming link)
@computed get _piechartData() {
- var guids = StrListCast(this.props.layoutDoc.dataViz_rowGuids);
if (this.props.axes.length < 1) return [];
+
+ const ax0 = this.props.axes[0];
if (this.props.axes.length < 2) {
- var ax0 = this.props.axes[0];
- if (/\d/.test(this.props.pairs[0][ax0])) {
- this.byCategory = false;
- }
- return this.props.pairs
- ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.dataViz_selectedRows && StrListCast(this.incomingLinks[0].dataViz_selectedRows).includes(guids[this.props.pairs.indexOf(pair)])))
- .map(pair => ({ [ax0]: pair[this.props.axes[0]] }));
- }
- var ax0 = this.props.axes[0];
- var ax1 = this.props.axes[1];
- if (/\d/.test(this.props.pairs[0][ax0])) {
- this.byCategory = false;
+ return this._tableData.map(record => ({ [ax0]: record[this.props.axes[0]] }));
}
- return this.props.pairs
- ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.dataViz_selectedRows && StrListCast(this.incomingLinks[0].dataViz_selectedRows).includes(guids[this.props.pairs.indexOf(pair)])))
- .map(pair => ({ [ax0]: pair[this.props.axes[0]], [ax1]: pair[this.props.axes[1]] }));
+ const ax1 = this.props.axes[1];
+ return this._tableData.map(record => ({ [ax0]: record[this.props.axes[0]], [ax1]: record[this.props.axes[1]] }));
}
@computed get defaultGraphTitle() {
var ax0 = this.props.axes[0];
var ax1 = this.props.axes.length > 1 ? this.props.axes[1] : undefined;
- if (this.props.axes.length < 2 || !/\d/.test(this.props.pairs[0][ax0]) || !ax1) {
+ if (this.props.axes.length < 2 || !/\d/.test(this.props.records[0][ax0]) || !ax1) {
return ax0 + ' Pie Chart';
- } else return ax1 + ' by ' + ax0 + ' Pie Chart';
+ }
+ return ax1 + ' by ' + ax0 + ' Pie Chart';
}
- @computed get incomingLinks() {
- return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links
- .filter(link => link.link_anchor_1 == this.props.rootDoc.draggedFrom) // get links where this chart doc is the target of the link
- .map(link => DocCast(link.link_anchor_1)); // then return the source of the link
+ @computed get parentViz() {
+ return DocCast(this.props.rootDoc.dataViz_parentViz);
+ // return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links
+ // .filter(link => link.link_anchor_1 == this.props.rootDoc.dataViz_parentViz) // get links where this chart doc is the target of the link
+ // .map(link => DocCast(link.link_anchor_1)); // then return the source of the link
}
componentWillUnmount() {
@@ -116,21 +122,15 @@ export class PieChart extends React.Component<PieChartProps> {
// cleans data by converting numerical data to numbers and taking out empty cells
data = (dataSet: any) => {
- var validData = dataSet.filter((d: { [x: string]: unknown }) => {
- var valid = true;
- Object.keys(dataSet[0]).map(key => {
- if (!d[key] || Number.isNaN(d[key])) valid = false;
- });
- return valid;
- });
- var field = dataSet[0] ? Object.keys(dataSet[0])[0] : undefined;
- const data = validData.map((d: { [x: string]: any }) => {
- if (!this.byCategory) {
- return +d[field!].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '');
- }
- return d[field!];
- });
- return data;
+ const validData = dataSet.filter((d: { [x: string]: unknown }) => !Object.keys(dataSet[0]).some(key => !d[key] || Number.isNaN(d[key])));
+ const field = dataSet[0] ? Object.keys(dataSet[0])[0] : undefined;
+ return !field
+ ? undefined
+ : validData.map((d: { [x: string]: any }) =>
+ this.byCategory
+ ? d[field] //
+ : +d[field].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '')
+ );
};
// outlines the slice selected / hovered over
@@ -192,13 +192,7 @@ export class PieChart extends React.Component<PieChartProps> {
// converts data into Objects
var data = this.data(dataSet);
- var pieDataSet = dataSet.filter((d: { [x: string]: unknown }) => {
- var valid = true;
- Object.keys(dataSet[0]).map(key => {
- if (!d[key] || Number.isNaN(d[key])) valid = false;
- });
- return valid;
- });
+ var pieDataSet = dataSet.filter((d: { [x: string]: unknown }) => !Object.keys(dataSet[0]).some(key => !d[key] || Number.isNaN(d[key])));
if (this.byCategory) {
let uniqueCategories = [...new Set(data)];
var pieStringDataSet: { frequency: number }[] = [];
@@ -232,7 +226,7 @@ export class PieChart extends React.Component<PieChartProps> {
// click/hover
const onPointClick = action((e: any) => this.highlightSelectedSlice(true, svg, arc, radius, d3.pointer(e), pieDataSet));
const onHover = action((e: any) => {
- const selected = this.highlightSelectedSlice(false, svg, arc, radius, d3.pointer(e), pieDataSet);
+ this.highlightSelectedSlice(false, svg, arc, radius, d3.pointer(e), pieDataSet);
updateHighlights();
});
const mouseOut = action((e: any) => {
@@ -252,16 +246,19 @@ export class PieChart extends React.Component<PieChartProps> {
// drawing the slices
var selected = this.selectedData;
var arcs = g.selectAll('arc').data(pie(data)).enter().append('g');
+ const sliceColors = StrListCast(this.props.layoutDoc.pieSliceColors).map(each => each.split('::'));
+ const possibleDataPointVals = pieDataSet.map((each: { [x: string]: any | { valueOf(): number } }) => {
+ try {
+ each[percentField] = Number(each[percentField].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''));
+ } catch (error) {
+ //return each[percentField] == d.data;
+ }
+ return each;
+ });
arcs.append('path')
.attr('fill', (d, i) => {
- var possibleDataPoints = pieDataSet.filter((each: { [x: string]: any | { valueOf(): number } }) => {
- try {
- return Number(each[percentField].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '')) == Number(d.data);
- } catch (error) {
- return each[percentField] == d.data;
- }
- });
var dataPoint;
+ const possibleDataPoints = possibleDataPointVals.filter((pval: any) => pval[percentField] === Number(d.data));
if (possibleDataPoints.length == 1) dataPoint = possibleDataPoints[0];
else {
dataPoint = possibleDataPoints[trackDuplicates[d.data.toString()]];
@@ -269,11 +266,8 @@ export class PieChart extends React.Component<PieChartProps> {
}
var sliceColor;
if (dataPoint) {
- var accessByName = dataPoint[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '');
- var sliceColors = StrListCast(this.props.layoutDoc.pieSliceColors).map(each => each.split('::'));
- sliceColors.map(each => {
- if (each[0] == StrCast(accessByName)) sliceColor = each[1];
- });
+ var accessByName = dataPoint[this.props.axes[0]];
+ sliceColors.forEach(each => each[0] == StrCast(accessByName) && (sliceColor = each[1]));
}
return sliceColor ? StrCast(sliceColor) : d3.schemeSet3[i] ? d3.schemeSet3[i] : d3.schemeSet3[i % d3.schemeSet3.length];
})
@@ -295,29 +289,25 @@ export class PieChart extends React.Component<PieChartProps> {
// adding labels
trackDuplicates = {};
data.forEach((eachData: any) => (!trackDuplicates[eachData] ? (trackDuplicates[eachData] = 0) : null));
- arcs.append('text')
- .attr('transform', function (d) {
- var centroid = arc.centroid(d as unknown as d3.DefaultArcObject);
- var heightOffset = (centroid[1] / radius) * Math.abs(centroid[1]);
- return 'translate(' + (centroid[0] + centroid[0] / (radius * 0.02)) + ',' + (centroid[1] + heightOffset) + ')';
- })
- .attr('text-anchor', 'middle')
- .text(function (d) {
- var possibleDataPoints = pieDataSet.filter((each: { [x: string]: any | { valueOf(): number } }) => {
- try {
- return Number(each[percentField].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '')) == Number(d.data);
- } catch (error) {
- return each[percentField] == d.data;
+ arcs.size() < 100 &&
+ arcs
+ .append('text')
+ .attr('transform', function (d) {
+ var centroid = arc.centroid(d as unknown as d3.DefaultArcObject);
+ var heightOffset = (centroid[1] / radius) * Math.abs(centroid[1]);
+ return 'translate(' + (centroid[0] + centroid[0] / (radius * 0.02)) + ',' + (centroid[1] + heightOffset) + ')';
+ })
+ .attr('text-anchor', 'middle')
+ .text(function (d) {
+ var dataPoint;
+ const possibleDataPoints = possibleDataPointVals.filter((pval: any) => pval[percentField] === Number(d.data));
+ if (possibleDataPoints.length == 1) dataPoint = possibleDataPoints[0];
+ else {
+ dataPoint = possibleDataPoints[trackDuplicates[d.data.toString()]];
+ trackDuplicates[d.data.toString()] = trackDuplicates[d.data.toString()] + 1;
}
+ return dataPoint ? dataPoint[percentField]! + (!descriptionField ? '' : ' - ' + dataPoint[descriptionField])! : '';
});
- var dataPoint;
- if (possibleDataPoints.length == 1) dataPoint = possibleDataPoints[0];
- else {
- dataPoint = possibleDataPoints[trackDuplicates[d.data.toString()]];
- trackDuplicates[d.data.toString()] = trackDuplicates[d.data.toString()] + 1;
- }
- return dataPoint ? dataPoint[percentField]! + (!descriptionField ? '' : ' - ' + dataPoint[descriptionField])! : '';
- });
};
@action changeSelectedColor = (color: string) => {
@@ -332,7 +322,6 @@ export class PieChart extends React.Component<PieChartProps> {
};
render() {
- this.componentDidMount();
var titleAccessor: any = '';
if (this.props.axes.length == 2) titleAccessor = 'dataViz_title_pieChart_' + this.props.axes[0] + '-' + this.props.axes[1];
else if (this.props.axes.length > 0) titleAccessor = 'dataViz_title_pieChart_' + this.props.axes[0];
@@ -355,7 +344,7 @@ export class PieChart extends React.Component<PieChartProps> {
if (each[0] == curSelectedSliceName!) selectedSliceColor = each[1];
});
- if (this._piechartData.length>0 || (!this.incomingLinks || this.incomingLinks.length==0)){
+ if (this._piechartData.length > 0 || !this.parentViz) {
return this.props.axes.length >= 1 ? (
<div className="chart-container">
<div className="graph-title">
diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
index 70483ac6f..067dff07a 100644
--- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx
+++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
@@ -1,21 +1,21 @@
-import { action, computed } from 'mobx';
+import { action, computed, IReactionDisposer, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { Doc, Field, StrListCast } from '../../../../../fields/Doc';
+import { Doc, Field, NumListCast, StrListCast } from '../../../../../fields/Doc';
import { List } from '../../../../../fields/List';
+import { listSpec } from '../../../../../fields/Schema';
+import { Cast, DocCast } from '../../../../../fields/Types';
import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../../Utils';
import { DragManager } from '../../../../util/DragManager';
+import { LinkManager } from '../../../../util/LinkManager';
import { DocumentView } from '../../DocumentView';
import { DataVizView } from '../DataVizBox';
-import { LinkManager } from '../../../../util/LinkManager';
-import { Cast, DocCast } from '../../../../../fields/Types';
import './Chart.scss';
-import { listSpec } from '../../../../../fields/Schema';
interface TableBoxProps {
rootDoc: Doc;
layoutDoc: Doc;
- pairs: { [key: string]: any }[];
+ records: { [key: string]: any }[];
selectAxes: (axes: string[]) => void;
axes: string[];
width: number;
@@ -31,151 +31,159 @@ interface TableBoxProps {
@observer
export class TableBox extends React.Component<TableBoxProps> {
- // filters all data to just display selected data if brushed (created from an incoming link)
+ _inputChangedDisposer?: IReactionDisposer;
+ componentDidMount() {
+ // if the tableData changes (ie., when records are selected by the parent (input) visulization),
+ // then we need to remove any selected rows that are no longer part of the visualized dataset.
+ this._inputChangedDisposer = reaction(() => this._tableData.slice(), this.filterSelectedRowsDown, { fireImmediately: true });
+ }
+ componentWillUnmount() {
+ this._inputChangedDisposer?.();
+ }
+ @computed get _tableDataIds() {
+ return !this.parentViz ? this.props.records.map((rec, i) => i) : NumListCast(this.parentViz.dataViz_selectedRows);
+ }
+ // returns all the data records that will be rendered by only returning those records that have been selected by the parent visualization (or all records if there is no parent)
@computed get _tableData() {
- if (this.incomingLinks.length! <= 0) return this.props.pairs;
- var guids = StrListCast(this.props.layoutDoc.dataViz_rowGuids);
- return this.props.pairs?.filter(pair => this.incomingLinks[0]!.dataViz_selectedRows && StrListCast(this.incomingLinks[0].dataViz_selectedRows).includes(guids[this.props.pairs.indexOf(pair)]));
+ return !this.parentViz ? this.props.records : this._tableDataIds.map(rowId => this.props.records[rowId]);
}
- @computed get incomingLinks() {
- return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links
- .filter(link => {
- return link.link_anchor_1 == this.props.rootDoc.draggedFrom;
- }) // get links where this chart doc is the target of the link
- .map(link => DocCast(link.link_anchor_1)); // then return the source of the link
+ @computed get parentViz() {
+ return DocCast(this.props.rootDoc.dataViz_parentViz);
+ // return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links
+ // .filter(link => link.link_anchor_1 == this.props.rootDoc.dataViz_parentViz) // get links where this chart doc is the target of the link
+ // .map(link => DocCast(link.link_anchor_1)); // then return the source of the link
}
@computed get columns() {
- if (!this.props.layoutDoc.dataViz_rowGuids) this.props.layoutDoc.dataViz_rowGuids = new List<string>();
- const guids = Cast(this.props.layoutDoc.dataViz_rowGuids, listSpec('string'), null);
- if (guids.length == 0) this.props.pairs.map(row => guids.push(Utils.GenerateGuid()));
return this._tableData.length ? Array.from(Object.keys(this._tableData[0])).filter(header => header != '' && header != undefined) : [];
}
- // updates the 'selected' field to no longer include rows that aren't in the table
- filterSelectedRowsDown() {
- if (!this.props.layoutDoc.dataViz_selectedRows) this.props.layoutDoc.dataViz_selectedRows = new List<string>();
- const selected = Cast(this.props.layoutDoc.dataViz_selectedRows, listSpec('string'), null);
- const incomingSelected = this.incomingLinks.length ? StrListCast(this.incomingLinks[0].dataViz_selectedRows) : undefined;
- if (incomingSelected) {
- selected.map(guid => {
- if (!incomingSelected.includes(guid)) selected.splice(selected.indexOf(guid), 1);
- }); // filters through selected to remove guids that were removed in the incoming data
- }
- }
+ // updates the 'dataViz_selectedRows' and 'dataViz_highlightedRows' fields to no longer include rows that aren't in the table
+ filterSelectedRowsDown = () => {
+ const selected = NumListCast(this.props.layoutDoc.dataViz_selectedRows);
+ this.props.layoutDoc.dataViz_selectedRows = new List<number>(selected.filter(rowId => this._tableDataIds.includes(rowId))); // filters through selected to remove guids that were removed in the incoming data
+ const highlighted = NumListCast(this.props.layoutDoc.dataViz_highlitedRows);
+ this.props.layoutDoc.dataViz_highlitedRows = new List<number>(highlighted.filter(rowId => this._tableDataIds.includes(rowId))); // filters through selected to remove guids that were removed in the incoming data
+ };
render() {
- this.filterSelectedRowsDown();
if (this._tableData.length > 0) {
return (
- <div className="table-container" style={{ height: this.props.height }}>
+ <div
+ className="table-container"
+ style={{ height: this.props.height }}
+ ref={r =>
+ r?.addEventListener(
+ 'wheel', // if scrollTop is 0, then don't let wheel trigger scroll on any container (which it would since onScroll won't be triggered on this)
+ (e: WheelEvent) => {
+ if (!r.scrollTop && e.deltaY <= 0) e.preventDefault();
+ e.stopPropagation();
+ },
+ { passive: false }
+ )
+ }>
<table className="table">
<thead>
<tr className="table-row">
- {this.columns
- .filter(col => !col.startsWith('select'))
- .map(col => {
- const header = React.createRef<HTMLElement>();
- return (
- <th
- key={this.columns.indexOf(col)}
- ref={header as any}
- style={{
- color: this.props.axes.slice().reverse().lastElement() === col ? 'darkgreen' : this.props.axes.lastElement() === col ? 'darkred' : undefined,
- background: this.props.axes.slice().reverse().lastElement() === col ? '#E3fbdb' : this.props.axes.lastElement() === col ? '#Fbdbdb' : undefined,
- fontWeight: 'bolder',
- border: '3px solid black',
- }}
- onPointerDown={e => {
- const downX = e.clientX;
- const downY = e.clientY;
- setupMoveUpEvents(
- {},
- e,
- e => {
- // dragging off a column to create a brushed DataVizBox
- const sourceAnchorCreator = () => this.props.docView?.()!.rootDoc!;
- const targetCreator = (annotationOn: Doc | undefined) => {
- const embedding = Doc.MakeEmbedding(this.props.docView?.()!.rootDoc!);
- embedding._dataViz = DataVizView.TABLE;
- embedding._dataViz_axes = new List<string>([col, col]);
- embedding._draggedFrom = this.props.docView?.()!.rootDoc!;
- embedding.annotationOn = annotationOn; //this.props.docView?.()!.rootDoc!;
- embedding.histogramBarColors = Field.Copy(this.props.layoutDoc.histogramBarColors);
- embedding.defaultHistogramColor = this.props.layoutDoc.defaultHistogramColor;
- embedding.pieSliceColors = Field.Copy(this.props.layoutDoc.pieSliceColors);
- return embedding;
- };
- if (this.props.docView?.() && !Utils.isClick(e.clientX, e.clientY, downX, downY, Date.now())) {
- DragManager.StartAnchorAnnoDrag([header.current!], new DragManager.AnchorAnnoDragData(this.props.docView()!, sourceAnchorCreator, targetCreator), downX, downY, {
- dragComplete: e => {
- if (!e.aborted && e.annoDragData && e.annoDragData.linkSourceDoc && e.annoDragData.dropDocument && e.linkDocument) {
- e.linkDocument.link_displayLine = true;
- e.linkDocument.link_matchEmbeddings = true;
- // e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.props.rootDoc;
- // e.annoDragData.linkSourceDoc.followLinkZoom = false;
- }
- },
- });
- return true;
- }
- return false;
- },
- emptyFunction,
- action(e => {
- const newAxes = this.props.axes;
- if (newAxes.includes(col)) {
- newAxes.splice(newAxes.indexOf(col), 1);
- } else if (newAxes.length > 1) {
- newAxes[1] = col;
- } else {
- newAxes.push(col);
- }
- this.props.selectAxes(newAxes);
- })
- );
- }}>
- {col}
- </th>
- );
- })}
+ {this.columns.map(col => (
+ <th
+ key={this.columns.indexOf(col)}
+ style={{
+ color: this.props.axes.slice().reverse().lastElement() === col ? 'darkgreen' : this.props.axes.lastElement() === col ? 'darkred' : undefined,
+ background: this.props.axes.slice().reverse().lastElement() === col ? '#E3fbdb' : this.props.axes.lastElement() === col ? '#Fbdbdb' : undefined,
+ fontWeight: 'bolder',
+ border: '3px solid black',
+ }}
+ onPointerDown={e => {
+ const downX = e.clientX;
+ const downY = e.clientY;
+ setupMoveUpEvents(
+ {},
+ e,
+ e => {
+ // dragging off a column to create a brushed DataVizBox
+ const sourceAnchorCreator = () => this.props.docView?.()!.rootDoc!;
+ const targetCreator = (annotationOn: Doc | undefined) => {
+ const embedding = Doc.MakeEmbedding(this.props.docView?.()!.rootDoc!);
+ embedding._dataViz = DataVizView.TABLE;
+ embedding._dataViz_axes = new List<string>([col, col]);
+ embedding._dataViz_parentViz = this.props.rootDoc;
+ embedding.annotationOn = annotationOn; //this.props.docView?.()!.rootDoc!;
+ embedding.histogramBarColors = Field.Copy(this.props.layoutDoc.histogramBarColors);
+ embedding.defaultHistogramColor = this.props.layoutDoc.defaultHistogramColor;
+ embedding.pieSliceColors = Field.Copy(this.props.layoutDoc.pieSliceColors);
+ return embedding;
+ };
+ if (this.props.docView?.() && !Utils.isClick(e.clientX, e.clientY, downX, downY, Date.now())) {
+ DragManager.StartAnchorAnnoDrag(e.target instanceof HTMLElement ? [e.target] : [], new DragManager.AnchorAnnoDragData(this.props.docView()!, sourceAnchorCreator, targetCreator), downX, downY, {
+ dragComplete: e => {
+ if (!e.aborted && e.annoDragData && e.annoDragData.linkSourceDoc && e.annoDragData.dropDocument && e.linkDocument) {
+ e.linkDocument.link_displayLine = true;
+ e.linkDocument.link_matchEmbeddings = true;
+ // e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.props.rootDoc;
+ // e.annoDragData.linkSourceDoc.followLinkZoom = false;
+ }
+ },
+ });
+ return true;
+ }
+ return false;
+ },
+ emptyFunction,
+ action(e => {
+ const newAxes = this.props.axes;
+ if (newAxes.includes(col)) newAxes.splice(newAxes.indexOf(col), 1);
+ else if (newAxes.length > 1) newAxes[1] = col;
+ else newAxes.push(col);
+ this.props.selectAxes(newAxes);
+ })
+ );
+ }}>
+ {col}
+ </th>
+ ))}
</tr>
</thead>
<tbody>
- {this._tableData?.map((p, i) => {
- var containsData = false;
- var guid = StrListCast(this.props.layoutDoc.dataViz_rowGuids)![this.props.pairs.indexOf(p)];
- this.columns.map(col => {
- if (p[col] != '' && p[col] != null && p[col] != undefined) containsData = true;
- });
- if (containsData) {
- return (
- <tr
- key={i}
- className="table-row"
- onClick={action(e => {
+ {this._tableDataIds
+ ?.map(rowId => ({ record: this.props.records[rowId], rowId }))
+ .map(({ record, rowId }) => (
+ <tr
+ key={rowId}
+ className="table-row"
+ onClick={action(e => {
+ const highlited = Cast(this.props.layoutDoc.dataViz_highlitedRows, listSpec('number'), null);
+ const selected = Cast(this.props.layoutDoc.dataViz_selectedRows, listSpec('number'), null);
+ if (e.metaKey) {
+ // highlighting a row
+ if (highlited?.includes(rowId)) highlited.splice(highlited.indexOf(rowId), 1);
+ else highlited?.push(rowId);
+ if (!selected?.includes(rowId)) selected?.push(rowId);
+ } else {
// selecting a row
- const selected = Cast(this.props.layoutDoc.dataViz_selectedRows, listSpec('string'), null);
- if (selected.includes(guid)) selected.splice(selected.indexOf(guid), 1);
- else {
- selected.push(guid);
- }
- })}
- style={{ background: StrListCast(this.props.layoutDoc.dataViz_selectedRows).includes(guid) ? 'lightgrey' : '', width: '110%' }}>
- {this.columns.map(col => {
- // each cell
- var colSelected = this.props.axes.length > 1 ? this.props.axes[0] == col || this.props.axes[1] == col : this.props.axes.length > 0 ? this.props.axes[0] == col : false;
- return (
- <td key={this.columns.indexOf(col)} style={{ border: colSelected ? '3px solid black' : '1px solid black', fontWeight: colSelected ? 'bolder' : 'normal' }}>
- {p[col]}
- </td>
- );
- })}
- </tr>
- );
- }
- })}
+ if (selected?.includes(rowId)) {
+ if (highlited?.includes(rowId)) highlited.splice(highlited.indexOf(rowId), 1);
+ selected.splice(selected.indexOf(rowId), 1);
+ } else selected?.push(rowId);
+ }
+ e.stopPropagation();
+ })}
+ style={{
+ background: NumListCast(this.props.layoutDoc.dataViz_highlitedRows).includes(rowId) ? 'lightYellow' : NumListCast(this.props.layoutDoc.dataViz_selectedRows).includes(rowId) ? 'lightgrey' : '',
+ width: '110%',
+ }}>
+ {this.columns.map(col => {
+ // each cell
+ const colSelected = this.props.axes.length > 1 ? this.props.axes[0] == col || this.props.axes[1] == col : this.props.axes.length > 0 ? this.props.axes[0] == col : false;
+ return (
+ <td key={this.columns.indexOf(col)} style={{ border: colSelected ? '3px solid black' : '1px solid black', fontWeight: colSelected ? 'bolder' : 'normal' }}>
+ {record[col]}
+ </td>
+ );
+ })}
+ </tr>
+ ))}
</tbody>
</table>
</div>
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 1e17320bf..be1eb3901 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -748,7 +748,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
event: undoBatch(action(() => (this.rootDoc._raiseWhenDragged = this.rootDoc._raiseWhenDragged === undefined ? false : undefined))),
icon: 'hand-point-up',
});
- !zorders && cm.addItem({ description: 'Z Order...', addDivider: true, noexpand: true, subitems: zorderItems, icon: 'compass' });
+ !zorders && cm.addItem({ description: 'Z Order...', addDivider: true, noexpand: true, subitems: zorderItems, icon: 'layer-group' });
}
onClicks.push({ description: 'Enter Portal', event: this.makeIntoPortal, icon: 'window-restore' });
@@ -806,7 +806,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
constantItems.push({ description: 'Close', event: this.deleteClicked, icon: 'times' });
}
}
- constantItems.push({ description: 'Show Metadata', event: () => this.props.addDocTab(this.props.Document, (OpenWhere.addRight.toString() + 'KeyValue') as OpenWhere), icon: 'layer-group' });
+ constantItems.push({ description: 'Show Metadata', event: () => this.props.addDocTab(this.props.Document, (OpenWhere.addRight.toString() + 'KeyValue') as OpenWhere), icon: 'table-columns' });
cm.addItem({ description: 'General...', noexpand: false, subitems: constantItems, icon: 'question' });
const help = cm.findByDescription('Help...');
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index 5ff5f7bfa..d132707fa 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -1,27 +1,27 @@
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Button, MultiToggle, ColorPicker, Dropdown, DropdownType, EditableText, IconButton, IListItemProps, NumberDropdown, NumberDropdownType, Popup, Size, Toggle, ToggleType, Type } from 'browndash-components';
+import { Button, ColorPicker, Dropdown, DropdownType, EditableText, IconButton, IListItemProps, MultiToggle, NumberDropdown, NumberDropdownType, Popup, Size, Toggle, ToggleType, Type } from 'browndash-components';
import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc';
import { ScriptField } from '../../../../fields/ScriptField';
import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
+import { colorMapping } from '../../../../server/DashSession/Session/utilities/session_config';
+import { Utils } from '../../../../Utils';
import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
import { SelectionManager } from '../../../util/SelectionManager';
import { undoable, UndoManager } from '../../../util/UndoManager';
import { ContextMenu } from '../../ContextMenu';
import { DocComponent } from '../../DocComponent';
import { EditableView } from '../../EditableView';
-import { Colors } from '../../global/globalEnums';
+import { SelectedDocView } from '../../selectedDoc';
import { StyleProp } from '../../StyleProvider';
-import { FieldView, FieldViewProps } from '../FieldView';
import { OpenWhere } from '../DocumentView';
+import { FieldView, FieldViewProps } from '../FieldView';
import { RichTextMenu } from '../formattedText/RichTextMenu';
import './FontIconBox.scss';
-import { SelectedDocView } from '../../selectedDoc';
-import { Utils } from '../../../../Utils';
-import { FaAlignCenter, FaAlignJustify, FaAlignLeft, FaAlignRight } from 'react-icons/fa';
+import TrailsIcon from './TrailsIcon';
export enum ButtonType {
TextButton = 'textBtn',
@@ -93,8 +93,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
else return null;
}
icon = StrCast(this.dataDoc[this.fieldKey ?? 'icon'] ?? this.dataDoc.icon, 'user') as any;
- const trailsIcon = () => <img src={`/assets/${'presTrails.png'}`} style={{ width: 30, height: 30, filter: `invert(${color === Colors.DARK_GRAY ? '0%' : '100%'})` }} />;
- return !icon ? null : icon === 'pres-trail' ? trailsIcon() : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />;
+ return !icon ? null : icon === 'pres-trail' ? TrailsIcon(color) : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />;
};
@computed get dropdown() {
return BoolCast(this.rootDoc.dropDownOpen);
diff --git a/src/client/views/nodes/FontIconBox/TrailsIcon.tsx b/src/client/views/nodes/FontIconBox/TrailsIcon.tsx
new file mode 100644
index 000000000..09fd6e3ae
--- /dev/null
+++ b/src/client/views/nodes/FontIconBox/TrailsIcon.tsx
@@ -0,0 +1,81 @@
+import * as React from 'react';
+
+const TrailsIcon = (fill: string) => (
+ <svg version="1.0" xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 1080.000000 1080.000000" preserveAspectRatio="xMidYMid meet">
+ <g transform="translate(0.000000,1080.000000) scale(0.100000,-0.100000)" fill={fill} stroke="none">
+ <path
+ d="M665 9253 c-74 -10 -157 -38 -240 -81 -74 -37 -107 -63 -186 -141
+-104 -104 -156 -191 -201 -334 l-23 -72 0 -3215 c0 -3072 1 -3218 18 -3280 10
+-36 39 -108 64 -160 40 -82 59 -107 142 -190 81 -81 111 -103 191 -143 52 -26
+122 -55 155 -65 57 -16 322 -17 4775 -20 3250 -2 4736 1 4784 8 256 39 486
+220 588 462 63 148 59 -96 56 3413 -3 3049 -4 3203 -21 3260 -78 260 -285 467
+-542 542 -57 17 -308 18 -4795 19 -2604 1 -4748 -1 -4765 -3z m9187 -787 c65
+-19 114 -60 143 -120 l25 -51 0 -2898 c0 -2582 -2 -2901 -15 -2934 -24 -57
+-62 -101 -108 -126 l-42 -22 -4435 -3 c-3954 -2 -4440 0 -4481 13 -26 9 -63
+33 -87 56 -79 79 -72 -205 -72 3012 0 2156 3 2889 12 2918 20 70 91 136 168
+160 14 4 2010 8 4436 8 3710 1 4418 -1 4456 -13z"
+ />
+ <path
+ d="M7692 7839 c-46 -14 -109 -80 -122 -128 -7 -27 -9 -472 -8 -1443 l3
+-1403 24 -38 c13 -21 42 -50 64 -65 l41 -27 816 0 816 0 41 27 c22 15 51 44
+64 65 l24 38 0 1425 0 1425 -24 38 c-13 21 -42 50 -64 65 l-41 27 -800 2
+c-488 1 -814 -2 -834 -8z"
+ />
+ <path
+ d="M1982 7699 c-46 -14 -109 -80 -122 -128 -7 -27 -10 -308 -8 -893 l3
+-853 24 -38 c13 -21 42 -50 64 -65 l41 -27 1386 0 1386 0 41 27 c22 15 51 44
+64 65 l24 38 0 876 0 875 -27 41 c-15 22 -44 51 -65 64 l-38 24 -1370 2 c-847
+1 -1383 -2 -1403 -8z"
+ />
+ <path
+ d="M6413 7093 c-13 -2 -23 -9 -23 -15 0 -24 21 -307 26 -343 l5 -40 182
+-1 c200 -1 307 -15 484 -65 57 -16 107 -29 112 -29 5 0 36 75 69 168 33 92 63
+175 67 184 6 14 -10 22 -92 48 -126 39 -308 76 -447 89 -106 11 -337 13 -383
+4z"
+ />
+ <path
+ d="M5840 7033 c-63 -8 -238 -29 -388 -47 -150 -18 -274 -35 -276 -37 -2
+-2 8 -89 23 -194 22 -163 29 -190 44 -193 10 -2 91 6 180 17 89 12 258 32 376
+46 118 14 216 27 218 28 7 8 -43 391 -52 392 -5 1 -62 -4 -125 -12z"
+ />
+ <path
+ d="M4762 4789 c-46 -14 -109 -80 -122 -128 -7 -27 -10 -323 -8 -943 l3
+-903 24 -38 c13 -21 42 -50 64 -65 l41 -27 926 0 926 0 41 27 c22 15 51 44 64
+65 l24 38 0 926 0 925 -27 41 c-15 22 -44 51 -65 64 l-38 24 -910 2 c-557 1
+-923 -2 -943 -8z"
+ />
+ <path
+ d="M8487 4297 c-26 -215 -161 -474 -307 -585 -27 -20 -49 -40 -49 -44
+-1 -3 49 -79 110 -167 l110 -161 44 31 c176 126 333 350 418 594 30 86 77 282
+77 320 0 8 -57 19 -167 34 -93 13 -182 25 -199 28 -31 5 -31 5 -37 -50z"
+ />
+ <path
+ d="M3965 4233 c-106 -9 -348 -36 -415 -47 -55 -8 -75 -15 -74 -26 1 -20
+56 -374 59 -377 1 -2 46 4 101 12 159 24 409 45 526 45 l108 0 0 200 0 200
+-132 -2 c-73 -1 -151 -3 -173 -5z"
+ />
+ <path
+ d="M3020 4079 c-85 -23 -292 -94 -368 -125 -97 -40 -298 -140 -305 -151
+-5 -7 172 -315 192 -336 4 -4 41 10 82 32 103 55 272 123 414 165 66 20 125
+38 132 41 11 4 -4 70 -78 348 -10 39 -14 41 -69 26z"
+ />
+ <path
+ d="M6955 3538 c-21 -91 -74 -362 -72 -364 7 -7 260 -44 367 -54 146 -13
+359 -13 475 0 49 6 90 12 91 13 2 1 -12 90 -29 197 -26 155 -36 194 -47 192
+-8 -2 -85 -6 -170 -9 -160 -6 -357 7 -505 33 -103 18 -104 18 -110 -8z"
+ />
+ <path
+ d="M1993 3513 c-52 -67 -71 -106 -98 -198 -35 -122 -44 -284 -21 -415 9
+-51 18 -96 21 -98 4 -5 360 79 375 88 7 4 7 24 0 60 -21 109 -7 244 31 307
+l20 31 -146 131 c-80 72 -147 131 -149 131 -2 0 -17 -17 -33 -37z"
+ />
+ <path
+ d="M2210 2519 c-91 -50 -166 -92 -168 -94 -2 -1 11 -26 28 -54 l32 -51
+244 0 c134 0 244 2 244 5 0 3 -23 33 -51 67 -28 35 -72 98 -97 140 -26 43 -51
+77 -57 77 -5 0 -84 -41 -175 -90z"
+ />
+ </g>
+ </svg>
+);
+
+export default TrailsIcon;
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 44da98f75..f5c6a9273 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -36,7 +36,6 @@ import { FieldView, FieldViewProps } from './FieldView';
import './ImageBox.scss';
import { PinProps, PresBox } from './trails';
import React = require('react');
-import Color = require('color');
export const pageSchema = createSchema({
googlePhotosUrl: 'string',
@@ -55,6 +54,17 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
public static LayoutString(fieldKey: string) {
return FieldView.LayoutString(ImageBox, fieldKey);
}
+
+ @observable public static imageRootDoc: Doc | undefined;
+ @observable public static imageEditorOpen: boolean = false;
+ @observable public static imageEditorSource: string = '';
+ @observable public static addDoc: ((doc: Doc | Doc[], annotationKey?: string) => boolean) | undefined;
+ @action public static setImageEditorOpen(open: boolean) {
+ ImageBox.imageEditorOpen = open;
+ }
+ @action public static setImageEditorSource(source: string) {
+ ImageBox.imageEditorSource = source;
+ }
private _ignoreScroll = false;
private _forcedScroll = false;
private _dropDisposer?: DragManager.DragDropDisposer;
@@ -246,6 +256,16 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
funcs.push({ description: `Show ${this.layoutDoc._showFullRes ? 'Dynamic Res' : 'Full Res'}`, event: this.resolution, icon: 'expand' });
funcs.push({ description: 'Set Native Pixel Size', event: this.setNativeSize, icon: 'expand-arrows-alt' });
funcs.push({ description: 'Copy path', event: () => Utils.CopyText(this.choosePath(field.url)), icon: 'copy' });
+ funcs.push({
+ description: 'Open Image Editor',
+ event: action(() => {
+ ImageBox.setImageEditorOpen(true);
+ ImageBox.setImageEditorSource(this.choosePath(field.url));
+ ImageBox.addDoc = this.props.addDocument;
+ ImageBox.imageRootDoc = this.rootDoc;
+ }),
+ icon: 'pencil-alt',
+ });
if (!Doc.noviceMode) {
funcs.push({ description: 'Export to Google Photos', event: () => GooglePhotos.Transactions.UploadImages([this.props.Document]), icon: 'caret-square-right' });
@@ -287,10 +307,11 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
@computed private get url() {
const data = Cast(this.dataDoc[this.fieldKey], ImageField);
- return data ? data.url.href : undefined;
+ return data ? data.url?.href : undefined;
}
choosePath(url: URL) {
+ if (!url?.href) return '';
const lower = url.href.toLowerCase();
if (url.protocol === 'data') return url.href;
if (url.href.indexOf(window.location.origin) === -1 && url.href.indexOf('dashblobstore') === -1) return Utils.CorsProxy(url.href);
@@ -318,7 +339,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
if (!(data instanceof ImageField)) {
return null;
}
- const primary = data.url.href;
+ const primary = data.url?.href;
if (primary.includes(window.location.origin)) {
return null;
}
diff --git a/src/client/views/nodes/WebBoxRenderer.js b/src/client/views/nodes/WebBoxRenderer.js
index eb8064780..425ef3e54 100644
--- a/src/client/views/nodes/WebBoxRenderer.js
+++ b/src/client/views/nodes/WebBoxRenderer.js
@@ -42,21 +42,21 @@ var ForeignHtmlRenderer = function (styleSheets) {
url = CorsProxy(new URL(webUrl).origin + inurl);
} else if (!inurl.startsWith('http') && !inurl.startsWith('//')) {
url = CorsProxy(webUrl + '/' + inurl);
- } else if (inurl.startsWith('https')) {
+ } else if (inurl.startsWith('https') && !inurl.startsWith(window.location.origin)) {
url = CorsProxy(inurl);
}
xhr.open('GET', url);
xhr.responseType = 'blob';
xhr.onreadystatechange = async function () {
- if (xhr.readyState === 4 && xhr.status === 200) {
+ if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
const resBase64 = await binaryStringToBase64(xhr.response);
resolve({
resourceUrl: inurl,
resourceBase64: resBase64,
});
- } else if (xhr.readyState === 4) {
+ } else if (xhr.readyState === XMLHttpRequest.DONE) {
console.log("COULDN'T FIND: " + (inurl.startsWith('/') ? webUrl + inurl : inurl));
resolve({
resourceUrl: '',
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 2afbbb457..200d06a0b 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -70,6 +70,8 @@ import { schema } from './schema_rts';
import { SummaryView } from './SummaryView';
import applyDevTools = require('prosemirror-dev-tools');
import React = require('react');
+import { GPTPopup, GPTPopupMode } from '../../pdf/GPTPopup/GPTPopup';
+import { BsMarkdownFill } from 'react-icons/bs';
const translateGoogleApi = require('translate-google-api');
export const GoogleRef = 'googleDocId';
type PullHandler = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => void;
@@ -898,16 +900,17 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
event: () => (this.layoutDoc._layout_autoHeight = !this.layoutDoc._layout_autoHeight),
icon: this.Document._layout_autoHeight ? 'lock' : 'unlock',
});
- optionItems.push({ description: `show markdown options`, event: RTFMarkup.Instance.open, icon: 'text' });
+ optionItems.push({ description: `show markdown options`, event: RTFMarkup.Instance.open, icon: <BsMarkdownFill /> });
!options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'eye' });
this._downX = this._downY = Number.NaN;
};
- animateRes = (resIndex: number) => {
- if (resIndex < this.gptRes.length) {
- this.dataDoc.text = (this.dataDoc.text as RichTextField)?.Text + this.gptRes[resIndex];
+ animateRes = (resIndex: number, newText: string) => {
+ if (resIndex < newText.length) {
+ const marks = this._editorView?.state.storedMarks ?? [];
+ this._editorView?.dispatch(this._editorView.state.tr.setStoredMarks(marks).insertText(newText[resIndex]).setStoredMarks(marks));
setTimeout(() => {
- this.animateRes(resIndex + 1);
+ this.animateRes(resIndex + 1, newText);
}, 20);
}
};
@@ -915,45 +918,24 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
askGPT = action(async () => {
try {
let res = await gptAPICall((this.dataDoc.text as RichTextField)?.Text, GPTCallType.COMPLETION);
- if (res) {
- this.gptRes = res;
- this.animateRes(0);
+ if (!res) {
+ console.error('GPT call failed');
+ this.animateRes(0, 'Something went wrong.');
+ } else {
+ this.animateRes(0, res);
}
} catch (err) {
- console.log(err);
- this.dataDoc.text = (this.dataDoc.text as RichTextField)?.Text + 'Something went wrong';
+ console.error('GPT call failed');
+ this.animateRes(0, 'Something went wrong.');
}
});
generateImage = async () => {
- console.log('Generate image from text: ', (this.dataDoc.text as RichTextField)?.Text);
- try {
- let image_url = await gptImageCall((this.dataDoc.text as RichTextField)?.Text);
- if (image_url) {
- const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [image_url] });
- const source = result.accessPaths.agnostic.client;
- const newDoc = Docs.Create.ImageDocument(source, {
- x: NumCast(this.rootDoc.x) + NumCast(this.layoutDoc._width) + 10,
- y: NumCast(this.rootDoc.y),
- _height: 200,
- _width: 200,
- data_nativeWidth: result.nativeWidth,
- data_nativeHeight: result.nativeHeight,
- });
- if (Doc.IsInMyOverlay(this.rootDoc)) {
- newDoc.overlayX = this.rootDoc.x;
- newDoc.overlayY = NumCast(this.rootDoc.y) + NumCast(this.rootDoc._height);
- Doc.AddToMyOverlay(newDoc);
- } else {
- this.props.addDocument?.(newDoc);
- }
- // Create link between prompt and image
- DocUtils.MakeLink(this.rootDoc, newDoc, { link_relationship: 'Image Prompt' });
- }
- } catch (err) {
- console.log(err);
- return '';
- }
+ GPTPopup.Instance?.setTextAnchor(this.getAnchor(false));
+ GPTPopup.Instance?.setImgTargetDoc(this.rootDoc);
+ GPTPopup.Instance.addToCollection = this.props.addDocument;
+ GPTPopup.Instance.setImgDesc((this.dataDoc.text as RichTextField)?.Text);
+ GPTPopup.Instance.generateImage();
};
breakupDictation = () => {
@@ -1248,11 +1230,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props);
setTimeout(this.autoLink, 20);
}
- // Accessing editor and text doc for gpt assisted text edits
- if (this._editorView && selected) {
- AnchorMenu.Instance?.setEditorView(this._editorView);
- AnchorMenu.Instance?.setTextDoc(this.dataDoc);
- }
}),
{ fireImmediately: true }
);
diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.scss b/src/client/views/nodes/generativeFill/GenerativeFill.scss
new file mode 100644
index 000000000..c2669a950
--- /dev/null
+++ b/src/client/views/nodes/generativeFill/GenerativeFill.scss
@@ -0,0 +1,97 @@
+$navHeight: 5rem;
+$canvasSize: 1024px;
+$scale: 0.5;
+
+.generativeFillContainer {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 9999;
+ height: 100vh;
+ width: 100vw;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+
+ .generativeFillControls {
+ flex-shrink: 0;
+ height: $navHeight;
+ color: #000000;
+ background-color: #ffffff;
+ z-index: 999;
+ width: 100%;
+ display: flex;
+ gap: 3rem;
+ justify-content: space-between;
+ align-items: center;
+ border-bottom: 1px solid #c7cdd0;
+ padding: 0 2rem;
+
+ h1 {
+ font-size: 1.5rem;
+ }
+ }
+
+ .drawingArea {
+ cursor: none;
+ touch-action: none;
+ position: relative;
+ flex-grow: 1;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ background-color: #f0f4f6;
+
+ canvas {
+ display: block;
+ position: absolute;
+ transform-origin: 50% 50%;
+ }
+
+ .pointer {
+ pointer-events: none;
+ position: absolute;
+ border-radius: 50%;
+ width: 50px;
+ height: 50px;
+ border: 1px solid #ffffff;
+ transform: translate(-50%, -50%);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ .innerPointer {
+ width: 100%;
+ height: 100%;
+ border: 1px solid #000000;
+ border-radius: 50%;
+ }
+ }
+
+ .iconContainer {
+ position: absolute;
+ top: 2rem;
+ left: 2rem;
+ display: flex;
+ flex-direction: column;
+ gap: 2rem;
+ }
+
+ .editsBox {
+ position: absolute;
+ top: 2rem;
+ right: 2rem;
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+
+ img {
+ transition: all 0.2s ease-in-out;
+ &:hover {
+ opacity: 0.8;
+ }
+ }
+ }
+ }
+}
diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.tsx b/src/client/views/nodes/generativeFill/GenerativeFill.tsx
new file mode 100644
index 000000000..1400d0f6d
--- /dev/null
+++ b/src/client/views/nodes/generativeFill/GenerativeFill.tsx
@@ -0,0 +1,591 @@
+import './GenerativeFill.scss';
+import React = require('react');
+import { useEffect, useRef, useState } from 'react';
+import { APISuccess, ImageUtility } from './generativeFillUtils/ImageHandler';
+import { BrushHandler } from './generativeFillUtils/BrushHandler';
+import { IconButton } from 'browndash-components';
+import { Checkbox, FormControlLabel, Slider, TextField } from '@mui/material';
+import { CursorData, ImageDimensions, Point } from './generativeFillUtils/generativeFillInterfaces';
+import { activeColor, canvasSize, eraserColor, freeformRenderSize, newCollectionSize, offsetDistanceY, offsetX } from './generativeFillUtils/generativeFillConstants';
+import { PointerHandler } from './generativeFillUtils/PointerHandler';
+import { IoMdUndo, IoMdRedo } from 'react-icons/io';
+import { MainView } from '../../MainView';
+import { Doc, DocListCast } from '../../../../fields/Doc';
+import { Networking } from '../../../Network';
+import { Utils } from '../../../../Utils';
+import { DocUtils, Docs } from '../../../documents/Documents';
+import { NumCast } from '../../../../fields/Types';
+import { CollectionDockingView } from '../../collections/CollectionDockingView';
+import { OpenWhereMod } from '../DocumentView';
+import Buttons from './GenerativeFillButtons';
+import { List } from '../../../../fields/List';
+import { CgClose } from 'react-icons/cg';
+import { ImageBox } from '../ImageBox';
+
+enum BrushStyle {
+ ADD,
+ SUBTRACT,
+ MARQUEE,
+}
+
+interface GenerativeFillProps {
+ imageEditorOpen: boolean;
+ imageEditorSource: string;
+ imageRootDoc: Doc | undefined;
+ addDoc: ((doc: Doc | Doc[], annotationKey?: string) => boolean) | undefined;
+}
+
+// Added field on image doc: gen_fill_children: List of children Docs
+
+const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc }: GenerativeFillProps) => {
+ const canvasRef = useRef<HTMLCanvasElement>(null);
+ const canvasBackgroundRef = useRef<HTMLCanvasElement>(null);
+ const drawingAreaRef = useRef<HTMLDivElement>(null);
+ const [cursorData, setCursorData] = useState<CursorData>({
+ x: 0,
+ y: 0,
+ width: 150,
+ });
+ const [isBrushing, setIsBrushing] = useState(false);
+ const [canvasScale, setCanvasScale] = useState(0.5);
+ // format: array of [image source, corresponding image Doc]
+ const [edits, setEdits] = useState<(string | Doc)[][]>([]);
+ const [edited, setEdited] = useState(false);
+ const [brushStyle, setBrushStyle] = useState<BrushStyle>(BrushStyle.ADD);
+ const [input, setInput] = useState('');
+ const [loading, setLoading] = useState(false);
+ const [canvasDims, setCanvasDims] = useState<ImageDimensions>({
+ width: canvasSize,
+ height: canvasSize,
+ });
+ // whether to create a new collection or not
+ const [isNewCollection, setIsNewCollection] = useState(true);
+ // the current image in the main canvas
+ const currImg = useRef<HTMLImageElement | null>(null);
+ // the unedited version of each generation (parent)
+ const originalImg = useRef<HTMLImageElement | null>(null);
+ const originalDoc = useRef<Doc | null>(null);
+ // stores history of data urls
+ const undoStack = useRef<string[]>([]);
+ // stores redo stack
+ const redoStack = useRef<string[]>([]);
+
+ // references to keep track of tree structure
+ const newCollectionRef = useRef<Doc | null>(null);
+ const parentDoc = useRef<Doc | null>(null);
+ const childrenDocs = useRef<Doc[]>([]);
+
+ // Undo and Redo
+ const handleUndo = () => {
+ const ctx = ImageUtility.getCanvasContext(canvasRef);
+ if (!ctx || !currImg.current || !canvasRef.current) return;
+
+ const target = undoStack.current[undoStack.current.length - 1];
+ if (!target) {
+ ImageUtility.drawImgToCanvas(currImg.current, canvasRef, canvasDims.width, canvasDims.height);
+ } else {
+ redoStack.current = [...redoStack.current, canvasRef.current.toDataURL()];
+ const img = new Image();
+ img.src = target;
+ ImageUtility.drawImgToCanvas(img, canvasRef, canvasDims.width, canvasDims.height);
+ undoStack.current = undoStack.current.slice(0, -1);
+ }
+ };
+
+ const handleRedo = () => {
+ const ctx = ImageUtility.getCanvasContext(canvasRef);
+ if (!ctx || !currImg.current || !canvasRef.current) return;
+
+ const target = redoStack.current[redoStack.current.length - 1];
+ if (!target) {
+ } else {
+ undoStack.current = [...undoStack.current, canvasRef.current?.toDataURL()];
+ const img = new Image();
+ img.src = target;
+ ImageUtility.drawImgToCanvas(img, canvasRef, canvasDims.width, canvasDims.height);
+ redoStack.current = redoStack.current.slice(0, -1);
+ }
+ };
+
+ // resets any erase strokes
+ const handleReset = () => {
+ if (!canvasRef.current || !currImg.current) return;
+ const ctx = ImageUtility.getCanvasContext(canvasRef);
+ if (!ctx) return;
+ ctx.clearRect(0, 0, canvasSize, canvasSize);
+ undoStack.current = [];
+ redoStack.current = [];
+ ImageUtility.drawImgToCanvas(currImg.current, canvasRef, canvasDims.width, canvasDims.height);
+ };
+
+ // initiate brushing
+ const handlePointerDown = (e: React.PointerEvent) => {
+ const canvas = canvasRef.current;
+ if (!canvas) return;
+ const ctx = ImageUtility.getCanvasContext(canvasRef);
+ if (!ctx) return;
+
+ undoStack.current = [...undoStack.current, canvasRef.current.toDataURL()];
+ redoStack.current = [];
+
+ setIsBrushing(true);
+ const { x, y } = PointerHandler.getPointRelativeToElement(canvas, e, canvasScale);
+ BrushHandler.brushCircleOverlay(x, y, cursorData.width / 2 / canvasScale, ctx, eraserColor, brushStyle === BrushStyle.SUBTRACT);
+ };
+
+ // stop brushing, push to undo stack
+ const handlePointerUp = (e: React.PointerEvent) => {
+ const ctx = ImageUtility.getCanvasContext(canvasBackgroundRef);
+ if (!ctx) return;
+ if (!isBrushing) return;
+ setIsBrushing(false);
+ };
+
+ // handles brushing on pointer movement
+ useEffect(() => {
+ if (!isBrushing) return;
+ const canvas = canvasRef.current;
+ if (!canvas) return;
+ const ctx = ImageUtility.getCanvasContext(canvasRef);
+ if (!ctx) return;
+
+ const handlePointerMove = (e: PointerEvent) => {
+ const currPoint = PointerHandler.getPointRelativeToElement(canvas, e, canvasScale);
+ const lastPoint: Point = {
+ x: currPoint.x - e.movementX / canvasScale,
+ y: currPoint.y - e.movementY / canvasScale,
+ };
+ BrushHandler.createBrushPathOverlay(lastPoint, currPoint, cursorData.width / 2 / canvasScale, ctx, eraserColor, brushStyle === BrushStyle.SUBTRACT);
+ };
+
+ drawingAreaRef.current?.addEventListener('pointermove', handlePointerMove);
+ return () => {
+ drawingAreaRef.current?.removeEventListener('pointermove', handlePointerMove);
+ };
+ }, [isBrushing]);
+
+ // first load
+ useEffect(() => {
+ const loadInitial = async () => {
+ if (!imageEditorSource || imageEditorSource === '') return;
+ const img = new Image();
+ const res = await ImageUtility.urlToBase64(imageEditorSource);
+ if (!res) return;
+ img.src = `data:image/png;base64,${res}`;
+
+ img.onload = () => {
+ currImg.current = img;
+ originalImg.current = img;
+ const imgWidth = img.naturalWidth;
+ const imgHeight = img.naturalHeight;
+ const scale = Math.min(canvasSize / imgWidth, canvasSize / imgHeight);
+ const width = imgWidth * scale;
+ const height = imgHeight * scale;
+ setCanvasDims({ width, height });
+ };
+ };
+
+ loadInitial();
+
+ // cleanup
+ return () => {
+ setInput('');
+ setEdited(false);
+ newCollectionRef.current = null;
+ parentDoc.current = null;
+ childrenDocs.current = [];
+ currImg.current = null;
+ originalImg.current = null;
+ originalDoc.current = null;
+ undoStack.current = [];
+ redoStack.current = [];
+ ImageUtility.clearCanvas(canvasRef);
+ };
+ }, [canvasRef, imageEditorSource]);
+
+ // once the appropriate dimensions are set, draw the image to the canvas
+ useEffect(() => {
+ if (!currImg.current) return;
+ ImageUtility.drawImgToCanvas(currImg.current, canvasRef, canvasDims.width, canvasDims.height);
+ }, [canvasDims]);
+
+ // handles brush sizing
+ useEffect(() => {
+ const handleKeyPress = (e: KeyboardEvent) => {
+ if (e.key === 'ArrowUp') {
+ e.preventDefault();
+ e.stopPropagation();
+ setCursorData(data => ({ ...data, width: data.width + 5 }));
+ } else if (e.key === 'ArrowDown') {
+ e.preventDefault();
+ e.stopPropagation();
+ setCursorData(data => (data.width >= 20 ? { ...data, width: data.width - 5 } : data));
+ }
+ };
+ window.addEventListener('keydown', handleKeyPress);
+ return () => window.removeEventListener('keydown', handleKeyPress);
+ }, []);
+
+ // handle pinch zoom
+ useEffect(() => {
+ const handlePinch = (e: WheelEvent) => {
+ e.preventDefault();
+ e.stopPropagation();
+ const delta = e.deltaY;
+ const scaleFactor = delta > 0 ? 0.98 : 1.02;
+ setCanvasScale(prevScale => prevScale * scaleFactor);
+ };
+
+ drawingAreaRef.current?.addEventListener('wheel', handlePinch, {
+ passive: false,
+ });
+ return () => drawingAreaRef.current?.removeEventListener('wheel', handlePinch);
+ }, [drawingAreaRef]);
+
+ // updates the current position of the cursor
+ const updateCursorData = (e: React.PointerEvent) => {
+ const drawingArea = drawingAreaRef.current;
+ if (!drawingArea) return;
+ const { x, y } = PointerHandler.getPointRelativeToElement(drawingArea, e, 1);
+ setCursorData(data => ({
+ ...data,
+ x,
+ y,
+ }));
+ };
+
+ // Get AI Edit
+ const getEdit = async () => {
+ const img = currImg.current;
+ if (!img) return;
+ const canvas = canvasRef.current;
+ if (!canvas) return;
+ const ctx = ImageUtility.getCanvasContext(canvasRef);
+ if (!ctx) return;
+ setLoading(true);
+ setEdited(true);
+ try {
+ const canvasOriginalImg = ImageUtility.getCanvasImg(img);
+ if (!canvasOriginalImg) return;
+ const canvasMask = ImageUtility.getCanvasMask(canvas, canvasOriginalImg);
+ if (!canvasMask) return;
+ const maskBlob = await ImageUtility.canvasToBlob(canvasMask);
+ const imgBlob = await ImageUtility.canvasToBlob(canvasOriginalImg);
+ const res = await ImageUtility.getEdit(imgBlob, maskBlob, input !== '' ? input + ' in the same style' : 'Fill in the image in the same style', 2);
+ // const res = await ImageUtility.mockGetEdit(img.src);
+
+ // create first image
+ if (!newCollectionRef.current) {
+ if (!isNewCollection && imageRootDoc) {
+ // if the parent hasn't been set yet
+ if (!parentDoc.current) parentDoc.current = imageRootDoc;
+ } else {
+ if (!(originalImg.current && imageRootDoc)) return;
+ // create new collection and add it to the view
+ newCollectionRef.current = Docs.Create.FreeformDocument([], {
+ x: NumCast(imageRootDoc.x) + NumCast(imageRootDoc._width) + offsetX,
+ y: NumCast(imageRootDoc.y),
+ _width: newCollectionSize,
+ _height: newCollectionSize,
+ title: 'Image edit collection',
+ });
+ DocUtils.MakeLink(imageRootDoc, newCollectionRef.current, { link_relationship: 'Image Edit Version History', link_displayLine: false });
+
+ // opening new tab
+ CollectionDockingView.AddSplit(newCollectionRef.current, OpenWhereMod.right);
+
+ // add the doc to the main freeform
+ await createNewImgDoc(originalImg.current, true);
+ }
+ } else {
+ childrenDocs.current = [];
+ }
+
+ originalImg.current = currImg.current;
+ originalDoc.current = parentDoc.current;
+ const { urls } = res as APISuccess;
+ const imgUrls = await Promise.all(urls.map(url => ImageUtility.convertImgToCanvasUrl(url, canvasDims.width, canvasDims.height)));
+ const imgRes = await Promise.all(
+ imgUrls.map(async url => {
+ const saveRes = await onSave(url);
+ return [url, saveRes as Doc];
+ })
+ );
+ setEdits(imgRes);
+ const image = new Image();
+ image.src = imgUrls[0];
+ ImageUtility.drawImgToCanvas(image, canvasRef, canvasDims.width, canvasDims.height);
+ currImg.current = image;
+ parentDoc.current = imgRes[0][1] as Doc;
+ } catch (err) {
+ console.log(err);
+ }
+ setLoading(false);
+ };
+
+ // adjusts all the img positions to be aligned
+ const adjustImgPositions = () => {
+ if (!parentDoc.current) return;
+ const startY = NumCast(parentDoc.current.y);
+ const children = DocListCast(parentDoc.current.gen_fill_children);
+ const len = children.length;
+ let initialYPositions: number[] = [];
+ for (let i = 0; i < len; i++) {
+ initialYPositions.push(startY + i * offsetDistanceY);
+ }
+ children.forEach((doc, i) => {
+ if (len % 2 === 1) {
+ doc.y = initialYPositions[i] - Math.floor(len / 2) * offsetDistanceY;
+ } else {
+ doc.y = initialYPositions[i] - (len / 2 - 1 / 2) * offsetDistanceY;
+ }
+ });
+ };
+
+ // creates a new image document and returns its reference
+ const createNewImgDoc = async (img: HTMLImageElement, firstDoc: boolean): Promise<Doc | undefined> => {
+ if (!imageRootDoc) return;
+ const src = img.src;
+ const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [src] });
+ const source = Utils.prepend(result.accessPaths.agnostic.client);
+
+ if (firstDoc) {
+ const x = 0;
+ const initialY = 0;
+ const newImg = Docs.Create.ImageDocument(source, {
+ x: x,
+ y: initialY,
+ _height: freeformRenderSize,
+ _width: freeformRenderSize,
+ data_nativeWidth: result.nativeWidth,
+ data_nativeHeight: result.nativeHeight,
+ });
+ if (isNewCollection && newCollectionRef.current) {
+ Doc.AddDocToList(newCollectionRef.current, undefined, newImg);
+ } else {
+ addDoc?.(newImg);
+ }
+ parentDoc.current = newImg;
+ return newImg;
+ } else {
+ if (!parentDoc.current) return;
+ const x = NumCast(parentDoc.current.x) + freeformRenderSize + offsetX;
+ const initialY = 0;
+
+ const newImg = Docs.Create.ImageDocument(source, {
+ x: x,
+ y: initialY,
+ _height: freeformRenderSize,
+ _width: freeformRenderSize,
+ data_nativeWidth: result.nativeWidth,
+ data_nativeHeight: result.nativeHeight,
+ });
+
+ const parentList = DocListCast(parentDoc.current.gen_fill_children);
+ if (parentList.length > 0) {
+ parentList.push(newImg);
+ parentDoc.current.gen_fill_children = new List<Doc>(parentList);
+ } else {
+ parentDoc.current.gen_fill_children = new List<Doc>([newImg]);
+ }
+
+ DocUtils.MakeLink(parentDoc.current, newImg, { link_relationship: `Image edit; Prompt: ${input}`, link_displayLine: true });
+ adjustImgPositions();
+
+ if (isNewCollection && newCollectionRef.current) {
+ Doc.AddDocToList(newCollectionRef.current, undefined, newImg);
+ } else {
+ addDoc?.(newImg);
+ }
+ return newImg;
+ }
+ };
+
+ // Saves an image to the collection
+ const onSave = async (src: string) => {
+ const img = new Image();
+ img.src = src;
+ if (!currImg.current || !originalImg.current || !imageRootDoc) return;
+ try {
+ const res = await createNewImgDoc(img, false);
+ return res;
+ } catch (err) {
+ console.log(err);
+ }
+ };
+
+ // Closes the editor view
+ const handleViewClose = () => {
+ ImageBox.setImageEditorOpen(false);
+ ImageBox.setImageEditorSource('');
+ if (newCollectionRef.current) {
+ newCollectionRef.current.fitContentOnce = true;
+ }
+ setEdits([]);
+ };
+
+ return (
+ <div className="generativeFillContainer" style={{ display: imageEditorOpen ? 'flex' : 'none' }}>
+ <div className="generativeFillControls">
+ <h1>Image Editor</h1>
+ <div style={{ display: 'flex', alignItems: 'center', gap: '1.5rem' }}>
+ <FormControlLabel
+ control={
+ <Checkbox
+ // disable once edited has been clicked (doesn't make sense to change after first edit)
+ disabled={edited}
+ checked={isNewCollection}
+ onChange={e => {
+ setIsNewCollection(prev => !prev);
+ }}
+ />
+ }
+ label={'Create New Collection'}
+ labelPlacement="end"
+ sx={{ whiteSpace: 'nowrap' }}
+ />
+ <Buttons getEdit={getEdit} loading={loading} onReset={handleReset} />
+ <IconButton color={activeColor} tooltip="close" icon={<CgClose size={'16px'} />} onClick={handleViewClose} />
+ </div>
+ </div>
+ {/* Main canvas for editing */}
+ <div
+ className="drawingArea" // this only works if pointerevents: none is set on the custom pointer
+ ref={drawingAreaRef}
+ onPointerOver={updateCursorData}
+ onPointerMove={updateCursorData}
+ onPointerDown={handlePointerDown}
+ onPointerUp={handlePointerUp}>
+ <canvas ref={canvasRef} width={canvasDims.width} height={canvasDims.height} style={{ transform: `scale(${canvasScale})` }} />
+ <canvas ref={canvasBackgroundRef} width={canvasDims.width} height={canvasDims.height} style={{ transform: `scale(${canvasScale})` }} />
+ <div
+ className="pointer"
+ style={{
+ left: cursorData.x,
+ top: cursorData.y,
+ width: cursorData.width,
+ height: cursorData.width,
+ }}>
+ <div className="innerPointer"></div>
+ </div>
+ {/* Icons */}
+ <div className="iconContainer">
+ {/* Undo and Redo */}
+ <IconButton
+ style={{ cursor: 'pointer' }}
+ onPointerDown={e => {
+ e.stopPropagation();
+ handleUndo();
+ }}
+ onPointerUp={e => {
+ e.stopPropagation();
+ }}
+ color={activeColor}
+ tooltip="Undo"
+ icon={<IoMdUndo />}
+ />
+ <IconButton
+ style={{ cursor: 'pointer' }}
+ onPointerDown={e => {
+ e.stopPropagation();
+ handleRedo();
+ }}
+ onPointerUp={e => {
+ e.stopPropagation();
+ }}
+ color={activeColor}
+ tooltip="Redo"
+ icon={<IoMdRedo />}
+ />
+ <div onPointerDown={e => e.stopPropagation()} style={{ height: 225, width: '100%', display: 'flex', justifyContent: 'center', cursor: 'pointer' }}>
+ <Slider
+ sx={{
+ '& input[type="range"]': {
+ WebkitAppearance: 'slider-vertical',
+ },
+ }}
+ orientation="vertical"
+ min={25}
+ max={500}
+ defaultValue={150}
+ size="small"
+ valueLabelDisplay="auto"
+ onChange={(e, val) => {
+ setCursorData(prev => ({ ...prev, width: val as number }));
+ }}
+ />
+ </div>
+ </div>
+ {/* Edits thumbnails*/}
+ <div className="editsBox">
+ {edits.map((edit, i) => (
+ <img
+ key={i}
+ width={75}
+ src={edit[0] as string}
+ style={{ cursor: 'pointer' }}
+ onClick={async () => {
+ const img = new Image();
+ img.src = edit[0] as string;
+ ImageUtility.drawImgToCanvas(img, canvasRef, canvasDims.width, canvasDims.height);
+ currImg.current = img;
+ parentDoc.current = edit[1] as Doc;
+ }}
+ />
+ ))}
+ {/* Original img thumbnail */}
+ {edits.length > 0 && (
+ <div style={{ position: 'relative' }}>
+ <label
+ style={{
+ position: 'absolute',
+ bottom: 10,
+ left: 10,
+ color: '#ffffff',
+ fontSize: '0.8rem',
+ letterSpacing: '1px',
+ textTransform: 'uppercase',
+ }}>
+ Original
+ </label>
+ <img
+ width={75}
+ src={originalImg.current?.src}
+ style={{ cursor: 'pointer' }}
+ onClick={() => {
+ if (!originalImg.current) return;
+ const img = new Image();
+ img.src = originalImg.current.src;
+ ImageUtility.drawImgToCanvas(img, canvasRef, canvasDims.width, canvasDims.height);
+ currImg.current = img;
+ parentDoc.current = originalDoc.current;
+ }}
+ />
+ </div>
+ )}
+ </div>
+ </div>
+ <div>
+ <TextField
+ value={input}
+ onChange={e => setInput(e.target.value)}
+ disabled={isBrushing}
+ type="text"
+ label="Prompt"
+ placeholder="Prompt..."
+ InputLabelProps={{ style: { fontSize: '16px' } }}
+ inputProps={{ style: { fontSize: '16px' } }}
+ sx={{
+ backgroundColor: '#ffffff',
+ position: 'absolute',
+ bottom: '16px',
+ transform: 'translateX(calc(50vw - 50%))',
+ width: 'calc(100vw - 64px)',
+ }}
+ />
+ </div>
+ </div>
+ );
+};
+
+export default GenerativeFill;
diff --git a/src/client/views/nodes/generativeFill/GenerativeFillButtons.scss b/src/client/views/nodes/generativeFill/GenerativeFillButtons.scss
new file mode 100644
index 000000000..0180ef904
--- /dev/null
+++ b/src/client/views/nodes/generativeFill/GenerativeFillButtons.scss
@@ -0,0 +1,4 @@
+.generativeFillBtnContainer {
+ display: flex;
+ gap: 1rem;
+}
diff --git a/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx b/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx
new file mode 100644
index 000000000..10eca358e
--- /dev/null
+++ b/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx
@@ -0,0 +1,44 @@
+import './GenerativeFillButtons.scss';
+import React = require('react');
+import ReactLoading from 'react-loading';
+import { activeColor } from './generativeFillUtils/generativeFillConstants';
+import { Button, IconButton, Type } from 'browndash-components';
+import { AiOutlineInfo } from 'react-icons/ai';
+
+interface ButtonContainerProps {
+ getEdit: () => Promise<void>;
+ loading: boolean;
+ onReset: () => void;
+}
+
+const Buttons = ({ loading, getEdit, onReset }: ButtonContainerProps) => {
+ return (
+ <div className="generativeFillBtnContainer">
+ <Button text="RESET" type={Type.PRIM} color={activeColor} onClick={onReset} />
+ {loading ? (
+ <Button
+ text="GET EDITS"
+ type={Type.TERT}
+ color={activeColor}
+ icon={<ReactLoading type="spin" color={'#ffffff'} width={20} height={20} />}
+ iconPlacement="right"
+ onClick={() => {
+ if (!loading) getEdit();
+ }}
+ />
+ ) : (
+ <Button
+ text="GET EDITS"
+ type={Type.TERT}
+ color={activeColor}
+ onClick={() => {
+ if (!loading) getEdit();
+ }}
+ />
+ )}
+ <IconButton type={Type.SEC} color={activeColor} tooltip="Open Documentation" icon={<AiOutlineInfo size={'16px'} />} onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/features/generativeai/#editing', '_blank')} />
+ </div>
+ );
+};
+
+export default Buttons;
diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/BrushHandler.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/BrushHandler.ts
new file mode 100644
index 000000000..f4ec70fbc
--- /dev/null
+++ b/src/client/views/nodes/generativeFill/generativeFillUtils/BrushHandler.ts
@@ -0,0 +1,25 @@
+import { GenerativeFillMathHelpers } from './GenerativeFillMathHelpers';
+import { eraserColor } from './generativeFillConstants';
+import { Point } from './generativeFillInterfaces';
+
+export class BrushHandler {
+ static brushCircleOverlay = (x: number, y: number, brushRadius: number, ctx: CanvasRenderingContext2D, fillColor: string, erase: boolean) => {
+ ctx.globalCompositeOperation = 'destination-out';
+ ctx.fillStyle = fillColor;
+ ctx.shadowColor = eraserColor;
+ ctx.shadowBlur = 5;
+ ctx.beginPath();
+ ctx.arc(x, y, brushRadius, 0, 2 * Math.PI);
+ ctx.fill();
+ ctx.closePath();
+ };
+
+ static createBrushPathOverlay = (startPoint: Point, endPoint: Point, brushRadius: number, ctx: CanvasRenderingContext2D, fillColor: string, erase: boolean) => {
+ const dist = GenerativeFillMathHelpers.distanceBetween(startPoint, endPoint);
+
+ for (let i = 0; i < dist; i += 5) {
+ const s = i / dist;
+ BrushHandler.brushCircleOverlay(startPoint.x * (1 - s) + endPoint.x * s, startPoint.y * (1 - s) + endPoint.y * s, brushRadius, ctx, fillColor, erase);
+ }
+ };
+}
diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/GenerativeFillMathHelpers.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/GenerativeFillMathHelpers.ts
new file mode 100644
index 000000000..97e03ff20
--- /dev/null
+++ b/src/client/views/nodes/generativeFill/generativeFillUtils/GenerativeFillMathHelpers.ts
@@ -0,0 +1,10 @@
+import { Point } from './generativeFillInterfaces';
+
+export class GenerativeFillMathHelpers {
+ static distanceBetween = (p1: Point, p2: Point) => {
+ return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
+ };
+ static angleBetween = (p1: Point, p2: Point) => {
+ return Math.atan2(p2.x - p1.x, p2.y - p1.y);
+ };
+}
diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts
new file mode 100644
index 000000000..47a14135f
--- /dev/null
+++ b/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts
@@ -0,0 +1,314 @@
+import { RefObject } from 'react';
+import { bgColor, canvasSize } from './generativeFillConstants';
+
+export interface APISuccess {
+ status: 'success';
+ urls: string[];
+}
+
+export interface APIError {
+ status: 'error';
+ message: string;
+}
+
+export class ImageUtility {
+ /**
+ *
+ * @param canvas Canvas to convert
+ * @returns Blob of canvas
+ */
+ static canvasToBlob = (canvas: HTMLCanvasElement): Promise<Blob> => {
+ return new Promise(resolve => {
+ canvas.toBlob(blob => {
+ if (blob) {
+ resolve(blob);
+ }
+ }, 'image/png');
+ });
+ };
+
+ // given a square api image, get the cropped img
+ static getCroppedImg = (img: HTMLImageElement, width: number, height: number): HTMLCanvasElement | undefined => {
+ // Create a new canvas element
+ const canvas = document.createElement('canvas');
+ canvas.width = width;
+ canvas.height = height;
+ const ctx = canvas.getContext('2d');
+ if (ctx) {
+ // Clear the canvas
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ if (width < height) {
+ // horizontal padding, x offset
+ const xOffset = (canvasSize - width) / 2;
+ ctx.drawImage(img, xOffset, 0, canvas.width, canvas.height, 0, 0, canvas.width, canvas.height);
+ } else {
+ // vertical padding, y offset
+ const yOffset = (canvasSize - height) / 2;
+ ctx.drawImage(img, 0, yOffset, canvas.width, canvas.height, 0, 0, canvas.width, canvas.height);
+ }
+ return canvas;
+ }
+ };
+
+ // converts an image to a canvas data url
+ static convertImgToCanvasUrl = async (imageSrc: string, width: number, height: number): Promise<string> => {
+ return new Promise<string>((resolve, reject) => {
+ const img = new Image();
+ img.onload = () => {
+ const canvas = this.getCroppedImg(img, width, height);
+ if (canvas) {
+ const dataUrl = canvas.toDataURL();
+ resolve(dataUrl);
+ }
+ };
+ img.onerror = error => {
+ reject(error);
+ };
+ img.src = imageSrc;
+ });
+ };
+
+ // calls the openai api to get image edits
+ static getEdit = async (imgBlob: Blob, maskBlob: Blob, prompt: string, n?: number): Promise<APISuccess | APIError> => {
+ const apiUrl = 'https://api.openai.com/v1/images/edits';
+ const fd = new FormData();
+ fd.append('image', imgBlob, 'image.png');
+ fd.append('mask', maskBlob, 'mask.png');
+ fd.append('prompt', prompt);
+ fd.append('size', '1024x1024');
+ fd.append('n', n ? JSON.stringify(n) : '1');
+ fd.append('response_format', 'b64_json');
+
+ try {
+ const res = await fetch(apiUrl, {
+ method: 'POST',
+ headers: {
+ Authorization: `Bearer ${process.env.OPENAI_KEY}`,
+ },
+ body: fd,
+ });
+ const data = await res.json();
+ console.log(data.data);
+ return {
+ status: 'success',
+ urls: (data.data as { b64_json: string }[]).map(data => `data:image/png;base64,${data.b64_json}`),
+ };
+ } catch (err) {
+ console.log(err);
+ return { status: 'error', message: 'API error.' };
+ }
+ };
+
+ // mock api call
+ static mockGetEdit = async (mockSrc: string): Promise<APISuccess | APIError> => {
+ return {
+ status: 'success',
+ urls: [mockSrc, mockSrc, mockSrc],
+ };
+ };
+
+ // Gets the canvas rendering context of a canvas
+ static getCanvasContext = (canvasRef: RefObject<HTMLCanvasElement>): CanvasRenderingContext2D | null => {
+ if (!canvasRef.current) return null;
+ const ctx = canvasRef.current.getContext('2d');
+ if (!ctx) return null;
+ return ctx;
+ };
+
+ // Helper for downloading the canvas (for debugging)
+ static downloadCanvas = (canvas: HTMLCanvasElement) => {
+ const url = canvas.toDataURL();
+ const downloadLink = document.createElement('a');
+ downloadLink.href = url;
+ downloadLink.download = 'canvas';
+
+ downloadLink.click();
+ downloadLink.remove();
+ };
+
+ // Download the canvas (for debugging)
+ static downloadImageCanvas = (imgUrl: string) => {
+ const img = new Image();
+ img.src = imgUrl;
+ img.onload = () => {
+ const canvas = document.createElement('canvas');
+ canvas.width = canvasSize;
+ canvas.height = canvasSize;
+ const ctx = canvas.getContext('2d');
+ ctx?.drawImage(img, 0, 0, canvasSize, canvasSize);
+
+ this.downloadCanvas(canvas);
+ };
+ };
+
+ // Clears the canvas
+ static clearCanvas = (canvasRef: React.RefObject<HTMLCanvasElement>) => {
+ const ctx = this.getCanvasContext(canvasRef);
+ if (!ctx || !canvasRef.current) return;
+ ctx.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height);
+ };
+
+ // Draws the image to the current canvas
+ static drawImgToCanvas = (img: HTMLImageElement, canvasRef: React.RefObject<HTMLCanvasElement>, width: number, height: number) => {
+ const drawImg = (img: HTMLImageElement) => {
+ const ctx = this.getCanvasContext(canvasRef);
+ if (!ctx) return;
+ ctx.globalCompositeOperation = 'source-over';
+ ctx.clearRect(0, 0, width, height);
+ ctx.drawImage(img, 0, 0, width, height);
+ };
+
+ if (img.complete) {
+ drawImg(img);
+ } else {
+ img.onload = () => {
+ drawImg(img);
+ };
+ }
+ };
+
+ // Gets the image mask for the openai endpoint
+ static getCanvasMask = (srcCanvas: HTMLCanvasElement, paddedCanvas: HTMLCanvasElement): HTMLCanvasElement | undefined => {
+ const canvas = document.createElement('canvas');
+ canvas.width = canvasSize;
+ canvas.height = canvasSize;
+ const ctx = canvas.getContext('2d');
+ if (!ctx) return;
+ ctx?.clearRect(0, 0, canvasSize, canvasSize);
+ ctx.drawImage(paddedCanvas, 0, 0);
+
+ // extract and set padding data
+ if (srcCanvas.height > srcCanvas.width) {
+ // horizontal padding, x offset
+ const xOffset = (canvasSize - srcCanvas.width) / 2;
+ ctx?.clearRect(xOffset, 0, srcCanvas.width, srcCanvas.height);
+ ctx.drawImage(srcCanvas, xOffset, 0, srcCanvas.width, srcCanvas.height);
+ } else {
+ // vertical padding, y offset
+ const yOffset = (canvasSize - srcCanvas.height) / 2;
+ ctx?.clearRect(0, yOffset, srcCanvas.width, srcCanvas.height);
+ ctx.drawImage(srcCanvas, 0, yOffset, srcCanvas.width, srcCanvas.height);
+ }
+ return canvas;
+ };
+
+ // Fills in the blank areas of the image with an image reflection (to fill in a square-shaped canvas)
+ static drawHorizontalReflection = (ctx: CanvasRenderingContext2D, canvas: HTMLCanvasElement, xOffset: number) => {
+ const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
+ const data = imageData.data;
+ for (let i = 0; i < canvas.height; i++) {
+ for (let j = 0; j < xOffset; j++) {
+ const targetIdx = 4 * (i * canvas.width + j);
+ const sourceI = i;
+ const sourceJ = xOffset + (xOffset - j);
+ const sourceIdx = 4 * (sourceI * canvas.width + sourceJ);
+ data[targetIdx] = data[sourceIdx];
+ data[targetIdx + 1] = data[sourceIdx + 1];
+ data[targetIdx + 2] = data[sourceIdx + 2];
+ }
+ }
+ for (let i = 0; i < canvas.height; i++) {
+ for (let j = canvas.width - 1; j >= canvas.width - 1 - xOffset; j--) {
+ const targetIdx = 4 * (i * canvas.width + j);
+ const sourceI = i;
+ const sourceJ = canvas.width - 1 - xOffset - (xOffset - (canvas.width - j));
+ const sourceIdx = 4 * (sourceI * canvas.width + sourceJ);
+ data[targetIdx] = data[sourceIdx];
+ data[targetIdx + 1] = data[sourceIdx + 1];
+ data[targetIdx + 2] = data[sourceIdx + 2];
+ }
+ }
+ ctx.putImageData(imageData, 0, 0);
+ };
+
+ // Fills in the blank areas of the image with an image reflection (to fill in a square-shaped canvas)
+ static drawVerticalReflection = (ctx: CanvasRenderingContext2D, canvas: HTMLCanvasElement, yOffset: number) => {
+ const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
+ const data = imageData.data;
+ for (let j = 0; j < canvas.width; j++) {
+ for (let i = 0; i < yOffset; i++) {
+ const targetIdx = 4 * (i * canvas.width + j);
+ const sourceJ = j;
+ const sourceI = yOffset + (yOffset - i);
+ const sourceIdx = 4 * (sourceI * canvas.width + sourceJ);
+ data[targetIdx] = data[sourceIdx];
+ data[targetIdx + 1] = data[sourceIdx + 1];
+ data[targetIdx + 2] = data[sourceIdx + 2];
+ }
+ }
+ for (let j = 0; j < canvas.width; j++) {
+ for (let i = canvas.height - 1; i >= canvas.height - 1 - yOffset; i--) {
+ const targetIdx = 4 * (i * canvas.width + j);
+ const sourceJ = j;
+ const sourceI = canvas.height - 1 - yOffset - (yOffset - (canvas.height - i));
+ const sourceIdx = 4 * (sourceI * canvas.width + sourceJ);
+ data[targetIdx] = data[sourceIdx];
+ data[targetIdx + 1] = data[sourceIdx + 1];
+ data[targetIdx + 2] = data[sourceIdx + 2];
+ }
+ }
+ ctx.putImageData(imageData, 0, 0);
+ };
+
+ // Gets the unaltered (besides filling in padding) version of the image for the api call
+ static getCanvasImg = (img: HTMLImageElement): HTMLCanvasElement | undefined => {
+ const canvas = document.createElement('canvas');
+ canvas.width = canvasSize;
+ canvas.height = canvasSize;
+ const ctx = canvas.getContext('2d');
+ if (!ctx) return;
+ // fix scaling
+ const scale = Math.min(canvasSize / img.width, canvasSize / img.height);
+ const width = Math.floor(img.width * scale);
+ const height = Math.floor(img.height * scale);
+ ctx?.clearRect(0, 0, canvasSize, canvasSize);
+ ctx.fillStyle = bgColor;
+ ctx.fillRect(0, 0, canvasSize, canvasSize);
+
+ // extract and set padding data
+ if (img.naturalHeight > img.naturalWidth) {
+ // horizontal padding, x offset
+ const xOffset = Math.floor((canvasSize - width) / 2);
+ ctx.drawImage(img, xOffset, 0, width, height);
+
+ // draw reflected image padding
+ this.drawHorizontalReflection(ctx, canvas, xOffset);
+ } else {
+ // vertical padding, y offset
+ const yOffset = Math.floor((canvasSize - height) / 2);
+ ctx.drawImage(img, 0, yOffset, width, height);
+
+ // draw reflected image padding
+ this.drawVerticalReflection(ctx, canvas, yOffset);
+ }
+ return canvas;
+ };
+
+ /**
+ * Converts a url to base64 (tainted canvas workaround)
+ */
+ static urlToBase64 = async (imageUrl: string): Promise<string | undefined> => {
+ try {
+ const res = await fetch(imageUrl);
+ const blob = await res.blob();
+
+ return new Promise<string>((resolve, reject) => {
+ const reader = new FileReader();
+ reader.onload = () => {
+ const base64Data = reader.result?.toString().split(',')[1];
+ if (base64Data) {
+ resolve(base64Data);
+ } else {
+ reject(new Error('Failed to convert.'));
+ }
+ };
+ reader.onerror = () => {
+ reject(new Error('Error reading image data'));
+ };
+ reader.readAsDataURL(blob);
+ });
+ } catch (err) {
+ console.error(err);
+ }
+ };
+}
diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/PointerHandler.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/PointerHandler.ts
new file mode 100644
index 000000000..9e620ad11
--- /dev/null
+++ b/src/client/views/nodes/generativeFill/generativeFillUtils/PointerHandler.ts
@@ -0,0 +1,15 @@
+import { Point } from "./generativeFillInterfaces";
+
+export class PointerHandler {
+ static getPointRelativeToElement = (
+ element: HTMLElement,
+ e: React.PointerEvent | PointerEvent,
+ scale: number
+ ): Point => {
+ const boundingBox = element.getBoundingClientRect();
+ return {
+ x: (e.clientX - boundingBox.x) / scale,
+ y: (e.clientY - boundingBox.y) / scale,
+ };
+ };
+}
diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillConstants.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillConstants.ts
new file mode 100644
index 000000000..4772304bc
--- /dev/null
+++ b/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillConstants.ts
@@ -0,0 +1,9 @@
+export const canvasSize = 1024;
+export const freeformRenderSize = 300;
+export const offsetDistanceY = freeformRenderSize + 400;
+export const offsetX = 200;
+export const newCollectionSize = 500;
+
+export const activeColor = '#1976d2';
+export const eraserColor = '#e1e9ec';
+export const bgColor = '#f0f4f6';
diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillInterfaces.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillInterfaces.ts
new file mode 100644
index 000000000..1e7801056
--- /dev/null
+++ b/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillInterfaces.ts
@@ -0,0 +1,20 @@
+export interface CursorData {
+ x: number;
+ y: number;
+ width: number;
+}
+
+export interface Point {
+ x: number;
+ y: number;
+}
+
+export enum BrushMode {
+ ADD,
+ SUBTRACT,
+}
+
+export interface ImageDimensions {
+ width: number;
+ height: number;
+}
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index 8e53a87f6..b0924888a 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -49,9 +49,6 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
@observable public Status: 'marquee' | 'annotation' | '' = '';
// GPT additions
- @observable private GPTpopupText: string = '';
- @observable private loadingGPT: boolean = false;
- @observable private showGPTPopup: boolean = false;
@observable private GPTMode: GPTPopupMode = GPTPopupMode.SUMMARY;
@observable private selectedText: string = '';
@observable private editorView?: EditorView;
@@ -60,25 +57,11 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
private selectionRange: number[] | undefined;
@action
- setGPTPopupVis = (vis: boolean) => {
- this.showGPTPopup = vis;
- };
- @action
setGPTMode = (mode: GPTPopupMode) => {
this.GPTMode = mode;
};
@action
- setGPTPopupText = (txt: string) => {
- this.GPTpopupText = txt;
- };
-
- @action
- setLoading = (loading: boolean) => {
- this.loadingGPT = loading;
- };
-
- @action
setHighlightRange(r: number[] | undefined) {
this.highlightRange = r;
}
@@ -131,19 +114,12 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
componentDidMount() {
this._disposer2 = reaction(
() => this._opacity,
- opacity => {
- if (!opacity) {
- this.setGPTPopupVis(false);
- this.setGPTPopupText('');
- }
- },
+ opacity => {},
{ fireImmediately: true }
);
this._disposer = reaction(
() => SelectionManager.Views().slice(),
selected => {
- this.setGPTPopupVis(false);
- this.setGPTPopupText('');
AnchorMenu.Instance.fadeOut(true);
}
);
@@ -154,67 +130,23 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
* @param e pointer down event
*/
gptSummarize = async (e: React.PointerEvent) => {
+ // move this logic to gptpopup, need to implement generate again
+ GPTPopup.Instance.setVisible(true);
this.setHighlightRange(undefined);
- this.setGPTPopupVis(true);
- this.setGPTMode(GPTPopupMode.SUMMARY);
- this.setLoading(true);
+ GPTPopup.Instance.setMode(GPTPopupMode.SUMMARY);
+ GPTPopup.Instance.setLoading(true);
try {
const res = await gptAPICall(this.selectedText, GPTCallType.SUMMARY);
if (res) {
- this.setGPTPopupText(res);
+ GPTPopup.Instance.setText(res);
} else {
- this.setGPTPopupText('Something went wrong.');
+ GPTPopup.Instance.setText('Something went wrong.');
}
} catch (err) {
console.error(err);
}
-
- this.setLoading(false);
- };
-
- /**
- * Makes a GPT call to edit selected text.
- * @returns nothing
- */
- gptEdit = async () => {
- if (!this.editorView) return;
- this.setHighlightRange(undefined);
- const state = this.editorView.state;
- const sel = state.selection;
- const fullText = state.doc.textBetween(0, this.editorView.state.doc.content.size, ' \n');
- const selectedText = state.doc.textBetween(sel.from, sel.to);
-
- this.setGPTPopupVis(true);
- this.setGPTMode(GPTPopupMode.EDIT);
- this.setLoading(true);
-
- try {
- let res = await gptAPICall(selectedText, GPTCallType.EDIT);
- // let res = await this.mockGPTCall();
- if (!res) return;
- res = res.trim();
- const resultText = fullText.slice(0, sel.from - 1) + res + fullText.slice(sel.to - 1);
-
- if (res) {
- this.setGPTPopupText(resultText);
- this.setHighlightRange([sel.from - 1, sel.from - 1 + res.length]);
- } else {
- this.setGPTPopupText('Something went wrong.');
- }
- } catch (err) {
- console.error(err);
- }
-
- this.setLoading(false);
- };
-
- /**
- * Replaces text suggestions from GPT.
- */
- replaceText = (replacement: string) => {
- if (!this.editorView || !this.textDoc) return;
- this.textDoc.text = replacement;
+ GPTPopup.Instance.setLoading(false);
};
pointerDown = (e: React.PointerEvent) => {
@@ -325,17 +257,6 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
color={StrCast(Doc.UserDoc().userColor)}
/>
)}
- <GPTPopup
- key="gptpopup"
- visible={this.showGPTPopup}
- text={this.GPTpopupText}
- highlightRange={this.highlightRange}
- loading={this.loadingGPT}
- callSummaryApi={this.gptSummarize}
- callEditApi={this.gptEdit}
- replaceText={this.replaceText}
- mode={this.GPTMode}
- />
{AnchorMenu.Instance.OnAudio === unimplementedFunction ? null : (
<IconButton
tooltip="Click to Record Annotation" //
@@ -344,14 +265,6 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
color={StrCast(Doc.UserDoc().userColor)}
/>
)}
- {this.canEdit() && (
- <IconButton
- tooltip="AI edit suggestions" //
- onPointerDown={this.gptEdit}
- icon={<FontAwesomeIcon icon="pencil-alt" />}
- color={StrCast(Doc.UserDoc().userColor)}
- />
- )}
<Popup
tooltip="Find document to link to selected text" //
type={Type.PRIM}
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.scss b/src/client/views/pdf/GPTPopup/GPTPopup.scss
index 44413ede7..5d966395c 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.scss
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.scss
@@ -6,12 +6,6 @@ $button: #5b97ff;
$highlightedText: #82e0ff;
.summary-box {
- display: flex;
- flex-direction: column;
- justify-content: space-between;
- background-color: #ffffff;
- box-shadow: 0 2px 5px #7474748d;
- color: $textgrey;
position: fixed;
bottom: 10px;
right: 10px;
@@ -21,9 +15,16 @@ $highlightedText: #82e0ff;
padding: 15px;
padding-bottom: 0;
z-index: 999;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ background-color: #ffffff;
+ box-shadow: 0 2px 5px #7474748d;
+ color: $textgrey;
.summary-heading {
display: flex;
+ justify-content: space-between;
align-items: center;
border-bottom: 1px solid $greyborder;
padding-bottom: 5px;
@@ -110,6 +111,59 @@ $highlightedText: #82e0ff;
}
}
+.image-content-wrapper {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 8px;
+ padding-bottom: 16px;
+
+ .img-wrapper {
+ position: relative;
+ cursor: pointer;
+
+ .img-container {
+ pointer-events: none;
+ position: relative;
+
+ img {
+ pointer-events: all;
+ position: relative;
+ }
+ }
+
+ .img-container::after {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: rgba(0, 0, 0, 0.5);
+ opacity: 0;
+ transition: opacity 0.3s ease;
+ }
+
+ .btn-container {
+ position: absolute;
+ right: 8px;
+ bottom: 8px;
+ opacity: 0;
+ transition: opacity 0.3s ease;
+ }
+
+ &:hover {
+ .img-container::after {
+ opacity: 1;
+ }
+
+ .btn-container {
+ opacity: 1;
+ }
+ }
+ }
+}
+
// Typist CSS
.Typist .Cursor {
display: inline-block;
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index 8bd060d4f..8bf626d73 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -1,82 +1,226 @@
import React = require('react');
+import './GPTPopup.scss';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, observable } from 'mobx';
import { observer } from 'mobx-react';
import ReactLoading from 'react-loading';
import Typist from 'react-typist';
import { Doc } from '../../../../fields/Doc';
-import { Docs } from '../../../documents/Documents';
-import './GPTPopup.scss';
+import { DocUtils, Docs } from '../../../documents/Documents';
+import { Button, IconButton, Type } from 'browndash-components';
+import { NumCast, StrCast } from '../../../../fields/Types';
+import { CgClose } from 'react-icons/cg';
+import { AnchorMenu } from '../AnchorMenu';
+import { gptImageCall } from '../../../apis/gpt/GPT';
+import { Networking } from '../../../Network';
+import { Utils } from '../../../../Utils';
export enum GPTPopupMode {
SUMMARY,
EDIT,
+ IMAGE,
}
-interface GPTPopupProps {
- visible: boolean;
- text: string;
- loading: boolean;
- mode: GPTPopupMode;
- callSummaryApi: (e: React.PointerEvent) => Promise<void>;
- callEditApi: (e: React.PointerEvent) => Promise<void>;
- replaceText: (replacement: string) => void;
- highlightRange?: number[];
-}
+interface GPTPopupProps {}
@observer
export class GPTPopup extends React.Component<GPTPopupProps> {
static Instance: GPTPopup;
@observable
- private done: boolean = false;
+ public visible: boolean = false;
+ @action
+ public setVisible = (vis: boolean) => {
+ this.visible = vis;
+ };
@observable
- private sidebarId: string = '';
+ public loading: boolean = false;
+ @action
+ public setLoading = (loading: boolean) => {
+ this.loading = loading;
+ };
+ @observable
+ public text: string = '';
+ @action
+ public setText = (text: string) => {
+ this.text = text;
+ };
+
+ @observable
+ public imgDesc: string = '';
+ @action
+ public setImgDesc = (text: string) => {
+ this.imgDesc = text;
+ };
+
+ @observable
+ public imgUrls: string[][] = [];
+ @action
+ public setImgUrls = (imgs: string[][]) => {
+ this.imgUrls = imgs;
+ };
+ @observable
+ public mode: GPTPopupMode = GPTPopupMode.SUMMARY;
+ @action
+ public setMode = (mode: GPTPopupMode) => {
+ this.mode = mode;
+ };
+
+ @observable
+ public highlightRange: number[] = [];
+ @action callSummaryApi = () => {};
+ @action callEditApi = () => {};
+ @action replaceText = (replacement: string) => {};
+
+ @observable
+ private done: boolean = false;
@action
public setDone = (done: boolean) => {
this.done = done;
};
+
+ // change what can be a ref into a ref
+ @observable
+ private sidebarId: string = '';
@action
public setSidebarId = (id: string) => {
this.sidebarId = id;
};
+
+ @observable
+ private imgTargetDoc: Doc | undefined;
+ @action
+ public setImgTargetDoc = (anchor: Doc) => {
+ this.imgTargetDoc = anchor;
+ };
+
+ @observable
+ private textAnchor: Doc | undefined;
+ @action
+ public setTextAnchor = (anchor: Doc) => {
+ this.textAnchor = anchor;
+ };
+
public addDoc: (doc: Doc | Doc[], sidebarKey?: string | undefined) => boolean = () => false;
+ public addToCollection: ((doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) | undefined;
+
+ /**
+ * Generates a Dalle image and uploads it to the server.
+ */
+ generateImage = async () => {
+ if (this.imgDesc === '') return;
+ this.setImgUrls([]);
+ this.setMode(GPTPopupMode.IMAGE);
+ this.setVisible(true);
+ this.setLoading(true);
+
+ try {
+ let image_urls = await gptImageCall(this.imgDesc);
+ if (image_urls && image_urls[0]) {
+ const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [image_urls[0]] });
+ const source = Utils.prepend(result.accessPaths.agnostic.client);
+ this.setImgUrls([[image_urls[0], source]]);
+ }
+ } catch (err) {
+ console.log(err);
+ return '';
+ }
+ GPTPopup.Instance.setLoading(false);
+ };
/**
* Transfers the summarization text to a sidebar annotation text document.
*/
private transferToText = () => {
- const newDoc = Docs.Create.TextDocument(this.props.text.trim(), {
+ const newDoc = Docs.Create.TextDocument(this.text.trim(), {
_width: 200,
_height: 50,
_layout_fitWidth: true,
_layout_autoHeight: true,
});
this.addDoc(newDoc, this.sidebarId);
+ const anchor = AnchorMenu.Instance?.GetAnchor(undefined, false);
+ if (anchor) {
+ DocUtils.MakeLink(newDoc, anchor, {
+ link_relationship: 'GPT Summary',
+ });
+ }
+ };
+
+ /**
+ * Transfers the image urls to actual image docs
+ */
+ private transferToImage = (source: string) => {
+ const textAnchor = this.imgTargetDoc;
+ if (!textAnchor) return;
+ const newDoc = Docs.Create.ImageDocument(source, {
+ x: NumCast(textAnchor.x) + NumCast(textAnchor._width) + 10,
+ y: NumCast(textAnchor.y),
+ _height: 200,
+ _width: 200,
+ data_nativeWidth: 1024,
+ data_nativeHeight: 1024,
+ });
+ if (Doc.IsInMyOverlay(textAnchor)) {
+ newDoc.overlayX = textAnchor.x;
+ newDoc.overlayY = NumCast(textAnchor.y) + NumCast(textAnchor._height);
+ Doc.AddToMyOverlay(newDoc);
+ } else {
+ this.addToCollection?.(newDoc);
+ }
+ // Create link between prompt and image
+ DocUtils.MakeLink(textAnchor, newDoc, { link_relationship: 'Image Prompt' });
};
+ private getPreviewUrl = (source: string) => source.split('.').join('_m.');
+
constructor(props: GPTPopupProps) {
super(props);
GPTPopup.Instance = this;
}
componentDidUpdate = () => {
- if (this.props.loading) {
+ if (this.loading) {
this.setDone(false);
}
};
+ imageBox = () => {
+ return (
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
+ {this.heading('GENERATED IMAGE')}
+ <div className="image-content-wrapper">
+ {this.imgUrls.map(rawSrc => (
+ <div className="img-wrapper">
+ <div className="img-container">
+ <img key={rawSrc[0]} src={rawSrc[0]} width={150} height={150} alt="dalle generation" />
+ </div>
+ <div className="btn-container">
+ <Button text="Save Image" onClick={() => this.transferToImage(rawSrc[1])} color={StrCast(Doc.UserDoc().userColor)} type={Type.TERT} />
+ </div>
+ </div>
+ ))}
+ </div>
+ {!this.loading && (
+ <>
+ <IconButton tooltip="Generate Again" onClick={this.generateImage} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(Doc.UserDoc().userVariantColor)} />
+ </>
+ )}
+ </div>
+ );
+ };
+
summaryBox = () => (
<>
<div>
{this.heading('SUMMARY')}
<div className="content-wrapper">
- {!this.props.loading &&
+ {!this.loading &&
(!this.done ? (
<Typist
- key={this.props.text}
+ key={this.text}
avgTypingDelay={15}
cursor={{ hideWhenDone: true }}
onTypingDone={() => {
@@ -84,39 +228,32 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
this.setDone(true);
}, 500);
}}>
- {this.props.text}
+ {this.text}
</Typist>
) : (
- this.props.text
+ this.text
))}
</div>
</div>
- {!this.props.loading && (
+ {!this.loading && (
<div className="btns-wrapper">
{this.done ? (
<>
- <button className="icon-btn" onPointerDown={e => this.props.callSummaryApi(e)}>
- <FontAwesomeIcon icon="redo-alt" size="lg" />
- </button>
- <button
- className="text-btn"
- onClick={e => {
- this.transferToText();
- }}>
- Transfer to Text
- </button>
+ <IconButton tooltip="Generate Again" onClick={this.callSummaryApi} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(Doc.UserDoc().userVariantColor)} />
+ <Button tooltip="Transfer to text" text="Transfer To Text" onClick={this.transferToText} color={StrCast(Doc.UserDoc().userVariantColor)} type={Type.TERT} />
</>
) : (
<div className="summarizing">
<span>Summarizing</span>
<ReactLoading type="bubbles" color="#bcbcbc" width={20} height={20} />
- <button
- className="btn-secondary"
- onClick={e => {
+ <Button
+ text="Stop Animation"
+ onClick={() => {
this.setDone(true);
- }}>
- Stop Animation
- </button>
+ }}
+ color={StrCast(Doc.UserDoc().userVariantColor)}
+ type={Type.TERT}
+ />
</div>
)}
</div>
@@ -124,43 +261,6 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
</>
);
- editBox = () => {
- const hr = this.props.highlightRange;
- return (
- <>
- <div>
- {this.heading('TEXT EDIT SUGGESTIONS')}
- <div className="content-wrapper">
- {hr && (
- <div>
- {this.props.text.slice(0, hr[0])} <span className="highlighted-text">{this.props.text.slice(hr[0], hr[1])}</span> {this.props.text.slice(hr[1])}
- </div>
- )}
- </div>
- </div>
- {hr && !this.props.loading && (
- <>
- <div className="btns-wrapper">
- <>
- <button className="icon-btn" onPointerDown={e => this.props.callEditApi(e)}>
- <FontAwesomeIcon icon="redo-alt" size="lg" />
- </button>
- <button
- className="text-btn"
- onClick={e => {
- this.props.replaceText(this.props.text);
- }}>
- Replace Text
- </button>
- </>
- </div>
- {this.aiWarning()}
- </>
- )}
- </>
- );
- };
-
aiWarning = () =>
this.done ? (
<div className="ai-warning">
@@ -174,14 +274,14 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
heading = (headingText: string) => (
<div className="summary-heading">
<label className="summary-text">{headingText}</label>
- {this.props.loading && <ReactLoading type="spin" color="#bcbcbc" width={14} height={14} />}
+ {this.loading ? <ReactLoading type="spin" color="#bcbcbc" width={14} height={14} /> : <IconButton color={StrCast(Doc.UserDoc().userVariantColor)} tooltip="close" icon={<CgClose size="16px" />} onClick={() => this.setVisible(false)} />}
</div>
);
render() {
return (
- <div className="summary-box" style={{ display: this.props.visible ? 'flex' : 'none' }}>
- {this.props.mode === GPTPopupMode.SUMMARY ? this.summaryBox() : this.editBox()}
+ <div className="summary-box" style={{ display: this.visible ? 'flex' : 'none' }}>
+ {this.mode === GPTPopupMode.SUMMARY ? this.summaryBox() : this.mode === GPTPopupMode.IMAGE ? this.imageBox() : <></>}
</div>
);
}
diff --git a/src/fields/DocSymbols.ts b/src/fields/DocSymbols.ts
index 088903082..856c377fa 100644
--- a/src/fields/DocSymbols.ts
+++ b/src/fields/DocSymbols.ts
@@ -24,4 +24,4 @@ export const Initializing = Symbol('DocInitializing');
export const ForceServerWrite = Symbol('DocForceServerWrite');
export const CachedUpdates = Symbol('DocCachedUpdates');
-export const DashVersion = 'v0.5.6';
+export const DashVersion = 'v0.5.7';