aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoreleanor-park <eleanor_park@brown.edu>2024-08-27 16:22:33 -0400
committereleanor-park <eleanor_park@brown.edu>2024-08-27 16:22:33 -0400
commit6f73686ec4dc3e01ae3eacc0150aa59eafea0325 (patch)
tree392d6ebcb6122326441afbb7dfe69ff4cb583c1d
parentfd5278045e8c2e280d81cb965c0b2cc5afb59be8 (diff)
pulling from master
-rw-r--r--eslint.config.mjs17
-rw-r--r--package-lock.json195
-rw-r--r--package.json8
-rw-r--r--src/client/util/DropConverter.ts4
-rw-r--r--src/client/views/InkStrokeProperties.ts7
-rw-r--r--src/client/views/MarqueeAnnotator.tsx145
-rw-r--r--src/client/views/PropertiesView.scss2
-rw-r--r--src/client/views/PropertiesView.tsx52
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx64
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx65
-rw-r--r--src/client/views/pdf/AnchorMenu.tsx20
-rw-r--r--src/client/views/smartdraw/AnnotationPalette.tsx13
-rw-r--r--src/client/views/smartdraw/SmartDrawHandler.tsx22
14 files changed, 213 insertions, 403 deletions
diff --git a/eslint.config.mjs b/eslint.config.mjs
index b35601abd..c69209327 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -1,6 +1,13 @@
-import pluginJs from '@eslint/js';
-import pluginReactConfig from 'eslint-plugin-react/configs/recommended.js';
-import globals from 'globals';
-import tseslint from 'typescript-eslint';
+import globals from "globals";
+import pluginJs from "@eslint/js";
+import tseslint from "typescript-eslint";
+import pluginReact from "eslint-plugin-react";
-export default [{ languageOptions: { globals: { ...globals.browser, ...globals.node } } }, pluginJs.configs.recommended, ...tseslint.configs.recommended, pluginReactConfig];
+
+export default [
+ {files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"]},
+ {languageOptions: { globals: globals.browser }},
+ pluginJs.configs.recommended,
+ ...tseslint.configs.recommended,
+ pluginReact.configs.flat.recommended,
+]; \ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 7f6237ef5..95fb8ce11 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -246,7 +246,7 @@
"xregexp": "^5.1.1"
},
"devDependencies": {
- "@eslint/js": "^9.1.1",
+ "@eslint/js": "^9.9.1",
"@types/adm-zip": "^0.5.5",
"@types/animejs": "^3.1.12",
"@types/archiver": "^6.0.2",
@@ -301,9 +301,9 @@
"eslint-plugin-jsx-a11y": "^6.8.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^5.0.1",
- "eslint-plugin-react": "^7.34.1",
+ "eslint-plugin-react": "^7.35.0",
"eslint-plugin-react-hooks": "^4.6.0",
- "globals": "^15.1.0",
+ "globals": "^15.9.0",
"jsdom": "^24.0.0",
"mocha": "^10.2.0",
"prettier": "^3.1.0",
@@ -312,7 +312,7 @@
"ts-loader": "^9.5.1",
"ts-node": "^10.9.1",
"ts-node-dev": "^2.0.0",
- "typescript-eslint": "^7.8.0",
+ "typescript-eslint": "^7.18.0",
"webpack-dev-server": "^5.0.4"
},
"engines": {
@@ -2637,10 +2637,11 @@
}
},
"node_modules/@eslint/js": {
- "version": "9.6.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.6.0.tgz",
- "integrity": "sha512-D9B0/3vNg44ZeWbYMpBoXqNP4j6eQD5vNwIlGAuFRRzK/WtT/jvDQW3Bi9kkf3PMDMlM7Yi+73VLUsn5bJcl8A==",
+ "version": "9.9.1",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.1.tgz",
+ "integrity": "sha512-xIDQRsfg5hNBqHz04H1R3scSVwmI+KUbqjsQKHKQ1DAUSaUjYPReZZmS/5PNiKu1fUvzDd6H7DEDKACSEhu+TQ==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
@@ -9946,16 +9947,17 @@
"dev": true
},
"node_modules/@typescript-eslint/eslint-plugin": {
- "version": "7.16.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.16.0.tgz",
- "integrity": "sha512-py1miT6iQpJcs1BiJjm54AMzeuMPBSPuKPlnT8HlfudbcS5rYeX5jajpLf3mrdRh9dA/Ec2FVUY0ifeVNDIhZw==",
+ "version": "7.18.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz",
+ "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
- "@typescript-eslint/scope-manager": "7.16.0",
- "@typescript-eslint/type-utils": "7.16.0",
- "@typescript-eslint/utils": "7.16.0",
- "@typescript-eslint/visitor-keys": "7.16.0",
+ "@typescript-eslint/scope-manager": "7.18.0",
+ "@typescript-eslint/type-utils": "7.18.0",
+ "@typescript-eslint/utils": "7.18.0",
+ "@typescript-eslint/visitor-keys": "7.18.0",
"graphemer": "^1.4.0",
"ignore": "^5.3.1",
"natural-compare": "^1.4.0",
@@ -9979,14 +9981,15 @@
}
},
"node_modules/@typescript-eslint/parser": {
- "version": "7.16.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.16.0.tgz",
- "integrity": "sha512-ar9E+k7CU8rWi2e5ErzQiC93KKEFAXA2Kky0scAlPcxYblLt8+XZuHUZwlyfXILyQa95P6lQg+eZgh/dDs3+Vw==",
- "dependencies": {
- "@typescript-eslint/scope-manager": "7.16.0",
- "@typescript-eslint/types": "7.16.0",
- "@typescript-eslint/typescript-estree": "7.16.0",
- "@typescript-eslint/visitor-keys": "7.16.0",
+ "version": "7.18.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz",
+ "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "7.18.0",
+ "@typescript-eslint/types": "7.18.0",
+ "@typescript-eslint/typescript-estree": "7.18.0",
+ "@typescript-eslint/visitor-keys": "7.18.0",
"debug": "^4.3.4"
},
"engines": {
@@ -10006,12 +10009,13 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
- "version": "7.16.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.16.0.tgz",
- "integrity": "sha512-8gVv3kW6n01Q6TrI1cmTZ9YMFi3ucDT7i7aI5lEikk2ebk1AEjrwX8MDTdaX5D7fPXMBLvnsaa0IFTAu+jcfOw==",
+ "version": "7.18.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz",
+ "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==",
+ "license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "7.16.0",
- "@typescript-eslint/visitor-keys": "7.16.0"
+ "@typescript-eslint/types": "7.18.0",
+ "@typescript-eslint/visitor-keys": "7.18.0"
},
"engines": {
"node": "^18.18.0 || >=20.0.0"
@@ -10022,13 +10026,14 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
- "version": "7.16.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.16.0.tgz",
- "integrity": "sha512-j0fuUswUjDHfqV/UdW6mLtOQQseORqfdmoBNDFOqs9rvNVR2e+cmu6zJu/Ku4SDuqiJko6YnhwcL8x45r8Oqxg==",
+ "version": "7.18.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz",
+ "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "@typescript-eslint/typescript-estree": "7.16.0",
- "@typescript-eslint/utils": "7.16.0",
+ "@typescript-eslint/typescript-estree": "7.18.0",
+ "@typescript-eslint/utils": "7.18.0",
"debug": "^4.3.4",
"ts-api-utils": "^1.3.0"
},
@@ -10049,9 +10054,10 @@
}
},
"node_modules/@typescript-eslint/types": {
- "version": "7.16.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.16.0.tgz",
- "integrity": "sha512-fecuH15Y+TzlUutvUl9Cc2XJxqdLr7+93SQIbcZfd4XRGGKoxyljK27b+kxKamjRkU7FYC6RrbSCg0ALcZn/xw==",
+ "version": "7.18.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz",
+ "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==",
+ "license": "MIT",
"engines": {
"node": "^18.18.0 || >=20.0.0"
},
@@ -10061,12 +10067,13 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
- "version": "7.16.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.16.0.tgz",
- "integrity": "sha512-a5NTvk51ZndFuOLCh5OaJBELYc2O3Zqxfl3Js78VFE1zE46J2AaVuW+rEbVkQznjkmlzWsUI15BG5tQMixzZLw==",
+ "version": "7.18.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz",
+ "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==",
+ "license": "BSD-2-Clause",
"dependencies": {
- "@typescript-eslint/types": "7.16.0",
- "@typescript-eslint/visitor-keys": "7.16.0",
+ "@typescript-eslint/types": "7.18.0",
+ "@typescript-eslint/visitor-keys": "7.18.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
@@ -10088,9 +10095,10 @@
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
- "version": "7.6.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
- "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
+ "version": "7.6.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+ "license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
@@ -10099,15 +10107,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
- "version": "7.16.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.16.0.tgz",
- "integrity": "sha512-PqP4kP3hb4r7Jav+NiRCntlVzhxBNWq6ZQ+zQwII1y/G/1gdIPeYDCKr2+dH6049yJQsWZiHU6RlwvIFBXXGNA==",
+ "version": "7.18.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz",
+ "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
- "@typescript-eslint/scope-manager": "7.16.0",
- "@typescript-eslint/types": "7.16.0",
- "@typescript-eslint/typescript-estree": "7.16.0"
+ "@typescript-eslint/scope-manager": "7.18.0",
+ "@typescript-eslint/types": "7.18.0",
+ "@typescript-eslint/typescript-estree": "7.18.0"
},
"engines": {
"node": "^18.18.0 || >=20.0.0"
@@ -10121,11 +10130,12 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
- "version": "7.16.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.16.0.tgz",
- "integrity": "sha512-rMo01uPy9C7XxG7AFsxa8zLnWXTF8N3PYclekWSrurvhwiw1eW88mrKiAYe6s53AUY57nTRz8dJsuuXdkAhzCg==",
+ "version": "7.18.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz",
+ "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==",
+ "license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "7.16.0",
+ "@typescript-eslint/types": "7.18.0",
"eslint-visitor-keys": "^3.4.3"
},
"engines": {
@@ -10891,18 +10901,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/array.prototype.toreversed": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz",
- "integrity": "sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==",
- "dev": true,
- "dependencies": {
- "call-bind": "^1.0.2",
- "define-properties": "^1.2.0",
- "es-abstract": "^1.22.1",
- "es-shim-unscopables": "^1.0.0"
- }
- },
"node_modules/array.prototype.tosorted": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz",
@@ -17072,6 +17070,7 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
"integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+ "license": "MIT",
"dependencies": {
"path-type": "^4.0.0"
},
@@ -17618,6 +17617,7 @@
"version": "8.57.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
"integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
+ "license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
@@ -18884,35 +18884,36 @@
}
},
"node_modules/eslint-plugin-react": {
- "version": "7.34.3",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.3.tgz",
- "integrity": "sha512-aoW4MV891jkUulwDApQbPYTVZmeuSyFrudpbTAQuj5Fv8VL+o6df2xIGpw8B0hPjAaih1/Fb0om9grCdyFYemA==",
+ "version": "7.35.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.35.0.tgz",
+ "integrity": "sha512-v501SSMOWv8gerHkk+IIQBkcGRGrO2nfybfj5pLxuJNFTPxxA3PSryhXTK+9pNbtkggheDdsC0E9Q8CuPk6JKA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"array-includes": "^3.1.8",
"array.prototype.findlast": "^1.2.5",
"array.prototype.flatmap": "^1.3.2",
- "array.prototype.toreversed": "^1.1.2",
"array.prototype.tosorted": "^1.1.4",
"doctrine": "^2.1.0",
"es-iterator-helpers": "^1.0.19",
"estraverse": "^5.3.0",
+ "hasown": "^2.0.2",
"jsx-ast-utils": "^2.4.1 || ^3.0.0",
"minimatch": "^3.1.2",
"object.entries": "^1.1.8",
"object.fromentries": "^2.0.8",
- "object.hasown": "^1.1.4",
"object.values": "^1.2.0",
"prop-types": "^15.8.1",
"resolve": "^2.0.0-next.5",
"semver": "^6.3.1",
- "string.prototype.matchall": "^4.0.11"
+ "string.prototype.matchall": "^4.0.11",
+ "string.prototype.repeat": "^1.0.0"
},
"engines": {
"node": ">=4"
},
"peerDependencies": {
- "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8"
+ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7"
}
},
"node_modules/eslint-plugin-react-hooks": {
@@ -20855,10 +20856,11 @@
}
},
"node_modules/globals": {
- "version": "15.8.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-15.8.0.tgz",
- "integrity": "sha512-VZAJ4cewHTExBWDHR6yptdIBlx9YSSZuwojj9Nt5mBRXQzrKakDsVKQ1J63sklLvzAJm0X5+RpO4i3Y2hcOnFw==",
+ "version": "15.9.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-15.9.0.tgz",
+ "integrity": "sha512-SmSKyLLKFbSr6rptvP8izbyxJL4ILwqO9Jg23UA0sDlGlu58V59D1//I3vlc0KJphVdUR7vMjHIplYnzBxorQA==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=18"
},
@@ -20886,6 +20888,7 @@
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
"integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+ "license": "MIT",
"dependencies": {
"array-union": "^2.1.0",
"dir-glob": "^3.0.1",
@@ -34178,23 +34181,6 @@
"node": ">= 0.4"
}
},
- "node_modules/object.hasown": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz",
- "integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==",
- "dev": true,
- "dependencies": {
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.2",
- "es-object-atoms": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/object.values": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz",
@@ -38422,6 +38408,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
@@ -38983,6 +38970,17 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/string.prototype.repeat": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz",
+ "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
+ }
+ },
"node_modules/string.prototype.trim": {
"version": "1.2.9",
"resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz",
@@ -40552,14 +40550,15 @@
"integrity": "sha512-7sI4e/bZijOzyURng88oOFZCISQPTHozfE2sUu5AviFYk5QV7fYGb6YiDl+vKjF/pICA354JImBImL9XJWUvdQ=="
},
"node_modules/typescript-eslint": {
- "version": "7.16.0",
- "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-7.16.0.tgz",
- "integrity": "sha512-kaVRivQjOzuoCXU6+hLnjo3/baxyzWVO5GrnExkFzETRYJKVHYkrJglOu2OCm8Hi9RPDWX1PTNNTpU5KRV0+RA==",
+ "version": "7.18.0",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-7.18.0.tgz",
+ "integrity": "sha512-PonBkP603E3tt05lDkbOMyaxJjvKqQrXsnow72sVeOFINDE/qNmnnd+f9b4N+U7W6MXnnYyrhtmF2t08QWwUbA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "@typescript-eslint/eslint-plugin": "7.16.0",
- "@typescript-eslint/parser": "7.16.0",
- "@typescript-eslint/utils": "7.16.0"
+ "@typescript-eslint/eslint-plugin": "7.18.0",
+ "@typescript-eslint/parser": "7.18.0",
+ "@typescript-eslint/utils": "7.18.0"
},
"engines": {
"node": "^18.18.0 || >=20.0.0"
diff --git a/package.json b/package.json
index 16b2841be..8753e995b 100644
--- a/package.json
+++ b/package.json
@@ -23,7 +23,7 @@
"tsc": "tsc -t es5"
},
"devDependencies": {
- "@eslint/js": "^9.1.1",
+ "@eslint/js": "^9.9.1",
"@types/adm-zip": "^0.5.5",
"@types/animejs": "^3.1.12",
"@types/archiver": "^6.0.2",
@@ -78,9 +78,9 @@
"eslint-plugin-jsx-a11y": "^6.8.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^5.0.1",
- "eslint-plugin-react": "^7.34.1",
+ "eslint-plugin-react": "^7.35.0",
"eslint-plugin-react-hooks": "^4.6.0",
- "globals": "^15.1.0",
+ "globals": "^15.9.0",
"jsdom": "^24.0.0",
"mocha": "^10.2.0",
"prettier": "^3.1.0",
@@ -89,7 +89,7 @@
"ts-loader": "^9.5.1",
"ts-node": "^10.9.1",
"ts-node-dev": "^2.0.0",
- "typescript-eslint": "^7.8.0",
+ "typescript-eslint": "^7.18.0",
"webpack-dev-server": "^5.0.4"
},
"dependencies": {
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index 3e26fefad..aa7268b61 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -87,6 +87,10 @@ export function makeUserTemplateButton(doc: Doc) {
return dbox;
}
+/**
+ * Similar to makeUserTemplateButton, but rather than creating a draggable button for the template, it takes in
+ * an ImageField that will display.
+ */
export function makeUserTemplateImage(doc: Doc, image: ImageField) {
const layoutDoc = doc; // doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc;
if (layoutDoc.type !== DocumentType.FONTICON) {
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts
index 35d628a4e..ffda126f4 100644
--- a/src/client/views/InkStrokeProperties.ts
+++ b/src/client/views/InkStrokeProperties.ts
@@ -491,6 +491,12 @@ export class InkStrokeProperties {
return inkCopy;
});
+ /**
+ * Function that "smooths" ink strokes by using the gesture recognizer to detect shapes and
+ * removing excess control points with the simplify-js package.
+ * @param inkDocs
+ * @param tolerance Determines how strong the smooth effect will be
+ */
@undoBatch
smoothInkStrokes = (inkDocs: Doc[], tolerance: number = 5) => {
inkDocs.forEach(inkDoc => {
@@ -500,7 +506,6 @@ export class InkStrokeProperties {
const result = inkData.length > 2 && GestureUtils.GestureRecognizer.Recognize([inkData]);
console.log(result);
- let polygonPoints: { X: number; Y: number }[] | undefined = undefined;
if (result && (result.Name === 'line' ? result.Score > 0.92 : result.Score > 0.85)) {
switch (result.Name) {
case Gestures.Line:
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index f06f3efe0..3d0216625 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -28,7 +28,6 @@ export interface MarqueeAnnotatorProps {
marqueeContainer: HTMLDivElement;
docView: () => DocumentView;
savedAnnotations: () => ObservableMap<number, HTMLDivElement[]>;
- savedTapes: () => ObservableMap<number, HTMLDivElement[]>;
selectionText: () => string;
annotationLayer: HTMLDivElement;
addDocument: (doc: Doc) => boolean;
@@ -75,7 +74,6 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
backgroundColor: color,
annotationOn: this.props.Document,
title: 'Annotation on ' + this.props.Document.title,
- a,
});
marqueeAnno.x = NumCast(doc.freeform_panX_min) + (parseInt(anno.style.left || '0') - containerOffset[0]) / scale;
marqueeAnno.y = NumCast(doc.freeform_panY_min) + (parseInt(anno.style.top || '0') - containerOffset[1]) / scale;
@@ -130,139 +128,6 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
return textRegionAnno;
};
- // @undoBatch
- // makeTapeDocument = (color: string, isLinkButton?: boolean, savedTapes?: ObservableMap<number, HTMLDivElement[]>): Opt<Doc> => {
- // const savedTapeMap = savedTapes?.values() && Array.from(savedTapes?.values()).length ? savedTapes : this.props.savedTapes();
- // if (savedTapeMap.size === 0) return undefined;
- // const tapes = Array.from(savedTapeMap.values())[0];
- // const doc = this.props.Document;
- // const scale = (this.props.annotationLayerScaling?.() || 1) * NumCast(doc._freeform_scale, 1);
- // if (tapes.length && (tapes[0] as any).marqueeing) {
- // const anno = tapes[0];
- // const containerOffset = this.props.containerOffset?.() || [0, 0];
- // const tape = Docs.Create.FreeformDocument([], {
- // onClick: isLinkButton ? FollowLinkScript() : undefined,
- // backgroundColor: color,
- // annotationOn: this.props.Document,
- // title: 'Tape on ' + this.props.Document.title,
- // });
- // tape.x = NumCast(doc.freeform_panX_min) + (parseInt(anno.style.left || '0') - containerOffset[0]) / scale;
- // tape.y = NumCast(doc.freeform_panY_min) + (parseInt(anno.style.top || '0') - containerOffset[1]) / scale;
- // tape._height = parseInt(anno.style.height || '0') / scale;
- // tape._width = parseInt(anno.style.width || '0') / scale;
- // anno.remove();
- // savedTapeMap.clear();
- // return tape;
- // }
-
- // const textRegionAnno = Docs.Create.ConfigDocument({
- // annotationOn: this.props.Document,
- // text: this.props.selectionText() as any, // text want an RTFfield, but strings are acceptable, too.
- // text_html: this.props.selectionText() as any,
- // backgroundColor: 'transparent',
- // presentation_duration: 2100,
- // presentation_transition: 500,
- // presentation_zoomText: true,
- // title: '>' + this.props.Document.title,
- // });
- // const textRegionAnnoProto = textRegionAnno[DocData];
- // let minX = Number.MAX_VALUE;
- // let maxX = -Number.MAX_VALUE;
- // let minY = Number.MAX_VALUE;
- // let maxY = -Number.MIN_VALUE;
- // const annoRects: string[] = [];
- // savedAnnoMap.forEach((value: HTMLDivElement[]) =>
- // value.forEach(anno => {
- // const x = parseInt(anno.style.left ?? '0');
- // const y = parseInt(anno.style.top ?? '0');
- // const height = parseInt(anno.style.height ?? '0');
- // const width = parseInt(anno.style.width ?? '0');
- // annoRects.push(`${x}:${y}:${width}:${height}`);
- // anno.remove();
- // minY = Math.min(NumCast(y), minY);
- // minX = Math.min(NumCast(x), minX);
- // maxY = Math.max(NumCast(y) + NumCast(height), maxY);
- // maxX = Math.max(NumCast(x) + NumCast(width), maxX);
- // })
- // );
-
- // textRegionAnnoProto.y = Math.max(minY, 0);
- // textRegionAnnoProto.x = Math.max(minX, 0);
- // textRegionAnnoProto.height = Math.max(maxY, 0) - Math.max(minY, 0);
- // textRegionAnnoProto.width = Math.max(maxX, 0) - Math.max(minX, 0);
- // textRegionAnnoProto.backgroundColor = color;
- // // mainAnnoDocProto.text = this._selectionText;
- // textRegionAnnoProto.text_inlineAnnotations = new List<string>(annoRects);
- // textRegionAnnoProto.opacity = 0;
- // textRegionAnnoProto.layout_unrendered = true;
- // savedAnnoMap.clear();
- // return textRegionAnno;
- // };
-
- @undoBatch
- makeTapeDocument = (color: string, isLinkButton?: boolean, savedTapes?: ObservableMap<number, HTMLDivElement[]>): Opt<Doc> => {
- // const savedAnnoMap = savedTapes?.values() && Array.from(savedTapes?.values()).length ? savedTapes : this.props.savedTapes();
- // if (savedAnnoMap.size === 0) return undefined;
- // const savedAnnos = Array.from(savedAnnoMap.values())[0];
- const doc = this.props.Document;
- const scale = (this.props.annotationLayerScaling?.() || 1) * NumCast(doc._freeform_scale, 1);
- const marqueeAnno = Docs.Create.FreeformDocument([], {
- onClick: isLinkButton ? FollowLinkScript() : undefined,
- backgroundColor: color,
- annotationOn: this.props.Document,
- title: 'Annotation on ' + this.props.Document.title,
- });
- marqueeAnno.x = NumCast(doc.freeform_panX_min) / scale;
- marqueeAnno.y = NumCast(doc.freeform_panY_min) / scale;
- marqueeAnno._height = parseInt('100') / scale;
- marqueeAnno._width = parseInt('100') / scale;
- return marqueeAnno;
- // }
-
- // const textRegionAnno = Docs.Create.ConfigDocument({
- // annotationOn: this.props.Document,
- // text: this.props.selectionText() as any, // text want an RTFfield, but strings are acceptable, too.
- // text_html: this.props.selectionText() as any,
- // backgroundColor: 'transparent',
- // presentation_duration: 2100,
- // presentation_transition: 500,
- // presentation_zoomText: true,
- // title: '>' + this.props.Document.title,
- // });
- // const textRegionAnnoProto = textRegionAnno[DocData];
- // let minX = Number.MAX_VALUE;
- // let maxX = -Number.MAX_VALUE;
- // let minY = Number.MAX_VALUE;
- // let maxY = -Number.MIN_VALUE;
- // const annoRects: string[] = [];
- // savedAnnoMap.forEach((value: HTMLDivElement[]) =>
- // value.forEach(anno => {
- // const x = parseInt(anno.style.left ?? '0');
- // const y = parseInt(anno.style.top ?? '0');
- // const height = parseInt(anno.style.height ?? '0');
- // const width = parseInt(anno.style.width ?? '0');
- // annoRects.push(`${x}:${y}:${width}:${height}`);
- // anno.remove();
- // minY = Math.min(NumCast(y), minY);
- // minX = Math.min(NumCast(x), minX);
- // maxY = Math.max(NumCast(y) + NumCast(height), maxY);
- // maxX = Math.max(NumCast(x) + NumCast(width), maxX);
- // })
- // );
-
- // textRegionAnnoProto.y = Math.max(minY, 0);
- // textRegionAnnoProto.x = Math.max(minX, 0);
- // textRegionAnnoProto.height = Math.max(maxY, 0) - Math.max(minY, 0);
- // textRegionAnnoProto.width = Math.max(maxX, 0) - Math.max(minX, 0);
- // textRegionAnnoProto.backgroundColor = color;
- // // mainAnnoDocProto.text = this._selectionText;
- // textRegionAnnoProto.text_inlineAnnotations = new List<string>(annoRects);
- // textRegionAnnoProto.opacity = 0;
- // textRegionAnnoProto.layout_unrendered = true;
- // savedAnnoMap.clear();
- // return textRegionAnno;
- };
-
@action
highlight = (color: string, isLinkButton: boolean, savedAnnotations?: ObservableMap<number, HTMLDivElement[]>, addAsAnnotation?: boolean) => {
// creates annotation documents for current highlights
@@ -272,15 +137,6 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
return annotationDoc as Doc;
};
- @action
- tape = (color: string, isLinkButton: boolean, savedTapes?: ObservableMap<number, HTMLDivElement[]>, addAsAnnotation?: boolean) => {
- // creates annotation documents for current highlights
- const effectiveAcl = GetEffectiveAcl(this.props.Document[DocData]);
- const tape = [AclAugment, AclSelfEdit, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeTapeDocument(color, isLinkButton, savedTapes);
- addAsAnnotation && tape && this.props.addDocument(tape);
- return tape as Doc;
- };
-
public static previewNewAnnotation = action((savedAnnotations: ObservableMap<number, HTMLDivElement[]>, annotationLayer: HTMLDivElement, div: HTMLDivElement, page: number) => {
div.style.backgroundColor = '#ACCEF7';
div.style.opacity = '0.5';
@@ -327,7 +183,6 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
AnchorMenu.Instance.OnClick = undoable(() => this.props.anchorMenuClick?.()?.(this.highlight(this.props.highlightDragSrcColor ?? 'rgba(173, 216, 230, 0.75)', true, undefined, true)), 'make sidebar annotation');
AnchorMenu.Instance.OnAudio = unimplementedFunction;
AnchorMenu.Instance.Highlight = (color: string) => this.highlight(color, false, undefined, true);
- AnchorMenu.Instance.Tape = (color: string) => this.tape(color, false, undefined, true);
AnchorMenu.Instance.GetAnchor = (savedAnnotations?: ObservableMap<number, HTMLDivElement[]> /* , addAsAnnotation?: boolean */) => this.highlight('rgba(173, 216, 230, 0.75)', true, savedAnnotations, true);
AnchorMenu.Instance.onMakeAnchor = () => AnchorMenu.Instance.GetAnchor(undefined, true);
diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss
index aa825a6e9..a5e60b831 100644
--- a/src/client/views/PropertiesView.scss
+++ b/src/client/views/PropertiesView.scss
@@ -642,5 +642,5 @@
.smooth,
.color,
.smooth-slider {
- margin-top: 3px;
+ margin-top: 7px;
}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index ac2625f32..c7b0a2ba5 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -802,7 +802,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
};
getField(key: string) {
- return this.containsInkDoc ? Field.toString(this.inkDoc?.[DocData][key] as FieldType) : Field.toString(this.selectedDoc?.[DocData][key] as FieldType);
+ return Field.toString(this.selectedDoc?.[DocData][key] as FieldType);
}
@computed get shapeXps() { return NumCast(this.selectedDoc?.x); } // prettier-ignore
@@ -960,6 +960,22 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
const targetDoc = this.selectedLayoutDoc;
return (
<div>
+ {!targetDoc.layout_isSvg && (
+ <div className="color">
+ <Toggle
+ text={'Color with GPT'}
+ color={SettingsManager.userColor}
+ icon={<FontAwesomeIcon icon="fill-drip" />}
+ iconPlacement="left"
+ align="flex-start"
+ fillWidth
+ toggleType={ToggleType.BUTTON}
+ onClick={undoable(() => {
+ SmartDrawHandler.Instance.colorWithGPT(targetDoc);
+ }, 'smoothStrokes')}
+ />
+ </div>
+ )}
<div className="smooth">
<Toggle
text={'Smooth Ink Strokes'}
@@ -988,22 +1004,6 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
1
)}
</div>
- {!targetDoc.layout_isSvg && (
- <div className="color">
- <Toggle
- text={'Color with GPT'}
- color={SettingsManager.userColor}
- icon={<FontAwesomeIcon icon="fill-drip" />}
- iconPlacement="left"
- align="flex-start"
- fillWidth
- toggleType={ToggleType.BUTTON}
- onClick={undoable(() => {
- SmartDrawHandler.Instance.colorWithGPT(targetDoc);
- }, 'smoothStrokes')}
- />
- </div>
- )}
</div>
);
}
@@ -1026,9 +1026,15 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
}
@computed get markScal() { return Number(this.getField('stroke_markerScale') || '1'); } // prettier-ignore
set markScal(value) {
+ if (this.containsInkDoc) {
+ const childDocs = DocListCast(this.selectedDoc[DocData].data);
+ childDocs.forEach(doc => {
+ doc[DocData].stroke_markerScale = Number(value);
+ });
+ }
this.selectedDoc && (this.selectedDoc[DocData].stroke_markerScale = Number(value));
}
- @computed get smoothAmt() { return Number(this.getField('stroke_smoothAmount') || '1'); } // prettier-ignore
+ @computed get smoothAmt() { return Number(this.getField('stroke_smoothAmount') || '10'); } // prettier-ignore
set smoothAmt(value) {
this.selectedDoc && (this.selectedDoc[DocData].stroke_smoothAmount = Number(value));
}
@@ -1039,9 +1045,8 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
childDocs.forEach(doc => {
doc[DocData].stroke_startMarker = value;
});
- } else {
- this.selectedDoc && (this.selectedDoc[DocData].stroke_startMarker = value);
}
+ this.selectedDoc && (this.selectedDoc[DocData].stroke_startMarker = value);
}
@computed get markTail() { return this.getField('stroke_endMarker') || ''; } // prettier-ignore
set markTail(value) {
@@ -1050,9 +1055,8 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
childDocs.forEach(doc => {
doc[DocData].stroke_endMarker = value;
});
- } else {
- this.selectedDoc && (this.selectedDoc[DocData].stroke_endMarker = value);
}
+ this.selectedDoc && (this.selectedDoc[DocData].stroke_endMarker = value);
}
regInput = (key: string, value: any, setter: (val: string) => {}) => (
@@ -1304,6 +1308,10 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
);
}
+ /**
+ * Determines if a selected collection/group document contains any ink strokes to allow users to edit groups
+ * of ink strokes in the properties menu.
+ */
containsInk = (selectedDoc: Doc) => {
const childDocs: Doc[] = DocListCast(selectedDoc[DocData].data);
for (var i = 0; i < childDocs.length; i++) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 8575807b3..8dc5f03b0 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -599,6 +599,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
_eraserLock = 0;
_eraserPts: number[][] = []; // keep track of the last few eraserPts to make the eraser circle 'stretch'
+ /**
+ * Erases strokes by intersecting them with an invisible "eraser stroke".
+ * By default this iterates through all intersected ink strokes, determines which parts of a stroke need to be erased based on the type
+ * of eraser, draws back the ink segments to keep, and deletes the original stroke.
+ *
+ * Radius eraser: erases strokes by intersecting them with a circle of variable radius. Essentially creates an InkField for the
+ * eraser circle, then determines its intersections with other ink strokes. Each stroke's DocumentView and its
+ * intersection t-values are put into a map, which gets looped through to take out the erased parts.
+ */
erase = (e: PointerEvent, delta: number[]) => {
e.stopImmediatePropagation();
const currPoint = { X: e.clientX, Y: e.clientY };
@@ -646,11 +655,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return false;
};
- /**
- * Erases strokes by intersecting them with an invisible "eraser stroke".
- * By default this iterates through all intersected ink strokes, determines their segmentation, draws back the non-intersected segments,
- * and deletes the original stroke.
- */
@action
onEraserMove = (e: PointerEvent, down: number[], delta: number[]) => {
this.erase(e, delta);
@@ -665,42 +669,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.erase(e, [0, 0]);
};
- /**
- * Erases strokes by intersecting them with a circle of variable radius. Essentially creates an InkField for the
- * eraser circle, then determines its intersections with other ink strokes. Each stroke's DocumentView and its
- * intersection t-values are put into a map, which gets looped through to take out the erased parts.
- * @param e
- * @param down
- * @param delta
- * @returns
- */
- // @action
- // onRadiusEraserMove = (e: PointerEvent, down: number[], delta: number[]) => {
- // const currPoint = { X: e.clientX, Y: e.clientY };
- // this._eraserPts.push([currPoint.X, currPoint.Y]);
- // this._eraserPts = this._eraserPts.slice(Math.max(0, this._eraserPts.length - 5));
- // const strokeMap: Map<DocumentView, number[]> = this.getRadiusEraserIntersections({ X: currPoint.X - delta[0], Y: currPoint.Y - delta[1] }, currPoint);
-
- // strokeMap.forEach((intersects, stroke) => {
- // if (!this._deleteList.includes(stroke)) {
- // this._deleteList.push(stroke);
- // SetActiveInkWidth(StrCast(stroke.Document.stroke_width?.toString()) || '1');
- // SetActiveInkColor(StrCast(stroke.Document.color?.toString()) || 'black');
- // const segments = this.radiusErase(stroke, intersects.sort());
- // segments?.forEach(segment =>
- // this.forceStrokeGesture(
- // e,
- // Gestures.Stroke,
- // segment.reduce((data, curve) => [...data, ...curve.points.map(p => stroke.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })], [] as PointData[])
- // )
- // );
- // }
- // stroke.layoutDoc.opacity = 0;
- // stroke.layoutDoc.dontIntersect = true;
- // });
- // return false;
- // };
-
forceStrokeGesture = (e: PointerEvent, gesture: Gestures, points: InkData, text?: any) => {
this.onGesture(e, new GestureUtils.GestureEvent(gesture, points, InkField.getBounds(points), text));
};
@@ -1240,6 +1208,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return tVals;
};
+ /**
+ * Creates an ink document to add to the freeform canvas.
+ */
createInkDoc = (points: InkData, transformedBounds?: { x: number; y: number; width: number; height: number }) => {
const bounds = InkField.getBounds(points);
const B = transformedBounds || this.screenToFreeformContentsXf.transformBounds(bounds.left, bounds.top, bounds.width, bounds.height);
@@ -1273,6 +1244,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
_drawing: Doc[] = [];
_drawingContainer: Doc | undefined = undefined;
+ /**
+ * Function that creates a drawing--a group of ink strokes--to go with the smart draw function.
+ */
@undoBatch
createDrawingDoc = (strokeData: [InkData, string, string][], opts: DrawingOptions, gptRes: string) => {
this._drawing = [];
@@ -1304,6 +1278,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return collection;
};
+ /**
+ * Part of regenerating a drawing--deletes the old drawing.
+ */
removeDrawing = (doc?: Doc) => {
this._batch = UndoManager.StartBatch('regenerateDrawing');
if (doc) {
@@ -1317,6 +1294,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this._drawing = [];
};
+ /**
+ * Adds the created drawing to the freeform canvas and sets the metadata.
+ */
addDrawing = (doc: Doc, opts: DrawingOptions, gptRes: string) => {
const docData = doc[DocData];
docData.title = opts.text.match(/^(.*?)~~~.*$/)?.[1] || opts.text;
@@ -1926,8 +1906,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const locPt = this.ScreenToLocalBoxXf().transformPoint(e.clientX, e.clientY);
this._eraserX = locPt[0];
this._eraserY = locPt[1];
- // Doc.ActiveTool === InkTool.RadiusEraser ? this._childPointerEvents = 'none' : this._childPointerEvents = 'all'
- // super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY));
};
@action
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
index 76c37dff0..b3fdd9379 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
@@ -21,7 +21,6 @@ export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> {
public pinWithView: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
public classifyImages: (e: React.MouseEvent | undefined) => void = unimplementedFunction;
public groupImages: () => void = unimplementedFunction;
- public smoothStrokes: (docs?: Doc[]) => void = unimplementedFunction;
public isShown = () => this._opacity > 0;
constructor(props: any) {
super(props);
@@ -42,7 +41,6 @@ export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> {
<IconButton tooltip="Delete Documents" onPointerDown={this.delete} icon={<FontAwesomeIcon icon="trash-alt" />} color={this.userColor} />
<IconButton tooltip="Pin selected region" onPointerDown={this.pinWithView} icon={<FontAwesomeIcon icon="map-pin" />} color={this.userColor} />
<IconButton tooltip="Classify Images" onPointerDown={this.classifyImages} icon={<FontAwesomeIcon icon="object-group" />} color={this.userColor} />
- <IconButton tooltip="Smooth Strokes" onPointerDown={() => this.smoothStrokes} icon={<FontAwesomeIcon icon="object-group" />} color={this.userColor} />
</>
);
return this.getElement(buttons);
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 92c0da983..bc1dfed92 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -284,7 +284,6 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
MarqueeOptionsMenu.Instance.pinWithView = this.pinWithView;
MarqueeOptionsMenu.Instance.classifyImages = this.classifyImages;
MarqueeOptionsMenu.Instance.groupImages = this.groupImages;
- MarqueeOptionsMenu.Instance.smoothStrokes = this.smoothStrokes;
document.addEventListener('pointerdown', hideMarquee, true);
document.addEventListener('wheel', hideMarquee, true);
} else {
@@ -498,70 +497,6 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
});
@undoBatch
- smoothStrokes = action((docs?: Doc[]) => {
- docs && docs.length > 0 ? (this._selectedDocs = docs) : (this._selectedDocs = this.marqueeSelect(false, DocumentType.INK));
- if (this._selectedDocs.length == 0) return;
-
- this._selectedDocs.forEach(stroke => {
- const docView = DocumentView.getDocumentView(stroke);
- const inkStroke = docView?.ComponentView as InkingStroke;
- const { inkData } = inkStroke.inkScaledData();
-
- const result = inkData.length > 2 && GestureUtils.GestureRecognizer.Recognize([inkData]);
- console.log(result);
- let polygonPoints: { X: number; Y: number }[] | undefined = undefined;
- if (result && (result.Name === 'line' ? result.Score > 0.9 : result.Score > 0.8)) {
- switch (result.Name) {
- case Gestures.Line:
- case Gestures.Triangle:
- case Gestures.Rectangle:
- case Gestures.Circle:
- GestureOverlay.makeBezierPolygon(inkData, result.Name, true);
- break;
- default:
- }
- } else {
- const distances: number[] = [];
- for (var i = 0; i < inkData.length - 3; i += 4) {
- distances.push(Math.sqrt((inkData[i].X - inkData[i + 3].X) ** 2 + (inkData[i].Y - inkData[i + 3].Y) ** 2));
- }
- const avgDist = (NumCast(stroke.width) + NumCast(stroke.height)) / 2;
- // const avgDist = distances.reduce((a, b) => a + b) / distances.length;
- if (Math.sqrt((inkData.lastElement().X - inkData[0].X) ** 2 + (inkData.lastElement().Y - inkData[0].Y) ** 2) < avgDist) {
- inkData.pop();
- inkData.push({ X: inkData[0].X, Y: inkData[0].Y });
- }
- // const editedPoints: InkData = [];
- // const toDelete: number[] = [];
-
- // distances.forEach((dist, i) => {
- // if (dist < avgDist / 3) {
- // toDelete.unshift(i * 4);
- // }
- // });
- // toDelete.forEach(pt => {
- // InkStrokeProperties.Instance._currentPoint = pt;
- // docView && InkStrokeProperties.Instance.deletePoints(docView, false);
- // });
-
- // for (var i = 0; i < distances.length; i++) {
- // if (distances[i] > avgDist / 3) {
- // editedPoints.push(...inkData.slice(i * 4, i * 4 + 4));
- // } else {
- // if (i !== distances.length) {
- // editedPoints.push(...inkData.slice(i * 4, i * 4 + 2));
- // editedPoints.push(...inkData.slice(i * 4 + 6, i * 4 + 8));
- // i++;
- // }
- // }
- // }
- // inkData.length = 0;
- // inkData.push(...editedPoints);
- }
- });
- });
-
- @undoBatch
syntaxHighlight = action((e: KeyboardEvent | React.PointerEvent | undefined) => {
const selected = this.marqueeSelect(false);
if (e instanceof KeyboardEvent ? e.key === 'i' : true) {
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index ea574493a..7719f2f7c 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -57,7 +57,6 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
public StartCropDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
public Highlight: (color: string) => Opt<Doc> = (/* color: string */) => undefined;
- public Tape: (color: string) => Opt<Doc> = (/* color: string */) => undefined;
public GetAnchor: (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => Opt<Doc> = emptyFunction;
public Delete: () => void = unimplementedFunction;
public PinToPres: () => void = unimplementedFunction;
@@ -144,6 +143,9 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
this.addToCollection?.(newCol);
};
+ /**
+ * Creates a GPT drawing based on selected text.
+ */
gptDraw = async (e: React.PointerEvent) => {
try {
SmartDrawHandler.Instance.AddDrawing = this.createDrawingAnnotation;
@@ -155,6 +157,9 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
}
};
+ /**
+ * Defines how a GPT drawing should be added to the current document.
+ */
@undoBatch
createDrawingAnnotation = action((drawing: Doc, opts: DrawingOptions, gptRes: string) => {
this.AddDrawingAnnotation(drawing);
@@ -203,12 +208,6 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
AnchorMenu.Instance.fadeOut(true);
};
- @action
- tapeClicked = () => {
- this.Tape(this.highlightColor);
- // AnchorMenu.Instance.fadeOut(true);
- };
-
@computed get highlighter() {
return (
<Group>
@@ -219,13 +218,6 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
colorPicker={this.highlightColor}
color={SettingsManager.userColor}
/>
- <IconButton
- tooltip="Click to Add Tape" //
- icon={<FontAwesomeIcon icon="tape" />}
- onClick={this.tapeClicked}
- colorPicker={this.highlightColor}
- color={SettingsManager.userColor}
- />
<ColorPicker selectedColor={this.highlightColor} setFinalColor={this.changeHighlightColor} setSelectedColor={this.changeHighlightColor} size={Size.XSMALL} />
</Group>
);
diff --git a/src/client/views/smartdraw/AnnotationPalette.tsx b/src/client/views/smartdraw/AnnotationPalette.tsx
index 7e4d46204..7f00fa2f2 100644
--- a/src/client/views/smartdraw/AnnotationPalette.tsx
+++ b/src/client/views/smartdraw/AnnotationPalette.tsx
@@ -107,6 +107,10 @@ export class AnnotationPalette extends ObservableReactComponent<AnnotationPalett
this._props.Document[DocData].data = undefined;
};
+ /**
+ * Adds a doc to the annotation palette. Gets a snapshot of the document to use as a preview in the palette. When this
+ * preview is dragged onto a parent document, a copy of that document is added as an annotation.
+ */
public static addToPalette = async (doc: Doc) => {
if (!doc.savedAsAnno) {
const clone = await Doc.MakeClone(doc);
@@ -129,6 +133,10 @@ export class AnnotationPalette extends ObservableReactComponent<AnnotationPalett
return undefined;
}
+ /**
+ * Calls the draw with GPT functions in SmartDrawHandler to allow users to generate drawings straight from
+ * the annotation palette.
+ */
@undoBatch
generateDrawings = action(async () => {
this._isLoading = true;
@@ -159,6 +167,11 @@ export class AnnotationPalette extends ObservableReactComponent<AnnotationPalett
Doc.AddDocToList(this._props.Document, 'data', drawing);
};
+ /**
+ * Saves the currently showing, newly generated drawing to the annotation palette and sets the metadata.
+ * AddToPalette() is generically used to add any document to the palette, while this defines the behavior for when a user
+ * presses the "save drawing" button.
+ */
saveDrawing = async () => {
const cIndex: number = this._props.Document.carousel_index as number;
const focusedDrawing = DocListCast(this._props.Document.data)[cIndex];
diff --git a/src/client/views/smartdraw/SmartDrawHandler.tsx b/src/client/views/smartdraw/SmartDrawHandler.tsx
index 52df598ee..4ec787e07 100644
--- a/src/client/views/smartdraw/SmartDrawHandler.tsx
+++ b/src/client/views/smartdraw/SmartDrawHandler.tsx
@@ -208,6 +208,9 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> {
this._canInteract = true;
};
+ /**
+ * Calls GPT API to create a drawing based on user input
+ */
@action
drawWithGPT = async (startPt: { X: number; Y: number }, input: string, complexity: number, size: number, autoColor: boolean) => {
if (input === '') return;
@@ -226,6 +229,9 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> {
return strokeData;
};
+ /**
+ * Regenerates drawings with the option to add a specific regenerate prompt/request.
+ */
@action
regenerate = async (lastInput?: DrawingOptions, lastResponse?: string, regenInput?: string) => {
if (lastInput) this._lastInput = lastInput;
@@ -256,6 +262,9 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> {
}
};
+ /**
+ * Parses the svg code that GPT returns into Bezier curves.
+ */
@action
parseSvg = async (res: string, startPoint: { X: number; Y: number }, regenerate: boolean, autoColor: boolean) => {
const svg = res.match(/<svg[^>]*>([\s\S]*?)<\/svg>/g);
@@ -278,6 +287,9 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> {
}
};
+ /**
+ * Sends request to GPT API to recolor a selected ink document or group of ink documents.
+ */
colorWithGPT = async (drawing: Doc) => {
const img = await this.getIcon(drawing);
const { href } = (img as URLField).url;
@@ -310,6 +322,9 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> {
}
};
+ /**
+ * Function that parses the GPT color response and sets the selected stroke(s) to the new color.
+ */
@undoBatch
colorStrokes = (res: string, drawing: Doc) => {
const colorList = res.match(/\{.*?\}/g);
@@ -320,13 +335,14 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> {
strokes[index][DocData].color = strokeAndFill[0];
const inkStroke = DocumentView.getDocumentView(strokes[index])?.ComponentView as InkingStroke;
const { inkData } = inkStroke.inkScaledData();
- if (InkingStroke.IsClosed(inkData)) {
- strokes[index][DocData].fillColor = strokeAndFill[1];
- }
+ InkingStroke.IsClosed(inkData) ? (strokes[index][DocData].fillColor = strokeAndFill[1]) : (strokes[index][DocData].fillColor = undefined);
}
});
};
+ /**
+ * Gets an image snapshot of a doc. In this class, it's used to snapshot a selected ink stroke/group to use for GPT color.
+ */
async getIcon(doc: Doc) {
const docView = DocumentView.getDocumentView(doc);
console.log(doc);