From c94bbf5b9f1a37630208d4ba64a93c5399044042 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 30 Nov 2023 20:47:28 -0500
Subject: completed transition from rootDoc => Document, dataDoc, layoutDoc

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

(limited to 'src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx')

diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index e7ca26d5c..4212d46eb 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -135,7 +135,7 @@ export class FormattedTextBoxComment {
             naft &&
                 LinkDocPreview.SetLinkInfo({
                     docProps: textBox.props,
-                    linkSrc: textBox.rootDoc,
+                    linkSrc: textBox.Document,
                     linkDoc: linkDoc ? (DocServer.GetCachedRefField(linkDoc) as Doc) : undefined,
                     location: (pos => [pos.left, pos.top + 25])(view.coordsAtPos(state.selection.from - Math.max(0, nbef - 1))),
                     hrefs,
-- 
cgit v1.2.3-70-g09d2


From be2569d8640f1693eb27f124ad3dd8062ada4837 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 11 Dec 2023 00:41:05 -0500
Subject: more updates to mobx 6.  updated typescript to v5. updated pdf-dist

---
 package-lock.json                                  | 1521 ++------------------
 package.json                                       |    9 +-
 src/client/views/ContextMenu.tsx                   |    1 -
 src/client/views/DashboardView.tsx                 |    7 +-
 src/client/views/MainView.tsx                      |    7 +-
 src/client/views/PropertiesDocContextSelector.tsx  |   27 +-
 src/client/views/SidebarAnnos.tsx                  |   77 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |    2 +-
 .../collectionSchema/SchemaTableCell.tsx           |    6 +-
 src/client/views/linking/LinkMenu.tsx              |    4 +-
 src/client/views/linking/LinkMenuItem.tsx          |    6 +-
 src/client/views/nodes/DocumentView.tsx            |    2 +-
 src/client/views/nodes/LinkAnchorBox.tsx           |    4 +-
 src/client/views/nodes/LinkDocPreview.tsx          |   96 +-
 src/client/views/nodes/PDFBox.tsx                  |    3 +-
 src/client/views/nodes/WebBox.tsx                  |  154 +-
 .../views/nodes/formattedText/DashFieldView.tsx    |   49 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |    4 +-
 .../formattedText/FormattedTextBoxComment.tsx      |    4 +-
 src/client/views/pdf/PDFViewer.tsx                 |   32 +-
 src/server/ApiManagers/PDFManager.ts               |   50 +-
 src/server/DashUploadUtils.ts                      |   25 +-
 src/typings/index.d.ts                             |    1 +
 webpack.config.js                                  |   22 +-
 24 files changed, 477 insertions(+), 1636 deletions(-)

(limited to 'src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx')

diff --git a/package-lock.json b/package-lock.json
index 9afc22828..272851e13 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,6 +11,7 @@
                 "@azure/storage-blob": "^12.17.0",
                 "@babel/preset-env": "^7.23.5",
                 "@babel/preset-react": "^7.23.3",
+                "@bundled-es-modules/pdfjs-dist": "^3.6.172-alpha.1",
                 "@emotion/react": "^11.11.1",
                 "@emotion/styled": "^11.11.0",
                 "@ffmpeg/core": "0.12.4",
@@ -35,7 +36,7 @@
                 "@types/google-maps": "^3.2.6",
                 "@types/reveal": "^4.2.0",
                 "@types/supercluster": "^7.1.3",
-                "@types/web": "0.0.125",
+                "@types/web": "^0.0.127",
                 "@webscopeio/react-textarea-autocomplete": "^4.9.2",
                 "adm-zip": "^0.5.10",
                 "archiver": "^6.0.1",
@@ -87,7 +88,7 @@
                 "flexlayout-react": "^0.7.15",
                 "fluent-ffmpeg": "^2.1.2",
                 "forever-agent": "^0.6.1",
-                "fork-ts-checker-webpack-plugin": "^1.6.0",
+                "fork-ts-checker-webpack-plugin": "^9.0.2",
                 "formidable": "3.5.1",
                 "function-plot": "^1.23.3",
                 "golden-layout": "^2.6.0",
@@ -135,7 +136,7 @@
                 "path-browserify": "^1.0.1",
                 "pdf-parse": "^1.1.1",
                 "pdfjs": "^2.4.7",
-                "pdfjs-dist": "^2.14.305",
+                "pdfjs-dist": "^4.0.269",
                 "probe-image-size": "^7.2.3",
                 "process": "^0.11.10",
                 "prosemirror-commands": "^1.5.2",
@@ -275,7 +276,7 @@
                 "ts-loader": "^9.5.1",
                 "ts-node": "^10.9.1",
                 "ts-node-dev": "^2.0.0",
-                "typescript": "^4.7.4",
+                "typescript": "^5.3.3",
                 "webpack-dev-server": "^4.15.1",
                 "webpack-hot-middleware": "^2.25.4"
             }
@@ -2245,6 +2246,11 @@
                 "node": ">=6.9.0"
             }
         },
+        "node_modules/@bundled-es-modules/pdfjs-dist": {
+            "version": "3.6.172-alpha.1",
+            "resolved": "https://registry.npmjs.org/@bundled-es-modules/pdfjs-dist/-/pdfjs-dist-3.6.172-alpha.1.tgz",
+            "integrity": "sha512-edQXn/5UR1LU1rPoFaZCBk22X8COSO4xctUjakkX6YtZ8BYT9J4EStU8X4bqX7ht2FLC3J21YMQ6RPDmut/l2g=="
+        },
         "node_modules/@cspotcode/source-map-support": {
             "version": "0.8.1",
             "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
@@ -5076,9 +5082,9 @@
             "dev": true
         },
         "node_modules/@types/web": {
-            "version": "0.0.125",
-            "resolved": "https://registry.npmjs.org/@types/web/-/web-0.0.125.tgz",
-            "integrity": "sha512-TGZ4jAe1Nj4x+/wZICAZhAM8K3RIqti90ye0onlprDarBH0nLImnyZ2S729X+mzab3EMBu3KWd/KNwJbKHRV4g=="
+            "version": "0.0.127",
+            "resolved": "https://registry.npmjs.org/@types/web/-/web-0.0.127.tgz",
+            "integrity": "sha512-5PR8ULDp5pYzBWke+VKihf2TkE2H5+RyyGJSDFAk+R1dWUKpwPoW/OiWv0q7DaLeJ/qvXPoKSuV331VsaEneew=="
         },
         "node_modules/@types/webidl-conversions": {
             "version": "7.0.3",
@@ -5776,30 +5782,6 @@
                 "dequal": "^2.0.3"
             }
         },
-        "node_modules/arr-diff": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
-            "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/arr-flatten": {
-            "version": "1.1.0",
-            "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
-            "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/arr-union": {
-            "version": "3.1.0",
-            "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
-            "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/array-back": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz",
@@ -5981,14 +5963,6 @@
                 "node": ">=8"
             }
         },
-        "node_modules/array-unique": {
-            "version": "0.3.2",
-            "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
-            "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/array.prototype.findlastindex": {
             "version": "1.2.3",
             "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz",
@@ -6148,14 +6122,6 @@
                 "node": "*"
             }
         },
-        "node_modules/assign-symbols": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
-            "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/ast-types-flow": {
             "version": "0.0.8",
             "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz",
@@ -6176,17 +6142,6 @@
             "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz",
             "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg=="
         },
-        "node_modules/async-each": {
-            "version": "1.0.6",
-            "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.6.tgz",
-            "integrity": "sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg==",
-            "funding": [
-                {
-                    "type": "individual",
-                    "url": "https://paulmillr.com/funding/"
-                }
-            ]
-        },
         "node_modules/asynciterator.prototype": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz",
@@ -6201,17 +6156,6 @@
             "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
             "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
         },
-        "node_modules/atob": {
-            "version": "2.1.2",
-            "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
-            "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
-            "bin": {
-                "atob": "bin/atob.js"
-            },
-            "engines": {
-                "node": ">= 4.5.0"
-            }
-        },
         "node_modules/available-typed-arrays": {
             "version": "1.0.5",
             "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
@@ -6293,71 +6237,6 @@
                 "babel-node": "lib/cli.js"
             }
         },
-        "node_modules/babel-code-frame": {
-            "version": "6.26.0",
-            "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
-            "integrity": "sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==",
-            "dependencies": {
-                "chalk": "^1.1.3",
-                "esutils": "^2.0.2",
-                "js-tokens": "^3.0.2"
-            }
-        },
-        "node_modules/babel-code-frame/node_modules/ansi-regex": {
-            "version": "2.1.1",
-            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-            "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/babel-code-frame/node_modules/ansi-styles": {
-            "version": "2.2.1",
-            "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-            "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/babel-code-frame/node_modules/chalk": {
-            "version": "1.1.3",
-            "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-            "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
-            "dependencies": {
-                "ansi-styles": "^2.2.1",
-                "escape-string-regexp": "^1.0.2",
-                "has-ansi": "^2.0.0",
-                "strip-ansi": "^3.0.0",
-                "supports-color": "^2.0.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/babel-code-frame/node_modules/js-tokens": {
-            "version": "3.0.2",
-            "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
-            "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg=="
-        },
-        "node_modules/babel-code-frame/node_modules/strip-ansi": {
-            "version": "3.0.1",
-            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
-            "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
-            "dependencies": {
-                "ansi-regex": "^2.0.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/babel-code-frame/node_modules/supports-color": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-            "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==",
-            "engines": {
-                "node": ">=0.8.0"
-            }
-        },
         "node_modules/babel-eslint": {
             "version": "10.1.0",
             "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz",
@@ -6543,51 +6422,11 @@
             "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
             "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
         },
-        "node_modules/base": {
-            "version": "0.11.2",
-            "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
-            "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
-            "dependencies": {
-                "cache-base": "^1.0.1",
-                "class-utils": "^0.3.5",
-                "component-emitter": "^1.2.1",
-                "define-property": "^1.0.0",
-                "isobject": "^3.0.1",
-                "mixin-deep": "^1.2.0",
-                "pascalcase": "^0.1.1"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/base-64": {
             "version": "0.1.0",
             "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz",
             "integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA=="
         },
-        "node_modules/base/node_modules/define-property": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-            "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
-            "dependencies": {
-                "is-descriptor": "^1.0.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/base/node_modules/is-descriptor": {
-            "version": "1.0.3",
-            "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz",
-            "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==",
-            "dependencies": {
-                "is-accessor-descriptor": "^1.0.1",
-                "is-data-descriptor": "^1.0.1"
-            },
-            "engines": {
-                "node": ">= 0.4"
-            }
-        },
         "node_modules/Base64": {
             "version": "0.2.1",
             "resolved": "https://registry.npmjs.org/Base64/-/Base64-0.2.1.tgz",
@@ -6700,15 +6539,6 @@
                 "node": ">=8"
             }
         },
-        "node_modules/bindings": {
-            "version": "1.5.0",
-            "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
-            "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
-            "optional": true,
-            "dependencies": {
-                "file-uri-to-path": "1.0.0"
-            }
-        },
         "node_modules/bingmaps-react": {
             "version": "1.2.10",
             "resolved": "https://registry.npmjs.org/bingmaps-react/-/bingmaps-react-1.2.10.tgz",
@@ -9895,25 +9725,6 @@
                 "node": ">= 0.8"
             }
         },
-        "node_modules/cache-base": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
-            "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
-            "dependencies": {
-                "collection-visit": "^1.0.0",
-                "component-emitter": "^1.2.1",
-                "get-value": "^2.0.6",
-                "has-value": "^1.0.0",
-                "isobject": "^3.0.1",
-                "set-value": "^2.0.0",
-                "to-object-path": "^0.3.0",
-                "union-value": "^1.0.0",
-                "unset-value": "^1.0.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/cacheable-lookup": {
             "version": "7.0.0",
             "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz",
@@ -10229,20 +10040,6 @@
             "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz",
             "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw=="
         },
-        "node_modules/class-utils": {
-            "version": "0.3.6",
-            "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
-            "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
-            "dependencies": {
-                "arr-union": "^3.1.0",
-                "define-property": "^0.2.5",
-                "isobject": "^3.0.0",
-                "static-extend": "^0.1.1"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/classnames": {
             "version": "2.3.2",
             "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",
@@ -10372,18 +10169,6 @@
                 "node": ">=6"
             }
         },
-        "node_modules/collection-visit": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
-            "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==",
-            "dependencies": {
-                "map-visit": "^1.0.0",
-                "object-visit": "^1.0.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/color": {
             "version": "4.2.3",
             "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
@@ -10502,14 +10287,6 @@
             "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz",
             "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w=="
         },
-        "node_modules/component-emitter": {
-            "version": "1.3.1",
-            "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz",
-            "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==",
-            "funding": {
-                "url": "https://github.com/sponsors/sindresorhus"
-            }
-        },
         "node_modules/compress-commons": {
             "version": "5.0.1",
             "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-5.0.1.tgz",
@@ -10770,14 +10547,6 @@
                 "node": ">= 0.8"
             }
         },
-        "node_modules/copy-descriptor": {
-            "version": "0.1.1",
-            "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
-            "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/core-js": {
             "version": "3.34.0",
             "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.34.0.tgz",
@@ -11884,17 +11653,6 @@
                 "url": "https://github.com/sponsors/ljharb"
             }
         },
-        "node_modules/define-property": {
-            "version": "0.2.5",
-            "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-            "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
-            "dependencies": {
-                "is-descriptor": "^0.1.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/delaunator": {
             "version": "5.0.0",
             "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz",
@@ -12374,12 +12132,6 @@
                 "url": "https://github.com/fb55/domhandler?sponsor=1"
             }
         },
-        "node_modules/dommatrix": {
-            "version": "1.0.3",
-            "resolved": "https://registry.npmjs.org/dommatrix/-/dommatrix-1.0.3.tgz",
-            "integrity": "sha512-l32Xp/TLgWb8ReqbVJAFIvXmY7go4nTxxlWiAFyhoQw9RKEOHBZNnyGvJWqDVSPmq3Y9HlM4npqF/T6VMOXhww==",
-            "deprecated": "dommatrix is no longer maintained. Please use @thednp/dommatrix."
-        },
         "node_modules/domutils": {
             "version": "3.1.0",
             "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
@@ -14544,36 +14296,6 @@
             "resolved": "https://registry.npmjs.org/exifr/-/exifr-7.1.3.tgz",
             "integrity": "sha512-g/aje2noHivrRSLbAUtBPWFbxKdKhgj/xr1vATDdUXPOFYJlQ62Ft0oy+72V6XLIpDJfHs6gXLbBLAolqOXYRw=="
         },
-        "node_modules/expand-brackets": {
-            "version": "2.1.4",
-            "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
-            "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==",
-            "dependencies": {
-                "debug": "^2.3.3",
-                "define-property": "^0.2.5",
-                "extend-shallow": "^2.0.1",
-                "posix-character-classes": "^0.1.0",
-                "regex-not": "^1.0.0",
-                "snapdragon": "^0.8.1",
-                "to-regex": "^3.0.1"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/expand-brackets/node_modules/debug": {
-            "version": "2.6.9",
-            "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-            "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-            "dependencies": {
-                "ms": "2.0.0"
-            }
-        },
-        "node_modules/expand-brackets/node_modules/ms": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-            "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
-        },
         "node_modules/expand-tilde": {
             "version": "2.0.2",
             "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz",
@@ -14814,17 +14536,6 @@
             "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
             "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
         },
-        "node_modules/extend-shallow": {
-            "version": "2.0.1",
-            "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-            "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
-            "dependencies": {
-                "is-extendable": "^0.1.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/external-editor": {
             "version": "3.1.0",
             "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
@@ -14851,47 +14562,6 @@
                 "node": ">=0.10.0"
             }
         },
-        "node_modules/extglob": {
-            "version": "2.0.4",
-            "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
-            "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
-            "dependencies": {
-                "array-unique": "^0.3.2",
-                "define-property": "^1.0.0",
-                "expand-brackets": "^2.1.4",
-                "extend-shallow": "^2.0.1",
-                "fragment-cache": "^0.2.1",
-                "regex-not": "^1.0.0",
-                "snapdragon": "^0.8.1",
-                "to-regex": "^3.0.1"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/extglob/node_modules/define-property": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-            "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
-            "dependencies": {
-                "is-descriptor": "^1.0.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/extglob/node_modules/is-descriptor": {
-            "version": "1.0.3",
-            "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz",
-            "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==",
-            "dependencies": {
-                "is-accessor-descriptor": "^1.0.1",
-                "is-data-descriptor": "^1.0.1"
-            },
-            "engines": {
-                "node": ">= 0.4"
-            }
-        },
         "node_modules/extsprintf": {
             "version": "1.3.0",
             "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
@@ -15089,12 +14759,6 @@
                 "url": "https://github.com/sindresorhus/file-type?sponsor=1"
             }
         },
-        "node_modules/file-uri-to-path": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
-            "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
-            "optional": true
-        },
         "node_modules/fill-range": {
             "version": "7.0.1",
             "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@@ -15410,14 +15074,6 @@
                 "for-each-property": "0.0.4"
             }
         },
-        "node_modules/for-in": {
-            "version": "1.0.2",
-            "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
-            "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/foreground-child": {
             "version": "3.1.1",
             "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
@@ -15453,299 +15109,189 @@
             }
         },
         "node_modules/fork-ts-checker-webpack-plugin": {
-            "version": "1.6.0",
-            "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-1.6.0.tgz",
-            "integrity": "sha512-vqOY5gakcoon2s12V7MMe01OPwfgqulUWFzm+geQaPPOBKjW1I7aqqoBVlU0ECn97liMB0ECs16pRdIGe9qdRw==",
+            "version": "9.0.2",
+            "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-9.0.2.tgz",
+            "integrity": "sha512-Uochze2R8peoN1XqlSi/rGUkDQpRogtLFocP9+PGu68zk1BDAKXfdeCdyVZpgTk8V8WFVQXdEz426VKjXLO1Gg==",
             "dependencies": {
-                "babel-code-frame": "^6.22.0",
-                "chalk": "^2.4.1",
-                "chokidar": "^2.0.4",
-                "micromatch": "^3.1.10",
+                "@babel/code-frame": "^7.16.7",
+                "chalk": "^4.1.2",
+                "chokidar": "^3.5.3",
+                "cosmiconfig": "^8.2.0",
+                "deepmerge": "^4.2.2",
+                "fs-extra": "^10.0.0",
+                "memfs": "^3.4.1",
                 "minimatch": "^3.0.4",
-                "semver": "^5.6.0",
-                "tapable": "^1.0.0",
-                "worker-rpc": "^0.1.0"
+                "node-abort-controller": "^3.0.1",
+                "schema-utils": "^3.1.1",
+                "semver": "^7.3.5",
+                "tapable": "^2.2.1"
             },
             "engines": {
-                "node": ">=6.11.5",
+                "node": ">=12.13.0",
                 "yarn": ">=1.0.0"
+            },
+            "peerDependencies": {
+                "typescript": ">3.6.0",
+                "webpack": "^5.11.0"
             }
         },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/anymatch": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
-            "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
-            "dependencies": {
-                "micromatch": "^3.1.4",
-                "normalize-path": "^2.1.1"
-            }
-        },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/anymatch/node_modules/normalize-path": {
-            "version": "2.1.1",
-            "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
-            "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==",
+        "node_modules/fork-ts-checker-webpack-plugin/node_modules/ansi-styles": {
+            "version": "4.3.0",
+            "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+            "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
             "dependencies": {
-                "remove-trailing-separator": "^1.0.1"
+                "color-convert": "^2.0.1"
             },
             "engines": {
-                "node": ">=0.10.0"
+                "node": ">=8"
+            },
+            "funding": {
+                "url": "https://github.com/chalk/ansi-styles?sponsor=1"
             }
         },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/binary-extensions": {
-            "version": "1.13.1",
-            "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
-            "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
+        "node_modules/fork-ts-checker-webpack-plugin/node_modules/argparse": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+            "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
         },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/braces": {
-            "version": "2.3.2",
-            "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
-            "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+        "node_modules/fork-ts-checker-webpack-plugin/node_modules/chalk": {
+            "version": "4.1.2",
+            "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+            "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
             "dependencies": {
-                "arr-flatten": "^1.1.0",
-                "array-unique": "^0.3.2",
-                "extend-shallow": "^2.0.1",
-                "fill-range": "^4.0.0",
-                "isobject": "^3.0.1",
-                "repeat-element": "^1.1.2",
-                "snapdragon": "^0.8.1",
-                "snapdragon-node": "^2.0.1",
-                "split-string": "^3.0.2",
-                "to-regex": "^3.0.1"
+                "ansi-styles": "^4.1.0",
+                "supports-color": "^7.1.0"
             },
             "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/chokidar": {
-            "version": "2.1.8",
-            "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
-            "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
-            "deprecated": "Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies",
-            "dependencies": {
-                "anymatch": "^2.0.0",
-                "async-each": "^1.0.1",
-                "braces": "^2.3.2",
-                "glob-parent": "^3.1.0",
-                "inherits": "^2.0.3",
-                "is-binary-path": "^1.0.0",
-                "is-glob": "^4.0.0",
-                "normalize-path": "^3.0.0",
-                "path-is-absolute": "^1.0.0",
-                "readdirp": "^2.2.1",
-                "upath": "^1.1.1"
+                "node": ">=10"
             },
-            "optionalDependencies": {
-                "fsevents": "^1.2.7"
+            "funding": {
+                "url": "https://github.com/chalk/chalk?sponsor=1"
             }
         },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/define-property": {
-            "version": "2.0.2",
-            "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
-            "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
+        "node_modules/fork-ts-checker-webpack-plugin/node_modules/color-convert": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+            "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
             "dependencies": {
-                "is-descriptor": "^1.0.2",
-                "isobject": "^3.0.1"
+                "color-name": "~1.1.4"
             },
             "engines": {
-                "node": ">=0.10.0"
+                "node": ">=7.0.0"
             }
         },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/fill-range": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
-            "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==",
+        "node_modules/fork-ts-checker-webpack-plugin/node_modules/color-name": {
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+            "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+        },
+        "node_modules/fork-ts-checker-webpack-plugin/node_modules/cosmiconfig": {
+            "version": "8.3.6",
+            "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz",
+            "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==",
             "dependencies": {
-                "extend-shallow": "^2.0.1",
-                "is-number": "^3.0.0",
-                "repeat-string": "^1.6.1",
-                "to-regex-range": "^2.1.0"
+                "import-fresh": "^3.3.0",
+                "js-yaml": "^4.1.0",
+                "parse-json": "^5.2.0",
+                "path-type": "^4.0.0"
             },
             "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/fsevents": {
-            "version": "1.2.13",
-            "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
-            "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
-            "deprecated": "The v1 package contains DANGEROUS / INSECURE binaries. Upgrade to safe fsevents v2",
-            "hasInstallScript": true,
-            "optional": true,
-            "os": [
-                "darwin"
-            ],
-            "dependencies": {
-                "bindings": "^1.5.0",
-                "nan": "^2.12.1"
+                "node": ">=14"
             },
-            "engines": {
-                "node": ">= 4.0"
-            }
-        },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/glob-parent": {
-            "version": "3.1.0",
-            "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
-            "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==",
-            "dependencies": {
-                "is-glob": "^3.1.0",
-                "path-dirname": "^1.0.0"
-            }
-        },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/glob-parent/node_modules/is-glob": {
-            "version": "3.1.0",
-            "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
-            "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==",
-            "dependencies": {
-                "is-extglob": "^2.1.0"
+            "funding": {
+                "url": "https://github.com/sponsors/d-fischer"
             },
-            "engines": {
-                "node": ">=0.10.0"
+            "peerDependencies": {
+                "typescript": ">=4.9.5"
+            },
+            "peerDependenciesMeta": {
+                "typescript": {
+                    "optional": true
+                }
             }
         },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/is-binary-path": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
-            "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==",
-            "dependencies": {
-                "binary-extensions": "^1.0.0"
-            },
+        "node_modules/fork-ts-checker-webpack-plugin/node_modules/deepmerge": {
+            "version": "4.3.1",
+            "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
+            "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
             "engines": {
                 "node": ">=0.10.0"
             }
         },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/is-buffer": {
-            "version": "1.1.6",
-            "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
-            "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
-        },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/is-descriptor": {
-            "version": "1.0.3",
-            "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz",
-            "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==",
+        "node_modules/fork-ts-checker-webpack-plugin/node_modules/fs-extra": {
+            "version": "10.1.0",
+            "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
+            "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
             "dependencies": {
-                "is-accessor-descriptor": "^1.0.1",
-                "is-data-descriptor": "^1.0.1"
+                "graceful-fs": "^4.2.0",
+                "jsonfile": "^6.0.1",
+                "universalify": "^2.0.0"
             },
             "engines": {
-                "node": ">= 0.4"
+                "node": ">=12"
             }
         },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/is-extendable": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
-            "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
-            "dependencies": {
-                "is-plain-object": "^2.0.4"
-            },
+        "node_modules/fork-ts-checker-webpack-plugin/node_modules/has-flag": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+            "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
             "engines": {
-                "node": ">=0.10.0"
+                "node": ">=8"
             }
         },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/is-number": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-            "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==",
+        "node_modules/fork-ts-checker-webpack-plugin/node_modules/js-yaml": {
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+            "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
             "dependencies": {
-                "kind-of": "^3.0.2"
+                "argparse": "^2.0.1"
             },
-            "engines": {
-                "node": ">=0.10.0"
+            "bin": {
+                "js-yaml": "bin/js-yaml.js"
             }
         },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/is-number/node_modules/kind-of": {
-            "version": "3.2.2",
-            "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-            "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+        "node_modules/fork-ts-checker-webpack-plugin/node_modules/jsonfile": {
+            "version": "6.1.0",
+            "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+            "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
             "dependencies": {
-                "is-buffer": "^1.1.5"
+                "universalify": "^2.0.0"
             },
-            "engines": {
-                "node": ">=0.10.0"
+            "optionalDependencies": {
+                "graceful-fs": "^4.1.6"
             }
         },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/micromatch": {
-            "version": "3.1.10",
-            "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
-            "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+        "node_modules/fork-ts-checker-webpack-plugin/node_modules/semver": {
+            "version": "7.5.4",
+            "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
+            "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
             "dependencies": {
-                "arr-diff": "^4.0.0",
-                "array-unique": "^0.3.2",
-                "braces": "^2.3.1",
-                "define-property": "^2.0.2",
-                "extend-shallow": "^3.0.2",
-                "extglob": "^2.0.4",
-                "fragment-cache": "^0.2.1",
-                "kind-of": "^6.0.2",
-                "nanomatch": "^1.2.9",
-                "object.pick": "^1.3.0",
-                "regex-not": "^1.0.0",
-                "snapdragon": "^0.8.1",
-                "to-regex": "^3.0.2"
+                "lru-cache": "^6.0.0"
             },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/micromatch/node_modules/extend-shallow": {
-            "version": "3.0.2",
-            "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
-            "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
-            "dependencies": {
-                "assign-symbols": "^1.0.0",
-                "is-extendable": "^1.0.1"
+            "bin": {
+                "semver": "bin/semver.js"
             },
             "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/readable-stream": {
-            "version": "2.3.8",
-            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
-            "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
-            "dependencies": {
-                "core-util-is": "~1.0.0",
-                "inherits": "~2.0.3",
-                "isarray": "~1.0.0",
-                "process-nextick-args": "~2.0.0",
-                "safe-buffer": "~5.1.1",
-                "string_decoder": "~1.1.1",
-                "util-deprecate": "~1.0.1"
+                "node": ">=10"
             }
         },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/readdirp": {
-            "version": "2.2.1",
-            "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
-            "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
+        "node_modules/fork-ts-checker-webpack-plugin/node_modules/supports-color": {
+            "version": "7.2.0",
+            "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+            "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
             "dependencies": {
-                "graceful-fs": "^4.1.11",
-                "micromatch": "^3.1.10",
-                "readable-stream": "^2.0.2"
+                "has-flag": "^4.0.0"
             },
             "engines": {
-                "node": ">=0.10"
-            }
-        },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/tapable": {
-            "version": "1.1.3",
-            "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
-            "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
-            "engines": {
-                "node": ">=6"
+                "node": ">=8"
             }
         },
-        "node_modules/fork-ts-checker-webpack-plugin/node_modules/to-regex-range": {
-            "version": "2.1.1",
-            "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
-            "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==",
-            "dependencies": {
-                "is-number": "^3.0.0",
-                "repeat-string": "^1.6.1"
-            },
+        "node_modules/fork-ts-checker-webpack-plugin/node_modules/universalify": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+            "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
             "engines": {
-                "node": ">=0.10.0"
+                "node": ">= 10.0.0"
             }
         },
         "node_modules/form-data": {
@@ -15810,17 +15356,6 @@
                 "node": ">= 0.6"
             }
         },
-        "node_modules/fragment-cache": {
-            "version": "0.2.1",
-            "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
-            "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==",
-            "dependencies": {
-                "map-cache": "^0.2.2"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/fresh": {
             "version": "0.5.2",
             "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
@@ -16193,14 +15728,6 @@
                 "url": "https://github.com/sponsors/ljharb"
             }
         },
-        "node_modules/get-value": {
-            "version": "2.0.6",
-            "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
-            "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/getpass": {
             "version": "0.1.7",
             "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
@@ -16529,25 +16056,6 @@
                 "node": ">= 0.4.0"
             }
         },
-        "node_modules/has-ansi": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
-            "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==",
-            "dependencies": {
-                "ansi-regex": "^2.0.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/has-ansi/node_modules/ansi-regex": {
-            "version": "2.1.1",
-            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-            "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/has-bigints": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
@@ -16616,69 +16124,6 @@
             "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
             "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
         },
-        "node_modules/has-value": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
-            "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==",
-            "dependencies": {
-                "get-value": "^2.0.6",
-                "has-values": "^1.0.0",
-                "isobject": "^3.0.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/has-values": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
-            "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==",
-            "dependencies": {
-                "is-number": "^3.0.0",
-                "kind-of": "^4.0.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/has-values/node_modules/is-buffer": {
-            "version": "1.1.6",
-            "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
-            "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
-        },
-        "node_modules/has-values/node_modules/is-number": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-            "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==",
-            "dependencies": {
-                "kind-of": "^3.0.2"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/has-values/node_modules/is-number/node_modules/kind-of": {
-            "version": "3.2.2",
-            "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-            "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
-            "dependencies": {
-                "is-buffer": "^1.1.5"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/has-values/node_modules/kind-of": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
-            "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==",
-            "dependencies": {
-                "is-buffer": "^1.1.5"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/hasown": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
@@ -17824,17 +17269,6 @@
                 "node": ">= 0.10"
             }
         },
-        "node_modules/is-accessor-descriptor": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.1.tgz",
-            "integrity": "sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==",
-            "dependencies": {
-                "hasown": "^2.0.0"
-            },
-            "engines": {
-                "node": ">= 0.10"
-            }
-        },
         "node_modules/is-alphabetical": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
@@ -17986,17 +17420,6 @@
                 "url": "https://github.com/sponsors/ljharb"
             }
         },
-        "node_modules/is-data-descriptor": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.1.tgz",
-            "integrity": "sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw==",
-            "dependencies": {
-                "hasown": "^2.0.0"
-            },
-            "engines": {
-                "node": ">= 0.4"
-            }
-        },
         "node_modules/is-date-object": {
             "version": "1.0.5",
             "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
@@ -18020,18 +17443,6 @@
                 "url": "https://github.com/sponsors/wooorm"
             }
         },
-        "node_modules/is-descriptor": {
-            "version": "0.1.7",
-            "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz",
-            "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==",
-            "dependencies": {
-                "is-accessor-descriptor": "^1.0.1",
-                "is-data-descriptor": "^1.0.1"
-            },
-            "engines": {
-                "node": ">= 0.4"
-            }
-        },
         "node_modules/is-docker": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz",
@@ -18067,14 +17478,6 @@
                 "node": ">=0.4.0"
             }
         },
-        "node_modules/is-extendable": {
-            "version": "0.1.1",
-            "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
-            "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/is-extglob": {
             "version": "2.1.1",
             "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -19361,25 +18764,6 @@
             "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
             "dev": true
         },
-        "node_modules/map-cache": {
-            "version": "0.2.2",
-            "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
-            "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/map-visit": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
-            "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==",
-            "dependencies": {
-                "object-visit": "^1.0.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/markdown-table": {
             "version": "3.0.3",
             "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.3.tgz",
@@ -19766,11 +19150,6 @@
                 "node": ">= 0.6"
             }
         },
-        "node_modules/microevent.ts": {
-            "version": "0.1.1",
-            "resolved": "https://registry.npmjs.org/microevent.ts/-/microevent.ts-0.1.1.tgz",
-            "integrity": "sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g=="
-        },
         "node_modules/micromark": {
             "version": "4.0.0",
             "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.0.tgz",
@@ -20452,29 +19831,6 @@
                 "node": ">=8"
             }
         },
-        "node_modules/mixin-deep": {
-            "version": "1.3.2",
-            "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
-            "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
-            "dependencies": {
-                "for-in": "^1.0.2",
-                "is-extendable": "^1.0.1"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/mixin-deep/node_modules/is-extendable": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
-            "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
-            "dependencies": {
-                "is-plain-object": "^2.0.4"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/mkdirp": {
             "version": "0.5.4",
             "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz",
@@ -21404,74 +20760,6 @@
             "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz",
             "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ=="
         },
-        "node_modules/nanomatch": {
-            "version": "1.2.13",
-            "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
-            "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
-            "dependencies": {
-                "arr-diff": "^4.0.0",
-                "array-unique": "^0.3.2",
-                "define-property": "^2.0.2",
-                "extend-shallow": "^3.0.2",
-                "fragment-cache": "^0.2.1",
-                "is-windows": "^1.0.2",
-                "kind-of": "^6.0.2",
-                "object.pick": "^1.3.0",
-                "regex-not": "^1.0.0",
-                "snapdragon": "^0.8.1",
-                "to-regex": "^3.0.1"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/nanomatch/node_modules/define-property": {
-            "version": "2.0.2",
-            "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
-            "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
-            "dependencies": {
-                "is-descriptor": "^1.0.2",
-                "isobject": "^3.0.1"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/nanomatch/node_modules/extend-shallow": {
-            "version": "3.0.2",
-            "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
-            "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
-            "dependencies": {
-                "assign-symbols": "^1.0.0",
-                "is-extendable": "^1.0.1"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/nanomatch/node_modules/is-descriptor": {
-            "version": "1.0.3",
-            "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz",
-            "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==",
-            "dependencies": {
-                "is-accessor-descriptor": "^1.0.1",
-                "is-data-descriptor": "^1.0.1"
-            },
-            "engines": {
-                "node": ">= 0.4"
-            }
-        },
-        "node_modules/nanomatch/node_modules/is-extendable": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
-            "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
-            "dependencies": {
-                "is-plain-object": "^2.0.4"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/natural-compare": {
             "version": "1.4.0",
             "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@@ -21541,6 +20829,11 @@
                 "tslib": "^2.0.3"
             }
         },
+        "node_modules/node-abort-controller": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz",
+            "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ=="
+        },
         "node_modules/node-domexception": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
@@ -24533,35 +23826,6 @@
                 "node": ">=0.10.0"
             }
         },
-        "node_modules/object-copy": {
-            "version": "0.1.0",
-            "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
-            "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==",
-            "dependencies": {
-                "copy-descriptor": "^0.1.0",
-                "define-property": "^0.2.5",
-                "kind-of": "^3.0.3"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/object-copy/node_modules/is-buffer": {
-            "version": "1.1.6",
-            "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
-            "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
-        },
-        "node_modules/object-copy/node_modules/kind-of": {
-            "version": "3.2.2",
-            "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-            "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
-            "dependencies": {
-                "is-buffer": "^1.1.5"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/object-inspect": {
             "version": "1.13.1",
             "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
@@ -24623,17 +23887,6 @@
                 "pipe-functions": "^1.2.0"
             }
         },
-        "node_modules/object-visit": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
-            "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==",
-            "dependencies": {
-                "isobject": "^3.0.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/object.assign": {
             "version": "4.1.0",
             "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
@@ -24722,17 +23975,6 @@
                 "url": "https://github.com/sponsors/ljharb"
             }
         },
-        "node_modules/object.pick": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
-            "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==",
-            "dependencies": {
-                "isobject": "^3.0.1"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/object.values": {
             "version": "1.1.7",
             "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz",
@@ -25136,14 +24378,6 @@
                 "tslib": "^2.0.3"
             }
         },
-        "node_modules/pascalcase": {
-            "version": "0.1.1",
-            "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
-            "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/passport": {
             "version": "0.7.0",
             "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz",
@@ -25215,11 +24449,6 @@
             "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
             "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="
         },
-        "node_modules/path-dirname": {
-            "version": "1.0.2",
-            "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
-            "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q=="
-        },
         "node_modules/path-exists": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
@@ -25294,6 +24523,15 @@
                 "node": ">=8"
             }
         },
+        "node_modules/path2d-polyfill": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/path2d-polyfill/-/path2d-polyfill-2.0.1.tgz",
+            "integrity": "sha512-ad/3bsalbbWhmBo0D6FZ4RNMwsLsPpL6gnvhuSaU5Vm7b06Kr5ubSltQQ0T7YKsiJQO+g22zJ4dJKNTXIyOXtA==",
+            "optional": true,
+            "engines": {
+                "node": ">=8"
+            }
+        },
         "node_modules/pathval": {
             "version": "1.1.1",
             "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
@@ -25336,20 +24574,15 @@
             }
         },
         "node_modules/pdfjs-dist": {
-            "version": "2.16.105",
-            "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.16.105.tgz",
-            "integrity": "sha512-J4dn41spsAwUxCpEoVf6GVoz908IAA3mYiLmNxg8J9kfRXc2jxpbUepcP0ocp0alVNLFthTAM8DZ1RaHh8sU0A==",
-            "dependencies": {
-                "dommatrix": "^1.0.3",
-                "web-streams-polyfill": "^3.2.1"
-            },
-            "peerDependencies": {
-                "worker-loader": "^3.0.8"
+            "version": "4.0.269",
+            "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-4.0.269.tgz",
+            "integrity": "sha512-jjWO56tcOjnmPqDf8PmXDeZ781AGvpHMYI3HhNtaFKTRXXPaD1ArSrhVe38/XsrIQJ0onISCND/vuXaWJkiDWw==",
+            "engines": {
+                "node": ">=18"
             },
-            "peerDependenciesMeta": {
-                "worker-loader": {
-                    "optional": true
-                }
+            "optionalDependencies": {
+                "canvas": "^2.11.2",
+                "path2d-polyfill": "^2.0.1"
             }
         },
         "node_modules/pdfjs/node_modules/pako": {
@@ -25515,14 +24748,6 @@
                 "node": ">=4.0.0"
             }
         },
-        "node_modules/posix-character-classes": {
-            "version": "0.1.1",
-            "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
-            "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/postcss": {
             "version": "8.4.32",
             "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz",
@@ -26816,41 +26041,6 @@
                 "@babel/runtime": "^7.8.4"
             }
         },
-        "node_modules/regex-not": {
-            "version": "1.0.2",
-            "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
-            "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
-            "dependencies": {
-                "extend-shallow": "^3.0.2",
-                "safe-regex": "^1.1.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/regex-not/node_modules/extend-shallow": {
-            "version": "3.0.2",
-            "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
-            "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
-            "dependencies": {
-                "assign-symbols": "^1.0.0",
-                "is-extendable": "^1.0.1"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/regex-not/node_modules/is-extendable": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
-            "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
-            "dependencies": {
-                "is-plain-object": "^2.0.4"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/regexp.prototype.flags": {
             "version": "1.5.1",
             "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz",
@@ -26998,11 +26188,6 @@
                 "url": "https://opencollective.com/unified"
             }
         },
-        "node_modules/remove-trailing-separator": {
-            "version": "1.1.0",
-            "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
-            "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw=="
-        },
         "node_modules/renderkid": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz",
@@ -27100,22 +26285,6 @@
                 "node": ">=8"
             }
         },
-        "node_modules/repeat-element": {
-            "version": "1.1.4",
-            "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz",
-            "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/repeat-string": {
-            "version": "1.6.1",
-            "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
-            "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==",
-            "engines": {
-                "node": ">=0.10"
-            }
-        },
         "node_modules/request": {
             "version": "2.88.2",
             "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
@@ -27308,12 +26477,6 @@
                 "node": ">=4"
             }
         },
-        "node_modules/resolve-url": {
-            "version": "0.2.1",
-            "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
-            "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==",
-            "deprecated": "https://github.com/lydell/resolve-url#deprecated"
-        },
         "node_modules/responselike": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz",
@@ -27341,14 +26504,6 @@
                 "node": ">=8"
             }
         },
-        "node_modules/ret": {
-            "version": "0.1.15",
-            "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
-            "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
-            "engines": {
-                "node": ">=0.12"
-            }
-        },
         "node_modules/retry": {
             "version": "0.13.1",
             "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
@@ -27631,14 +26786,6 @@
             "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
             "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
         },
-        "node_modules/safe-regex": {
-            "version": "1.1.0",
-            "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
-            "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==",
-            "dependencies": {
-                "ret": "~0.1.10"
-            }
-        },
         "node_modules/safe-regex-test": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz",
@@ -27981,20 +27128,6 @@
                 "node": ">= 0.4"
             }
         },
-        "node_modules/set-value": {
-            "version": "2.0.1",
-            "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
-            "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
-            "dependencies": {
-                "extend-shallow": "^2.0.1",
-                "is-extendable": "^0.1.1",
-                "is-plain-object": "^2.0.3",
-                "split-string": "^3.0.1"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/setimmediate": {
             "version": "1.0.5",
             "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
@@ -28176,100 +27309,6 @@
                 "node": ">=6"
             }
         },
-        "node_modules/snapdragon": {
-            "version": "0.8.2",
-            "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
-            "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
-            "dependencies": {
-                "base": "^0.11.1",
-                "debug": "^2.2.0",
-                "define-property": "^0.2.5",
-                "extend-shallow": "^2.0.1",
-                "map-cache": "^0.2.2",
-                "source-map": "^0.5.6",
-                "source-map-resolve": "^0.5.0",
-                "use": "^3.1.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/snapdragon-node": {
-            "version": "2.1.1",
-            "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
-            "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
-            "dependencies": {
-                "define-property": "^1.0.0",
-                "isobject": "^3.0.0",
-                "snapdragon-util": "^3.0.1"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/snapdragon-node/node_modules/define-property": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-            "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
-            "dependencies": {
-                "is-descriptor": "^1.0.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/snapdragon-node/node_modules/is-descriptor": {
-            "version": "1.0.3",
-            "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz",
-            "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==",
-            "dependencies": {
-                "is-accessor-descriptor": "^1.0.1",
-                "is-data-descriptor": "^1.0.1"
-            },
-            "engines": {
-                "node": ">= 0.4"
-            }
-        },
-        "node_modules/snapdragon-util": {
-            "version": "3.0.1",
-            "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
-            "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
-            "dependencies": {
-                "kind-of": "^3.2.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/snapdragon-util/node_modules/is-buffer": {
-            "version": "1.1.6",
-            "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
-            "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
-        },
-        "node_modules/snapdragon-util/node_modules/kind-of": {
-            "version": "3.2.2",
-            "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-            "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
-            "dependencies": {
-                "is-buffer": "^1.1.5"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/snapdragon/node_modules/debug": {
-            "version": "2.6.9",
-            "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-            "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-            "dependencies": {
-                "ms": "2.0.0"
-            }
-        },
-        "node_modules/snapdragon/node_modules/ms": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-            "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
-        },
         "node_modules/socket.io": {
             "version": "4.7.2",
             "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.2.tgz",
@@ -28440,19 +27479,6 @@
                 "node": ">=0.10.0"
             }
         },
-        "node_modules/source-map-resolve": {
-            "version": "0.5.3",
-            "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
-            "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==",
-            "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated",
-            "dependencies": {
-                "atob": "^2.1.2",
-                "decode-uri-component": "^0.2.0",
-                "resolve-url": "^0.2.1",
-                "source-map-url": "^0.4.0",
-                "urix": "^0.1.0"
-            }
-        },
         "node_modules/source-map-support": {
             "version": "0.5.21",
             "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
@@ -28470,12 +27496,6 @@
                 "node": ">=0.10.0"
             }
         },
-        "node_modules/source-map-url": {
-            "version": "0.4.1",
-            "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz",
-            "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==",
-            "deprecated": "See https://github.com/lydell/source-map-url#deprecated"
-        },
         "node_modules/space-separated-tokens": {
             "version": "2.0.2",
             "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz",
@@ -28582,40 +27602,6 @@
             "resolved": "https://registry.npmjs.org/split-skip/-/split-skip-0.0.2.tgz",
             "integrity": "sha1-2J2Iu9L3Pka1FYqjcKVhIk6A1GE="
         },
-        "node_modules/split-string": {
-            "version": "3.1.0",
-            "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
-            "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
-            "dependencies": {
-                "extend-shallow": "^3.0.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/split-string/node_modules/extend-shallow": {
-            "version": "3.0.2",
-            "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
-            "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
-            "dependencies": {
-                "assign-symbols": "^1.0.0",
-                "is-extendable": "^1.0.1"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/split-string/node_modules/is-extendable": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
-            "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
-            "dependencies": {
-                "is-plain-object": "^2.0.4"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/sprintf-js": {
             "version": "1.0.3",
             "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
@@ -28658,18 +27644,6 @@
                 "standard-error": ">= 1.1.0 < 2"
             }
         },
-        "node_modules/static-extend": {
-            "version": "0.1.2",
-            "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
-            "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==",
-            "dependencies": {
-                "define-property": "^0.2.5",
-                "object-copy": "^0.1.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/statuses": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
@@ -29335,47 +28309,6 @@
                 "node": ">=4"
             }
         },
-        "node_modules/to-object-path": {
-            "version": "0.3.0",
-            "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
-            "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==",
-            "dependencies": {
-                "kind-of": "^3.0.2"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/to-object-path/node_modules/is-buffer": {
-            "version": "1.1.6",
-            "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
-            "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
-        },
-        "node_modules/to-object-path/node_modules/kind-of": {
-            "version": "3.2.2",
-            "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-            "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
-            "dependencies": {
-                "is-buffer": "^1.1.5"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/to-regex": {
-            "version": "3.0.2",
-            "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
-            "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
-            "dependencies": {
-                "define-property": "^2.0.2",
-                "extend-shallow": "^3.0.2",
-                "regex-not": "^1.0.2",
-                "safe-regex": "^1.1.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/to-regex-range": {
             "version": "5.0.1",
             "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -29387,53 +28320,6 @@
                 "node": ">=8.0"
             }
         },
-        "node_modules/to-regex/node_modules/define-property": {
-            "version": "2.0.2",
-            "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
-            "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
-            "dependencies": {
-                "is-descriptor": "^1.0.2",
-                "isobject": "^3.0.1"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/to-regex/node_modules/extend-shallow": {
-            "version": "3.0.2",
-            "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
-            "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
-            "dependencies": {
-                "assign-symbols": "^1.0.0",
-                "is-extendable": "^1.0.1"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/to-regex/node_modules/is-descriptor": {
-            "version": "1.0.3",
-            "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz",
-            "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==",
-            "dependencies": {
-                "is-accessor-descriptor": "^1.0.1",
-                "is-data-descriptor": "^1.0.1"
-            },
-            "engines": {
-                "node": ">= 0.4"
-            }
-        },
-        "node_modules/to-regex/node_modules/is-extendable": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
-            "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
-            "dependencies": {
-                "is-plain-object": "^2.0.4"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/toidentifier": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
@@ -30034,15 +28920,15 @@
             }
         },
         "node_modules/typescript": {
-            "version": "4.9.5",
-            "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
-            "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
+            "version": "5.3.3",
+            "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
+            "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
             "bin": {
                 "tsc": "bin/tsc",
                 "tsserver": "bin/tsserver"
             },
             "engines": {
-                "node": ">=4.2.0"
+                "node": ">=14.17"
             }
         },
         "node_modules/typescript-collections": {
@@ -30179,20 +29065,6 @@
             "resolved": "https://registry.npmjs.org/uninstall/-/uninstall-0.0.0.tgz",
             "integrity": "sha512-pjP/0+A4gsbDVa8XH/S2GZdT9NPJW8NFMy3GI7HnsWG+NAmFSSj3QidNosXBI9cPtxxNExEDdhKFO6sli8K3mA=="
         },
-        "node_modules/union-value": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
-            "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
-            "dependencies": {
-                "arr-union": "^3.1.0",
-                "get-value": "^2.0.6",
-                "is-extendable": "^0.1.1",
-                "set-value": "^2.0.1"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/unist-util-is": {
             "version": "6.0.0",
             "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz",
@@ -30303,50 +29175,6 @@
                 "node": ">= 0.8"
             }
         },
-        "node_modules/unset-value": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
-            "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==",
-            "dependencies": {
-                "has-value": "^0.3.1",
-                "isobject": "^3.0.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/unset-value/node_modules/has-value": {
-            "version": "0.3.1",
-            "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
-            "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==",
-            "dependencies": {
-                "get-value": "^2.0.3",
-                "has-values": "^0.1.4",
-                "isobject": "^2.0.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/unset-value/node_modules/has-value/node_modules/isobject": {
-            "version": "2.1.0",
-            "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
-            "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==",
-            "dependencies": {
-                "isarray": "1.0.0"
-            },
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/unset-value/node_modules/has-values": {
-            "version": "0.1.4",
-            "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
-            "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/untildify": {
             "version": "4.0.0",
             "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz",
@@ -30356,15 +29184,6 @@
                 "node": ">=8"
             }
         },
-        "node_modules/upath": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
-            "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
-            "engines": {
-                "node": ">=4",
-                "yarn": "*"
-            }
-        },
         "node_modules/update-browserslist-db": {
             "version": "1.0.13",
             "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
@@ -30402,12 +29221,6 @@
                 "punycode": "^2.1.0"
             }
         },
-        "node_modules/urix": {
-            "version": "0.1.0",
-            "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
-            "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==",
-            "deprecated": "Please see https://github.com/lydell/urix#deprecated"
-        },
         "node_modules/url": {
             "version": "0.11.3",
             "resolved": "https://registry.npmjs.org/url/-/url-0.11.3.tgz",
@@ -30500,14 +29313,6 @@
                 "url": "https://github.com/sponsors/ljharb"
             }
         },
-        "node_modules/use": {
-            "version": "3.1.1",
-            "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
-            "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/use-isomorphic-layout-effect": {
             "version": "1.1.2",
             "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz",
@@ -31472,14 +30277,6 @@
                 "node": ">=4.0.0"
             }
         },
-        "node_modules/worker-rpc": {
-            "version": "0.1.1",
-            "resolved": "https://registry.npmjs.org/worker-rpc/-/worker-rpc-0.1.1.tgz",
-            "integrity": "sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg==",
-            "dependencies": {
-                "microevent.ts": "~0.1.1"
-            }
-        },
         "node_modules/workerpool": {
             "version": "6.2.1",
             "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz",
diff --git a/package.json b/package.json
index 42b3c1ce2..e1efce5a3 100644
--- a/package.json
+++ b/package.json
@@ -89,7 +89,7 @@
         "ts-loader": "^9.5.1",
         "ts-node": "^10.9.1",
         "ts-node-dev": "^2.0.0",
-        "typescript": "^4.7.4",
+        "typescript": "^5.3.3",
         "webpack-dev-server": "^4.15.1",
         "webpack-hot-middleware": "^2.25.4"
     },
@@ -97,6 +97,7 @@
         "@azure/storage-blob": "^12.17.0",
         "@babel/preset-env": "^7.23.5",
         "@babel/preset-react": "^7.23.3",
+        "@bundled-es-modules/pdfjs-dist": "^3.6.172-alpha.1",
         "@emotion/react": "^11.11.1",
         "@emotion/styled": "^11.11.0",
         "@ffmpeg/core": "0.12.4",
@@ -121,7 +122,7 @@
         "@types/google-maps": "^3.2.6",
         "@types/reveal": "^4.2.0",
         "@types/supercluster": "^7.1.3",
-        "@types/web": "0.0.125",
+        "@types/web": "^0.0.127",
         "@webscopeio/react-textarea-autocomplete": "^4.9.2",
         "adm-zip": "^0.5.10",
         "archiver": "^6.0.1",
@@ -173,7 +174,7 @@
         "flexlayout-react": "^0.7.15",
         "fluent-ffmpeg": "^2.1.2",
         "forever-agent": "^0.6.1",
-        "fork-ts-checker-webpack-plugin": "^1.6.0",
+        "fork-ts-checker-webpack-plugin": "^9.0.2",
         "formidable": "3.5.1",
         "function-plot": "^1.23.3",
         "golden-layout": "^2.6.0",
@@ -221,7 +222,7 @@
         "path-browserify": "^1.0.1",
         "pdf-parse": "^1.1.1",
         "pdfjs": "^2.4.7",
-        "pdfjs-dist": "^2.14.305",
+        "pdfjs-dist": "^4.0.269",
         "probe-image-size": "^7.2.3",
         "process": "^0.11.10",
         "prosemirror-commands": "^1.5.2",
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index 249e2c3a3..831925054 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -204,7 +204,6 @@ export class ContextMenu extends React.Component {
     }
 
     render() {
-        console.log('DISPLAY = ' + this._display);
         return (
             <div
                 className="contextMenu-cont"
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 04fb1fe80..35c0d617b 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -1,6 +1,6 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Button, ColorPicker, EditableText, Size, Type } from 'browndash-components';
-import { action, computed, observable } from 'mobx';
+import { action, computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { FaPlus } from 'react-icons/fa';
@@ -39,6 +39,11 @@ enum DashboardGroup {
 export class DashboardView extends React.Component {
     public static _urlState: HistoryUtil.DocUrl;
 
+    constructor(props: any) {
+        super(props);
+        makeObservable(this);
+    }
+
     @observable private openModal = false;
     @observable private selectedDashboardGroup = DashboardGroup.MyDashboards;
     @observable private newDashboardName = '';
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index dae2490c9..a8f4020ee 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -56,7 +56,7 @@ 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 { LinkDocPreview, LinkInfo } from './nodes/LinkDocPreview';
 import { MapAnchorMenu } from './nodes/MapBox/MapAnchorMenu';
 import { MapBox } from './nodes/MapBox/MapBox';
 import { RadialMenu } from './nodes/RadialMenu';
@@ -968,9 +968,6 @@ export class MainView extends React.Component {
         );
     }
 
-    @computed get linkDocPreview() {
-        return LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : null;
-    }
     @observable mapBoxHackBool = false;
     @computed get mapBoxHack() {
         return this.mapBoxHackBool ? null : (
@@ -1039,7 +1036,7 @@ export class MainView extends React.Component {
                 {this._hideUI ? null : <TopBar />}
                 {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
                 {DocButtonState.Instance.LinkEditorDocView ? <LinkMenu clearLinkEditor={action(() => (DocButtonState.Instance.LinkEditorDocView = undefined))} docView={DocButtonState.Instance.LinkEditorDocView} /> : null}
-                {this.linkDocPreview}
+                {LinkInfo.Instance?.LinkInfo ? <LinkDocPreview {...LinkInfo.Instance.LinkInfo} /> : null}
 
                 {((page: string) => {
                     // prettier-ignore
diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx
index 196250167..5bde9d3c4 100644
--- a/src/client/views/PropertiesDocContextSelector.tsx
+++ b/src/client/views/PropertiesDocContextSelector.tsx
@@ -1,4 +1,4 @@
-import { computed } from 'mobx';
+import { computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast } from '../../fields/Doc';
@@ -8,6 +8,7 @@ import { DocFocusOrOpen } from '../util/DocumentManager';
 import { CollectionDockingView } from './collections/CollectionDockingView';
 import { DocumentView, OpenWhere } from './nodes/DocumentView';
 import './PropertiesDocContextSelector.scss';
+import { copyProps } from '../../Utils';
 
 type PropertiesDocContextSelectorProps = {
     DocView?: DocumentView;
@@ -18,10 +19,20 @@ type PropertiesDocContextSelectorProps = {
 
 @observer
 export class PropertiesDocContextSelector extends React.Component<PropertiesDocContextSelectorProps> {
+    _prevProps: PropertiesDocContextSelectorProps;
+    @observable _props: PropertiesDocContextSelectorProps;
+    constructor(props: PropertiesDocContextSelectorProps) {
+        super(props);
+        this._props = this._prevProps = props;
+        makeObservable(this);
+    }
+    componentDidUpdate() {
+        copyProps(this);
+    }
     @computed get _docs() {
-        if (!this.props.DocView) return [];
-        const target = this.props.DocView.props.Document;
-        const targetContext = this.props.DocView.props.docViewPath().lastElement()?.Document;
+        if (!this._props.DocView) return [];
+        const target = this._props.DocView._props.Document;
+        const targetContext = this._props.DocView._props.docViewPath().lastElement()?.Document;
         const embeddings = DocListCast(target.proto_embeddings);
         const containerProtos = embeddings.filter(embedding => embedding.embedContainer && embedding.embedContainer instanceof Doc).reduce((set, embedding) => set.add(Cast(embedding.embedContainer, Doc, null)), new Set<Doc>());
         const containerSets = Array.from(containerProtos.keys()).map(container => DocListCast(container.proto_embeddings));
@@ -40,23 +51,23 @@ export class PropertiesDocContextSelector extends React.Component<PropertiesDocC
         );
 
         return doclayouts
-            .filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance?.props.Document))
+            .filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance?.Document))
             .filter(doc => !Doc.IsSystem(doc))
             .filter(doc => doc !== targetContext)
             .map(doc => ({ col: doc, target }));
     }
 
     getOnClick = (col: Doc, target: Doc) => {
-        if (!this.props.DocView) return;
+        if (!this._props.DocView) return;
         col = Doc.IsDataProto(col) ? Doc.MakeDelegate(col) : col;
-        DocFocusOrOpen(Doc.GetProto(this.props.DocView.props.Document), undefined, col);
+        DocFocusOrOpen(Doc.GetProto(this._props.DocView.Document), undefined, col);
     };
 
     render() {
         if (this._docs.length < 1) return undefined;
         return (
             <div>
-                {this.props.hideTitle ? null : <p key="contexts">Contexts:</p>}
+                {this._props.hideTitle ? null : <p key="contexts">Contexts:</p>}
                 {this._docs.map(doc => (
                     <p key={doc.col[Id] + doc.target[Id]}>
                         <a onClick={() => this.getOnClick(doc.col, doc.target)}>{StrCast(doc.col.title)}</a>
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 2a52d86c2..19473de2b 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -1,11 +1,11 @@
-import { computed } from 'mobx';
+import { computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc, DocListCast, Field, FieldResult, StrListCast } from '../../fields/Doc';
 import { Id } from '../../fields/FieldSymbols';
 import { List } from '../../fields/List';
 import { RichTextField } from '../../fields/RichTextField';
 import { DocCast, NumCast, StrCast } from '../../fields/Types';
-import { emptyFunction, returnAll, returnFalse, returnOne, returnTrue, returnZero } from '../../Utils';
+import { copyProps, emptyFunction, returnAll, returnFalse, returnOne, returnTrue, returnZero } from '../../Utils';
 import { Docs, DocUtils } from '../documents/Documents';
 import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
 import { LinkManager } from '../util/LinkManager';
@@ -35,14 +35,21 @@ interface ExtraProps {
 }
 @observer
 export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
-    constructor(props: Readonly<FieldViewProps & ExtraProps>) {
+    _prevProps: FieldViewProps & ExtraProps;
+    @observable _props: FieldViewProps & ExtraProps;
+    constructor(props: FieldViewProps & ExtraProps) {
         super(props);
-        // this.props.dataDoc[this.sidebarKey] = new List<Doc>();  // bcz: can't do this here.  it blows away existing things and isn't a robust solution for making sure the field exists -- instead this should happen when the document is created and/or shared
+        this._props = this._prevProps = props;
+        makeObservable(this);
+        // this._props.dataDoc[this.sidebarKey] = new List<Doc>();  // bcz: can't do this here.  it blows away existing things and isn't a robust solution for making sure the field exists -- instead this should happen when the document is created and/or shared
+    }
+    componentDidUpdate() {
+        copyProps(this);
     }
     _stackRef = React.createRef<CollectionStackingView>();
     @computed get allMetadata() {
         const keys = new Map<string, FieldResult<Field>>();
-        DocListCast(this.props.Document[this.sidebarKey]).forEach(doc =>
+        DocListCast(this._props.Document[this.sidebarKey]).forEach(doc =>
             SearchUtil.documentKeys(doc)
                 .filter(key => key[0] && key[0] !== '_' && key[0] === key[0].toUpperCase())
                 .map(key => keys.set(key, doc[key]))
@@ -51,7 +58,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
     }
     @computed get allHashtags() {
         const keys = new Set<string>();
-        DocListCast(this.props.Document[this.sidebarKey]).forEach(doc => StrListCast(doc.tags).forEach(tag => keys.add(tag)));
+        DocListCast(this._props.Document[this.sidebarKey]).forEach(doc => StrListCast(doc.tags).forEach(tag => keys.add(tag)));
         return Array.from(keys.keys())
             .filter(key => key[0])
             .filter(key => !key.startsWith('_') && (key[0] === '#' || key[0] === key[0].toUpperCase()))
@@ -59,7 +66,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
     }
     @computed get allUsers() {
         const keys = new Set<string>();
-        DocListCast(this.props.Document[this.sidebarKey]).forEach(doc => keys.add(StrCast(doc.author)));
+        DocListCast(this._props.Document[this.sidebarKey]).forEach(doc => keys.add(StrCast(doc.author)));
         return Array.from(keys.keys()).sort();
     }
 
@@ -69,7 +76,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
             .join(' ');
         const target = Docs.Create.TextDocument(startup, {
             title: '-note-',
-            annotationOn: this.props.Document,
+            annotationOn: this._props.Document,
             _width: 200,
             _height: 50,
             _layout_fitWidth: true,
@@ -147,10 +154,10 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
         return target;
     };
     makeDocUnfiltered = (doc: Doc) => {
-        if (DocListCast(this.props.Document[this.sidebarKey]).find(anno => Doc.AreProtosEqual(doc.layout_unrendered ? DocCast(doc.annotationOn) : doc, anno))) {
+        if (DocListCast(this._props.Document[this.sidebarKey]).find(anno => Doc.AreProtosEqual(doc.layout_unrendered ? DocCast(doc.annotationOn) : doc, anno))) {
             if (this.childFilters()) {
                 // if any child filters exist, get rid of them
-                this.props.layoutDoc._childFilters = new List<string>();
+                this._props.layoutDoc._childFilters = new List<string>();
             }
             return true;
         }
@@ -158,27 +165,27 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
     };
 
     get sidebarKey() {
-        return this.props.fieldKey + '_sidebar';
+        return this._props.fieldKey + '_sidebar';
     }
     filtersHeight = () => 38;
     screenToLocalTransform = () =>
-        this.props
+        this._props
             .ScreenToLocalTransform()
-            .translate(Doc.NativeWidth(this.props.dataDoc), 0)
-            .scale(this.props.NativeDimScaling?.() || 1);
+            .translate(Doc.NativeWidth(this._props.dataDoc), 0)
+            .scale(this._props.NativeDimScaling?.() || 1);
     panelWidth = () =>
-        !this.props.showSidebar
+        !this._props.showSidebar
             ? 0
-            : this.props.usePanelWidth // [DocumentType.RTF, DocumentType.MAP].includes(this.props.layoutDoc.type as any)
-            ? this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1)
-            : ((NumCast(this.props.nativeWidth) - Doc.NativeWidth(this.props.dataDoc)) * this.props.PanelWidth()) / NumCast(this.props.nativeWidth);
-    panelHeight = () => this.props.PanelHeight() - this.filtersHeight();
-    addDocument = (doc: Doc | Doc[]) => this.props.sidebarAddDocument(doc, this.sidebarKey);
-    moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument(doc, targetCollection, addDocument, this.sidebarKey);
-    removeDocument = (doc: Doc | Doc[]) => this.props.removeDocument(doc, this.sidebarKey);
-    childFilters = () => StrListCast(this.props.layoutDoc._childFilters);
+            : this._props.usePanelWidth // [DocumentType.RTF, DocumentType.MAP].includes(this._props.layoutDoc.type as any)
+              ? this._props.PanelWidth() / (this._props.NativeDimScaling?.() || 1)
+              : ((NumCast(this._props.nativeWidth) - Doc.NativeWidth(this._props.dataDoc)) * this._props.PanelWidth()) / NumCast(this._props.nativeWidth);
+    panelHeight = () => this._props.PanelHeight() - this.filtersHeight();
+    addDocument = (doc: Doc | Doc[]) => this._props.sidebarAddDocument(doc, this.sidebarKey);
+    moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => this._props.moveDocument(doc, targetCollection, addDocument, this.sidebarKey);
+    removeDocument = (doc: Doc | Doc[]) => this._props.removeDocument(doc, this.sidebarKey);
+    childFilters = () => StrListCast(this._props.layoutDoc._childFilters);
     layout_showTitle = () => 'title';
-    setHeightCallback = (height: number) => this.props.setHeight?.(height + this.filtersHeight());
+    setHeightCallback = (height: number) => this._props.setHeight?.(height + this.filtersHeight());
     sortByLinkAnchorY = (a: Doc, b: Doc) => {
         const ay = LinkManager.Links(a).length && DocCast(LinkManager.Links(a)[0].link_anchor_1).y;
         const by = LinkManager.Links(b).length && DocCast(LinkManager.Links(b)[0].link_anchor_1).y;
@@ -188,7 +195,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
         const renderTag = (tag: string) => {
             const active = this.childFilters().includes(`tags${Doc.FilterSep}${tag}${Doc.FilterSep}check`);
             return (
-                <div key={tag} className={`sidebarAnnos-filterTag${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this.props.Document, 'tags', tag, 'check', true, undefined, e.shiftKey)}>
+                <div key={tag} className={`sidebarAnnos-filterTag${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this._props.Document, 'tags', tag, 'check', true, undefined, e.shiftKey)}>
                     {tag}
                 </div>
             );
@@ -196,7 +203,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
         const renderMeta = (tag: string, dflt: FieldResult<Field>) => {
             const active = this.childFilters().includes(`${tag}${Doc.FilterSep}${Doc.FilterAny}${Doc.FilterSep}exists`);
             return (
-                <div key={tag} className={`sidebarAnnos-filterTag${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this.props.Document, tag, Doc.FilterAny, 'exists', true, undefined, e.shiftKey)}>
+                <div key={tag} className={`sidebarAnnos-filterTag${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this._props.Document, tag, Doc.FilterAny, 'exists', true, undefined, e.shiftKey)}>
                     {tag}
                 </div>
             );
@@ -204,20 +211,20 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
         const renderUsers = (user: string) => {
             const active = this.childFilters().includes(`author:${user}:check`);
             return (
-                <div key={user} className={`sidebarAnnos-filterUser${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this.props.Document, 'author', user, 'check', true, undefined, e.shiftKey)}>
+                <div key={user} className={`sidebarAnnos-filterUser${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this._props.Document, 'author', user, 'check', true, undefined, e.shiftKey)}>
                     {user}
                 </div>
             );
         };
         // TODO: Calculation of the topbar is hardcoded and different for text nodes - it should all be the same and all be part of SidebarAnnos
-        return !this.props.showSidebar ? null : (
+        return !this._props.showSidebar ? null : (
             <div
                 style={{
                     position: 'absolute',
-                    pointerEvents: this.props.isContentActive() ? 'all' : undefined,
-                    top: this.props.Document.type !== DocumentType.RTF && StrCast(this.props.Document._layout_showTitle) === 'title' ? 15 : 0,
+                    pointerEvents: this._props.isContentActive() ? 'all' : undefined,
+                    top: this._props.Document.type !== DocumentType.RTF && StrCast(this._props.Document._layout_showTitle) === 'title' ? 15 : 0,
                     right: 0,
-                    background: this.props.styleProvider?.(this.props.Document, this.props, StyleProp.WidgetColor),
+                    background: this._props.styleProvider?.(this._props.Document, this._props, StyleProp.WidgetColor),
                     width: `100%`,
                     height: '100%',
                 }}>
@@ -231,7 +238,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
 
                 <div style={{ width: '100%', height: `calc(100% - 38px)`, position: 'relative' }}>
                     <CollectionStackingView
-                        {...this.props}
+                        {...this._props}
                         setContentView={emptyFunction}
                         NativeWidth={returnZero}
                         NativeHeight={returnZero}
@@ -246,14 +253,14 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
                         NativeDimScaling={returnOne}
                         //childlayout_showTitle={this.layout_showTitle}
                         isAnyChildContentActive={returnFalse}
-                        childDocumentsActive={this.props.isContentActive}
-                        whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
+                        childDocumentsActive={this._props.isContentActive}
+                        whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
                         childHideDecorationTitle={true}
                         removeDocument={this.removeDocument}
                         moveDocument={this.moveDocument}
                         addDocument={this.addDocument}
                         ScreenToLocalTransform={this.screenToLocalTransform}
-                        renderDepth={this.props.renderDepth + 1}
+                        renderDepth={this._props.renderDepth + 1}
                         type_collection={CollectionViewType.Stacking}
                         fieldKey={this.sidebarKey}
                         pointerEvents={returnAll}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 154c914ad..d16599bab 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1441,7 +1441,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         return anchor;
     };
 
-    infoUI = () => <CollectionFreeFormInfoUI Document={this.Document} Freeform={this} />;
+    infoUI = () => (this.Document.annotationOn ? null : <CollectionFreeFormInfoUI Document={this.Document} Freeform={this} />);
 
     componentDidMount() {
         this._props.setContentView?.(this);
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index af0fe73c3..bcfe2c232 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -121,7 +121,8 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
                 <EditableView
                     oneLine={this._props.oneLine}
                     allowCRs={this._props.allowCRs}
-                    contents={<FieldView {...fieldProps} />}
+                    contents={undefined}
+                    fieldContents={fieldProps}
                     editing={this.selected ? undefined : false}
                     GetValue={() => Field.toKeyValueString(this._props.Document, this._props.fieldKey)}
                     SetValue={undoable((value: string, shiftDown?: boolean, enterKey?: boolean) => {
@@ -333,7 +334,8 @@ export class SchemaBoolCell extends React.Component<SchemaTableCellProps> {
                     })}
                 />
                 <EditableView
-                    contents={<FieldView {...fieldProps} />}
+                    contents={undefined}
+                    fieldContents={fieldProps}
                     editing={this.selected ? undefined : false}
                     GetValue={() => Field.toKeyValueString(this._props.Document, this._props.fieldKey)}
                     SetValue={undoBatch((value: string, shiftDown?: boolean, enterKey?: boolean) => {
diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx
index 91d812f35..af22f41e1 100644
--- a/src/client/views/linking/LinkMenu.tsx
+++ b/src/client/views/linking/LinkMenu.tsx
@@ -3,7 +3,7 @@ import { observer } from 'mobx-react';
 import { Doc } from '../../../fields/Doc';
 import { LinkManager } from '../../util/LinkManager';
 import { DocumentView } from '../nodes/DocumentView';
-import { LinkDocPreview } from '../nodes/LinkDocPreview';
+import { LinkInfo } from '../nodes/LinkDocPreview';
 import './LinkMenu.scss';
 import { LinkMenuGroup } from './LinkMenuGroup';
 import * as React from 'react';
@@ -38,7 +38,7 @@ export class LinkMenu extends React.Component<Props> {
     }
 
     onPointerDown = action((e: PointerEvent) => {
-        LinkDocPreview.Clear();
+        LinkInfo.Clear();
         if (!this._linkMenuRef.current?.contains(e.target as any) && !this._editorRef.current?.contains(e.target as any)) {
             this.clear();
         }
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 858cc2e5e..d77525e04 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -15,7 +15,7 @@ import { SelectionManager } from '../../util/SelectionManager';
 import { SettingsManager } from '../../util/SettingsManager';
 import { undoBatch } from '../../util/UndoManager';
 import { DocumentView, DocumentViewInternal, OpenWhere } from '../nodes/DocumentView';
-import { LinkDocPreview } from '../nodes/LinkDocPreview';
+import { LinkDocPreview, LinkInfo } from '../nodes/LinkDocPreview';
 import './LinkMenuItem.scss';
 import * as React from 'react';
 
@@ -180,11 +180,11 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
                         </div>
                         <div
                             className="linkMenu-text"
-                            onPointerLeave={LinkDocPreview.Clear}
+                            onPointerLeave={LinkInfo.Clear}
                             onPointerEnter={e =>
                                 this._props.linkDoc &&
                                 this._props.clearLinkEditor &&
-                                LinkDocPreview.SetLinkInfo({
+                                LinkInfo.SetLinkInfo({
                                     docProps: this._props.docView._props,
                                     linkSrc: this._props.sourceDoc,
                                     linkDoc: this._props.linkDoc,
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index b3acb08e2..abe59feb5 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -136,7 +136,7 @@ export interface DocComponentView {
     componentUI?: (boundsLeft: number, boundsTop: number) => JSX.Element | null;
     dragStarting?: (snapToDraggedDoc: boolean, showGroupDragTarget: boolean, visited: Set<Doc>) => void;
     incrementalRendering?: () => void;
-    infoUI?: () => JSX.Element;
+    infoUI?: () => JSX.Element | null;
     getCenter?: (xf: Transform) => { X: number; Y: number };
     screenBounds?: () => Opt<{ left: number; top: number; right: number; bottom: number; center?: { X: number; Y: number } }>;
     ptToScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number };
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 1b5161e47..33c9b2e08 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -11,7 +11,7 @@ import { ViewBoxBaseComponent } from '../DocComponent';
 import { StyleProp } from '../StyleProvider';
 import { FieldView, FieldViewProps } from './FieldView';
 import './LinkAnchorBox.scss';
-import { LinkDocPreview } from './LinkDocPreview';
+import { LinkDocPreview, LinkInfo } from './LinkDocPreview';
 import * as React from 'react';
 // import globalCssVariables = require('../global/globalCssVariables.scss');
 const MEDIUM_GRAY = 'lightGray';
@@ -85,7 +85,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 title={targetTitle}
                 className={`linkAnchorBox-cont${small ? '-small' : ''}`}
                 onPointerEnter={e =>
-                    LinkDocPreview.SetLinkInfo({
+                    LinkInfo.SetLinkInfo({
                         docProps: this.props,
                         linkSrc: this.linkSource,
                         linkDoc: this.Document,
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index dd102edef..108930ad9 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -1,11 +1,11 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@mui/material';
-import { action, computed, observable } from 'mobx';
+import { action, computed, makeObservable, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import wiki from 'wikijs';
 import { Doc, Opt } from '../../../fields/Doc';
 import { Cast, DocCast, NumCast, PromiseValue, StrCast } from '../../../fields/Types';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnNone, setupMoveUpEvents } from '../../../Utils';
+import { copyProps, emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnNone, setupMoveUpEvents } from '../../../Utils';
 import { DocServer } from '../../DocServer';
 import { Docs } from '../../documents/Documents';
 import { DocumentType } from '../../documents/DocumentTypes';
@@ -20,6 +20,25 @@ import { DocumentView, DocumentViewSharedProps, OpenWhere } from './DocumentView
 import './LinkDocPreview.scss';
 import * as React from 'react';
 
+export class LinkInfo {
+    private static _instance: Opt<LinkInfo>;
+    constructor() {
+        LinkInfo._instance = this;
+        makeObservable(this);
+    }
+    @observable public LinkInfo: Opt<LinkDocPreviewProps> = undefined;
+
+    public static get Instance() {
+        return LinkInfo._instance ?? new LinkInfo();
+    }
+    public static Clear() {
+        runInAction(() => LinkInfo.Instance && (LinkInfo.Instance.LinkInfo = undefined));
+    }
+    public static SetLinkInfo(info?: LinkDocPreviewProps) {
+        runInAction(() => LinkInfo.Instance && (LinkInfo.Instance.LinkInfo = info));
+    }
+}
+
 interface LinkDocPreviewProps {
     linkDoc?: Doc;
     linkSrc?: Doc;
@@ -31,35 +50,28 @@ interface LinkDocPreviewProps {
 }
 @observer
 export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
-    @action public static Clear() {
-        LinkDocPreview.LinkInfo = undefined;
-    }
-    @action public static SetLinkInfo(info?: LinkDocPreviewProps) {
-        LinkDocPreview.LinkInfo !== info && (LinkDocPreview.LinkInfo = info);
+    _prevProps: LinkDocPreviewProps;
+    @observable _props: LinkDocPreviewProps;
+    constructor(props: LinkDocPreviewProps) {
+        super(props);
+        this._props = this._prevProps = props;
+        makeObservable(this);
     }
 
-    static _instance: Opt<LinkDocPreview>;
-
     _infoRef = React.createRef<HTMLDivElement>();
     _linkDocRef = React.createRef<HTMLDivElement>();
-    @observable public static LinkInfo: Opt<LinkDocPreviewProps>;
-    @observable _targetDoc: Opt<Doc>;
-    @observable _markerTargetDoc: Opt<Doc>;
-    @observable _linkDoc: Opt<Doc>;
-    @observable _linkSrc: Opt<Doc>;
+    @observable _targetDoc: Opt<Doc> = undefined;
+    @observable _markerTargetDoc: Opt<Doc> = undefined;
+    @observable _linkDoc: Opt<Doc> = undefined;
+    @observable _linkSrc: Opt<Doc> = undefined;
     @observable _toolTipText = '';
     @observable _hrefInd = 0;
 
-    constructor(props: any) {
-        super(props);
-        LinkDocPreview._instance = this;
-    }
-
     @action
     init() {
-        var linkTarget = this.props.linkDoc;
-        this._linkSrc = this.props.linkSrc;
-        this._linkDoc = this.props.linkDoc;
+        var linkTarget = this._props.linkDoc;
+        this._linkSrc = this._props.linkSrc;
+        this._linkDoc = this._props.linkDoc;
         const link_anchor_1 = DocCast(this._linkDoc?.link_anchor_1);
         const link_anchor_2 = DocCast(this._linkDoc?.link_anchor_2);
         if (link_anchor_1 && link_anchor_2) {
@@ -73,27 +85,27 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
         this.updateHref();
     }
     componentDidUpdate(props: any) {
-        if (props.linkSrc !== this.props.linkSrc || props.linkDoc !== this.props.linkDoc || props.hrefs !== this.props.hrefs) this.init();
+        copyProps(this);
+        if (props.linkSrc !== this._props.linkSrc || props.linkDoc !== this._props.linkDoc || props.hrefs !== this._props.hrefs) this.init();
     }
     componentDidMount() {
         this.init();
         document.addEventListener('pointerdown', this.onPointerDown, true);
     }
 
-    @action
     componentWillUnmount() {
-        LinkDocPreview.SetLinkInfo(undefined);
+        LinkInfo.Clear();
         document.removeEventListener('pointerdown', this.onPointerDown, true);
     }
 
     onPointerDown = (e: PointerEvent) => {
-        !this._linkDocRef.current?.contains(e.target as any) && LinkDocPreview.Clear(); // close preview when not clicking anywhere other than the info bar of the preview
+        !this._linkDocRef.current?.contains(e.target as any) && LinkInfo.Clear(); // close preview when not clicking anywhere other than the info bar of the preview
     };
 
     @action
     updateHref() {
-        if (this.props.hrefs?.length) {
-            const href = this.props.hrefs[this._hrefInd];
+        if (this._props.hrefs?.length) {
+            const href = this._props.hrefs[this._hrefInd];
             if (href.indexOf(Doc.localServerPath()) !== 0) {
                 // link to a web page URL -- try to show a preview
                 if (href.startsWith('https://en.wikipedia.org/wiki/')) {
@@ -123,7 +135,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                                 this._markerTargetDoc = linkTarget;
                                 this._targetDoc = /*linkTarget?.type === DocumentType.MARKER &&*/ linkTarget?.annotationOn ? Cast(linkTarget.annotationOn, Doc, null) ?? linkTarget : linkTarget;
                             }
-                            if (LinkDocPreview.LinkInfo?.noPreview || this._linkSrc?.followLinkToggle || this._markerTargetDoc?.type === DocumentType.PRES) this.followLink();
+                            if (LinkInfo.Instance?.LinkInfo?.noPreview || this._linkSrc?.followLinkToggle || this._markerTargetDoc?.type === DocumentType.PRES) this.followLink();
                         }
                     })
                 );
@@ -143,7 +155,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
             action(() => {
                 LinkManager.currentLink = this._linkDoc;
                 LinkManager.currentLinkAnchor = this._linkSrc;
-                this.props.docProps.DocumentView?.().select(false);
+                this._props.docProps.DocumentView?.().select(false);
                 if ((SettingsManager.Instance.propertiesWidth ?? 0) < 100) {
                     SettingsManager.Instance.propertiesWidth = 250;
                 }
@@ -157,7 +169,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
             returnFalse,
             emptyFunction,
             action(() => {
-                const nextHrefInd = (this._hrefInd + 1) % (this.props.hrefs?.length || 1);
+                const nextHrefInd = (this._hrefInd + 1) % (this._props.hrefs?.length || 1);
                 if (nextHrefInd !== this._hrefInd) {
                     this._linkDoc = undefined;
                     this._hrefInd = nextHrefInd;
@@ -168,19 +180,19 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
     };
 
     followLink = () => {
-        LinkDocPreview.Clear();
+        LinkInfo.Clear();
         if (this._linkDoc && this._linkSrc) {
             LinkFollower.FollowLink(this._linkDoc, this._linkSrc, false);
-        } else if (this.props.hrefs?.length) {
+        } else if (this._props.hrefs?.length) {
             const webDoc =
-                Array.from(SearchUtil.SearchCollection(Doc.MyFilesystem, this.props.hrefs[0]).keys()).lastElement() ??
-                Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, data_useCors: true });
+                Array.from(SearchUtil.SearchCollection(Doc.MyFilesystem, this._props.hrefs[0]).keys()).lastElement() ??
+                Docs.Create.WebDocument(this._props.hrefs[0], { title: this._props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, data_useCors: true });
             DocumentManager.Instance.showDocument(webDoc, {
                 openLocation: OpenWhere.lightbox,
                 willPan: true,
                 zoomTime: 500,
             });
-            //this.props.docProps?.addDocTab(webDoc, OpenWhere.lightbox);
+            //this._props.docProps?.addDocTab(webDoc, OpenWhere.lightbox);
         }
     };
 
@@ -216,7 +228,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                 </div>
                 <div className="linkDocPreview-buttonBar" style={{ float: 'right' }}>
                     <Tooltip title={<div className="dash-tooltip">Next Link</div>} placement="top">
-                        <div className="linkDocPreview-button" style={{ background: (this.props.hrefs?.length || 0) <= 1 ? 'gray' : 'green' }} onPointerDown={this.nextHref}>
+                        <div className="linkDocPreview-button" style={{ background: (this._props.hrefs?.length || 0) <= 1 ? 'gray' : 'green' }} onPointerDown={this.nextHref}>
                             <FontAwesomeIcon className="linkDocPreview-fa-icon" icon="chevron-right" color="white" size="sm" />
                         </div>
                     </Tooltip>
@@ -228,7 +240,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
     @computed get docPreview() {
         return (!this._linkDoc || !this._targetDoc || !this._linkSrc) && !this._toolTipText ? null : (
             <div className="linkDocPreview-inner">
-                {!this.props.showHeader ? null : this.previewHeader}
+                {!this._props.showHeader ? null : this.previewHeader}
                 <div
                     className="linkDocPreview-preview-wrapper"
                     onPointerDown={e =>
@@ -238,7 +250,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                             (e, down, delta) => {
                                 if (Math.abs(e.clientX - down[0]) + Math.abs(e.clientY - down[1]) > 100) {
                                     DragManager.StartDocumentDrag([this._infoRef.current!], new DragManager.DocumentDragData([this._targetDoc!]), e.pageX, e.pageY);
-                                    LinkDocPreview.Clear();
+                                    LinkInfo.Clear();
                                     return true;
                                 }
                                 return false;
@@ -256,11 +268,11 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                         <DocumentView
                             ref={r => {
                                 const targetanchor = this._linkDoc && this._linkSrc && LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc);
-                                targetanchor && this._targetDoc !== targetanchor && r?.props.focus?.(targetanchor, {});
+                                targetanchor && this._targetDoc !== targetanchor && r?._props.focus?.(targetanchor, {});
                             }}
                             Document={this._targetDoc!}
                             moveDocument={returnFalse}
-                            styleProvider={this.props.docProps?.styleProvider}
+                            styleProvider={this._props.docProps?.styleProvider}
                             docViewPath={returnEmptyDoclist}
                             ScreenToLocalTransform={Transform.Identity}
                             isDocumentActive={returnFalse}
@@ -299,7 +311,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                 className="linkDocPreview"
                 ref={this._linkDocRef}
                 onPointerDown={this.followLinkPointerDown}
-                style={{ borderColor: SettingsManager.userColor, left: this.props.location[0], top: this.props.location[1], width: this.width() + borders, height: this.height() + borders + (this.props.showHeader ? 37 : 0) }}>
+                style={{ borderColor: SettingsManager.userColor, left: this._props.location[0], top: this._props.location[1], width: this.width() + borders, height: this.height() + borders + (this._props.showHeader ? 37 : 0) }}>
                 {this.docPreview}
             </div>
         );
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 2a884cef8..a453210eb 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -611,8 +611,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     static pdfpromise = new Map<string, Promise<Pdfjs.PDFDocumentProxy>>();
     render() {
         TraceMobx();
-        const pdfView = this.renderPdfView;
-
+        const pdfView = !this._pdf ? null : this.renderPdfView;
         const href = this.pdfUrl?.url.href;
         if (!pdfView && href) {
             if (PDFBox.pdfcache.get(href)) setTimeout(action(() => (this._pdf = PDFBox.pdfcache.get(href))));
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index c722399c1..d74af9b27 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1,6 +1,7 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, trace } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, override, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import * as WebRequest from 'web-request';
 import { Doc, DocListCast, Field, Opt } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
@@ -12,7 +13,7 @@ import { listSpec } from '../../../fields/Schema';
 import { Cast, NumCast, StrCast, WebCast } from '../../../fields/Types';
 import { ImageField, WebField } from '../../../fields/URLField';
 import { TraceMobx } from '../../../fields/util';
-import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, getWordAtPoint, lightOrDark, returnFalse, returnOne, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, copyProps, emptyFunction, getWordAtPoint, lightOrDark, returnFalse, returnOne, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentManager } from '../../util/DocumentManager';
 import { ScriptingGlobals } from '../../util/ScriptingGlobals';
@@ -33,10 +34,9 @@ import { SidebarAnnos } from '../SidebarAnnos';
 import { StyleProp } from '../StyleProvider';
 import { DocComponentView, DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere } from './DocumentView';
 import { FieldView, FieldViewProps } from './FieldView';
-import { LinkDocPreview } from './LinkDocPreview';
+import { LinkInfo } from './LinkDocPreview';
 import { PinProps, PresBox } from './trails';
 import './WebBox.scss';
-import * as React from 'react';
 const { CreateImage } = require('./WebBoxRenderer');
 const _global = (window /* browser */ || global) /* node */ as any;
 const htmlToText = require('html-to-text');
@@ -95,12 +95,20 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return this.allAnnotations.filter(a => a.text_inlineAnnotations);
     }
     @computed get webField() {
-        return Cast(this.Document[this.props.fieldKey], WebField)?.url;
+        return Cast(this.Document[this._props.fieldKey], WebField)?.url;
     }
 
-    constructor(props: any) {
+    _prevProps: ViewBoxAnnotatableProps & FieldViewProps;
+    @override _props: ViewBoxAnnotatableProps & FieldViewProps;
+    constructor(props: ViewBoxAnnotatableProps & FieldViewProps) {
         super(props);
-        runInAction(() => (this._webUrl = this._url)); // setting the weburl will change the src parameter of the embedded iframe and force a navigation to it.
+        this._props = this._prevProps = props;
+        makeObservable(this);
+        this._webUrl = this._url; // setting the weburl will change the src parameter of the embedded iframe and force a navigation to it.
+    }
+
+    componentDidUpdate() {
+        copyProps(this);
     }
 
     @action
@@ -139,7 +147,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         if (!this._iframe) return;
         const scrollTop = NumCast(this.layoutDoc._layout_scrollTop);
         const nativeWidth = NumCast(this.layoutDoc.nativeWidth);
-        const nativeHeight = (nativeWidth * this.props.PanelHeight()) / this.props.PanelWidth();
+        const nativeHeight = (nativeWidth * this._props.PanelHeight()) / this._props.PanelWidth();
         var htmlString = this._iframe.contentDocument && new XMLSerializer().serializeToString(this._iframe.contentDocument);
         if (!htmlString) {
             htmlString = await (await fetch(Utils.CorsProxy(this.webField!.href))).text();
@@ -170,8 +178,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             });
     };
 
-    async componentDidMount() {
-        this.props.setContentView?.(this); // this tells the DocumentView that this WebBox is the "content" of the document.  this allows the DocumentView to call WebBox relevant methods to configure the UI (eg, show back/forward buttons)
+    componentDidMount() {
+        this._props.setContentView?.(this); // this tells the DocumentView that this WebBox is the "content" of the document.  this allows the DocumentView to call WebBox relevant methods to configure the UI (eg, show back/forward buttons)
 
         runInAction(() => {
             this._annotationKeySuffix = () => (this._urlHash ? this._urlHash + '_' : '') + 'annotations';
@@ -200,8 +208,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             () => this.layoutDoc._layout_autoHeight,
             layout_autoHeight => {
                 if (layout_autoHeight) {
-                    this.layoutDoc._nativeHeight = NumCast(this.props.Document[this.props.fieldKey + '_nativeHeight']);
-                    this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + '_nativeHeight']) * (this.props.NativeDimScaling?.() || 1));
+                    this.layoutDoc._nativeHeight = NumCast(this.Document[this._props.fieldKey + '_nativeHeight']);
+                    this._props.setHeight?.(NumCast(this.Document[this._props.fieldKey + '_nativeHeight']) * (this._props.NativeDimScaling?.() || 1));
                 }
             }
         );
@@ -218,10 +226,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 }
             } // else it's an HTMLfield
         } else if (this.webField && !this.dataDoc.text) {
-            const result = await WebRequest.get(Utils.CorsProxy(this.webField.href));
-            if (result) {
-                this.dataDoc.text = htmlToText.fromString(result.content);
-            }
+            WebRequest.get(Utils.CorsProxy(this.webField.href)) //
+                .then(result => result && (this.dataDoc.text = htmlToText.convert(result.content)));
         }
 
         this._disposers.scrollReaction = reaction(
@@ -236,7 +242,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             { fireImmediately: true }
         );
     }
-    @action componentWillUnmount() {
+    componentWillUnmount() {
         this._iframetimeout && clearTimeout(this._iframetimeout);
         this._iframetimeout = undefined;
         Object.values(this._disposers).forEach(disposer => disposer?.());
@@ -251,7 +257,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @action
     createTextAnnotation = (sel: Selection, selRange: Range | undefined) => {
         if (this._mainCont.current && selRange) {
-            if (this.dataDoc[this.props.fieldKey] instanceof HtmlField) this._mainCont.current.style.transform = `rotate(${NumCast(this.props.DocumentView!().screenToLocalTransform().RotateDeg)}deg)`;
+            if (this.dataDoc[this._props.fieldKey] instanceof HtmlField) this._mainCont.current.style.transform = `rotate(${NumCast(this._props.DocumentView!().screenToLocalTransform().RotateDeg)}deg)`;
             const clientRects = selRange.getClientRects();
             for (let i = 0; i < clientRects.length; i++) {
                 const rect = clientRects.item(i);
@@ -259,7 +265,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 if (rect && rect.width !== this._mainCont.current.clientWidth) {
                     const annoBox = document.createElement('div');
                     annoBox.className = 'marqueeAnnotator-annotationBox';
-                    const scale = this._url ? 1 : this.props.ScreenToLocalTransform().Scale;
+                    const scale = this._url ? 1 : this._props.ScreenToLocalTransform().Scale;
                     // transforms the positions from screen onto the pdf div
                     annoBox.style.top = ((rect.top - mainrect.translateY) * scale + (this._url ? this._mainCont.current.scrollTop : NumCast(this.layoutDoc.layout_scrollTop))).toString();
                     annoBox.style.left = ((rect.left - mainrect.translateX) * scale).toString();
@@ -282,7 +288,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     focus = (anchor: Doc, options: DocFocusOptions) => {
         if (anchor !== this.Document && this._outerRef.current) {
-            const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
+            const windowHeight = this._props.PanelHeight() / (this._props.NativeDimScaling?.() || 1);
             const scrollTo = Utils.scrollIntoView(NumCast(anchor.y), NumCast(anchor._height), NumCast(this.layoutDoc._layout_scrollTop), windowHeight, windowHeight * 0.1, Math.max(NumCast(anchor.y) + NumCast(anchor._height), this._scrollHeight));
             if (scrollTo !== undefined) {
                 if (this._initialScroll === undefined) {
@@ -298,8 +304,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     @action
     getView = (doc: Doc) => {
-        if (Doc.AreProtosEqual(doc, this.Document)) return new Promise<Opt<DocumentView>>(res => res(this.props.DocumentView?.()));
-        if (this.Document.layout_fieldKey === 'layout_icon') this.props.DocumentView?.().iconify();
+        if (Doc.AreProtosEqual(doc, this.Document)) return new Promise<Opt<DocumentView>>(res => res(this._props.DocumentView?.()));
+        if (this.Document.layout_fieldKey === 'layout_icon') this._props.DocumentView?.().iconify();
         const webUrl = WebCast(doc.config_data)?.url;
         if (this._url && webUrl && webUrl.href !== this._url) this.setData(webUrl.href);
         if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) this.toggleSidebar(false);
@@ -307,11 +313,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
 
     sidebarAddDocTab = (doc: Doc, where: OpenWhere) => {
-        if (DocListCast(this.props.Document[this.props.fieldKey + '_sidebar']).includes(doc) && !this.SidebarShown) {
+        if (DocListCast(this.Document[this._props.fieldKey + '_sidebar']).includes(doc) && !this.SidebarShown) {
             this.toggleSidebar(false);
             return true;
         }
-        return this.props.addDocTab(doc, where);
+        return this._props.addDocTab(doc, where);
     };
     getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
         let ele: Opt<HTMLDivElement> = undefined;
@@ -344,10 +350,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     iframeUp = (e: PointerEvent) => {
         this._getAnchor = AnchorMenu.Instance?.GetAnchor; // need to save AnchorMenu's getAnchor since a subsequent selection on another doc will overwrite this value
         this._textAnnotationCreator = undefined;
-        this.props.docViewPath().lastElement()?.docView?.cleanupPointerEvents(); // pointerup events aren't generated on containing document view, so we have to invoke it here.
+        this._props.docViewPath().lastElement()?.docView?.cleanupPointerEvents(); // pointerup events aren't generated on containing document view, so we have to invoke it here.
         if (this._iframe?.contentWindow && this._iframe.contentDocument && !this._iframe.contentWindow.getSelection()?.isCollapsed) {
             const mainContBounds = Utils.GetScreenTransform(this._mainCont.current!);
-            const scale = (this.props.NativeDimScaling?.() || 1) * mainContBounds.scale;
+            const scale = (this._props.NativeDimScaling?.() || 1) * mainContBounds.scale;
             const sel = this._iframe.contentWindow.getSelection();
             if (sel) {
                 this._selectionText = sel.toString();
@@ -355,7 +361,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 this._textAnnotationCreator = () => this.createTextAnnotation(sel, !sel.isCollapsed ? sel.getRangeAt(0) : undefined);
                 AnchorMenu.Instance.jumpTo(e.clientX * scale + mainContBounds.translateX, e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._layout_scrollTop) * scale);
                 // Changing which document to add the annotation to (the currently selected WebBox)
-                GPTPopup.Instance.setSidebarId(`${this.props.fieldKey}_${this._urlHash ? this._urlHash + '_' : ''}sidebar`);
+                GPTPopup.Instance.setSidebarId(`${this._props.fieldKey}_${this._urlHash ? this._urlHash + '_' : ''}sidebar`);
                 GPTPopup.Instance.addDoc = this.sidebarAddDocument;
             }
         }
@@ -393,23 +399,23 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             this._textAnnotationCreator = () => this.createTextAnnotation(sel, selRange);
             (!sel.isCollapsed || this.marqueeing) && AnchorMenu.Instance.jumpTo(e.clientX, e.clientY);
             // Changing which document to add the annotation to (the currently selected WebBox)
-            GPTPopup.Instance.setSidebarId(`${this.props.fieldKey}_${this._urlHash ? this._urlHash + '_' : ''}sidebar`);
+            GPTPopup.Instance.setSidebarId(`${this._props.fieldKey}_${this._urlHash ? this._urlHash + '_' : ''}sidebar`);
             GPTPopup.Instance.addDoc = this.sidebarAddDocument;
         }
     };
     @action
     iframeDown = (e: PointerEvent) => {
-        this.props.select(false);
+        this._props.select(false);
         const locpt = { 
-            x: (e.clientX / NumCast(this.Document.nativeWidth)) * this.props.PanelWidth(), 
-            y: ((e.clientY - NumCast(this.layoutDoc.layout_scrollTop))/ NumCast(this.Document.nativeHeight)) * this.props.PanelHeight() }; // prettier-ignore
-        const scrclick = this.props.DocumentView?.()._props.ScreenToLocalTransform().inverse().transformPoint(locpt.x, locpt.y)!;
-        const scrcent = this.props
+            x: (e.clientX / NumCast(this.Document.nativeWidth)) * this._props.PanelWidth(), 
+            y: ((e.clientY - NumCast(this.layoutDoc.layout_scrollTop))/ NumCast(this.Document.nativeHeight)) * this._props.PanelHeight() }; // prettier-ignore
+        const scrclick = this._props.DocumentView?.()._props.ScreenToLocalTransform().inverse().transformPoint(locpt.x, locpt.y)!;
+        const scrcent = this._props
             .DocumentView?.()
-            .props.ScreenToLocalTransform()
+            ._props.ScreenToLocalTransform()
             .inverse()
             .transformPoint(NumCast(this.Document.width) / 2, NumCast(this.Document.height) / 2)!;
-        const theclickoff = Utils.rotPt(scrclick[0] - scrcent[0], scrclick[1] - scrcent[1], -this.props.ScreenToLocalTransform().Rotate);
+        const theclickoff = Utils.rotPt(scrclick[0] - scrcent[0], scrclick[1] - scrcent[1], -this._props.ScreenToLocalTransform().Rotate);
         const theclick = [theclickoff.x + scrcent[0], theclickoff.y + scrcent[1]];
         MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
         const word = getWordAtPoint(e.target, e.clientX, e.clientY);
@@ -489,7 +495,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 this._scrollHeight = Math.max(this._scrollHeight, iframeContent.body.scrollHeight || 0);
                 if (this._scrollHeight) {
                     this.Document.nativeHeight = Math.min(NumCast(this.Document.nativeHeight), this._scrollHeight);
-                    this.layoutDoc.height = Math.min(NumCast(this.layoutDoc._height), (NumCast(this.layoutDoc._width) * NumCast(this.dataDoc.nativeHeight)) / NumCast(this.dataDoc.nativeWidth));
+                    this.layoutDoc.height = Math.min(NumCast(this.layoutDoc._height), (NumCast(this.layoutDoc._width) * NumCast(this.Document.nativeHeight)) / NumCast(this.Document.nativeWidth));
                 }
             };
             const swidth = Math.max(NumCast(this.layoutDoc.nativeWidth), iframeContent.body.scrollWidth || 0);
@@ -564,7 +570,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             clearStyleSheetRules(WebBox.webStyleSheet);
             this._scrollTimer = undefined;
             const newScrollTop = scrollTop > iframeHeight ? iframeHeight : scrollTop;
-            if (!LinkDocPreview.LinkInfo && this._outerRef.current && newScrollTop !== this.layoutDoc.thumbScrollTop && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()))) {
+            if (!LinkInfo.Instance?.LinkInfo && this._outerRef.current && newScrollTop !== this.layoutDoc.thumbScrollTop && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this._props.docViewPath()))) {
                 this.layoutDoc.thumb = undefined;
                 this.layoutDoc.thumbScrollTop = undefined;
                 this.layoutDoc.thumbNativeWidth = undefined;
@@ -709,7 +715,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             funcs.push({
                 description: (!this.layoutDoc.layout_reflowHorizontal ? 'Force' : 'Prevent') + ' Reflow',
                 event: () => {
-                    const nw = !this.layoutDoc.layout_reflowHorizontal ? undefined : Doc.NativeWidth(this.layoutDoc) - this.sidebarWidth() / (this.props.NativeDimScaling?.() || 1);
+                    const nw = !this.layoutDoc.layout_reflowHorizontal ? undefined : Doc.NativeWidth(this.layoutDoc) - this.sidebarWidth() / (this._props.NativeDimScaling?.() || 1);
                     this.layoutDoc.layout_reflowHorizontal = !nw;
                     if (nw) {
                         Doc.SetInPlace(this.layoutDoc, this.fieldKey + '_nativeWidth', nw, true);
@@ -734,7 +740,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         if (sel?.empty) sel.empty(); // Chrome
         else if (sel?.removeAllRanges) sel.removeAllRanges(); // Firefox
         this.marqueeing = [e.clientX, e.clientY];
-        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
+        if (!e.altKey && e.button === 0 && this._props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
             setupMoveUpEvents(
                 this,
                 e,
@@ -770,7 +776,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 setTimeout(() => {
                     // if menu comes up right away, the down event can still be active causing a menu item to be selected
                     this.specificContextMenu(undefined as any);
-                    this.props.docViewPath().lastElement().docView?.onContextMenu(undefined, x, y);
+                    this._props.docViewPath().lastElement().docView?.onContextMenu(undefined, x, y);
                 });
             }
         }
@@ -779,7 +785,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @observable lighttext = false;
 
     @computed get urlContent() {
-        if (this.props.ScreenToLocalTransform().Scale > 25) return <div></div>;
+        if (this._props.ScreenToLocalTransform().Scale > 25) return <div></div>;
         setTimeout(
             action(() => {
                 if (this._initialScroll === undefined && !this._webPageHasBeenRendered) {
@@ -788,7 +794,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 this._webPageHasBeenRendered = true;
             })
         );
-        const field = this.dataDoc[this.props.fieldKey];
+        const field = this.dataDoc[this._props.fieldKey];
         if (field instanceof HtmlField) {
             return (
                 <span
@@ -845,14 +851,14 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             e,
             action((e, down, delta) => {
                 this._draggingSidebar = true;
-                const localDelta = this.props
+                const localDelta = this._props
                     .ScreenToLocalTransform()
-                    .scale(this.props.NativeDimScaling?.() || 1)
+                    .scale(this._props.NativeDimScaling?.() || 1)
                     .transformDirection(delta[0], delta[1]);
                 const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']);
                 const nativeHeight = NumCast(this.layoutDoc[this.fieldKey + '_nativeHeight']);
                 const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
-                const ratio = (curNativeWidth + ((onButton ? 1 : -1) * localDelta[0]) / (this.props.NativeDimScaling?.() || 1)) / nativeWidth;
+                const ratio = (curNativeWidth + ((onButton ? 1 : -1) * localDelta[0]) / (this._props.NativeDimScaling?.() || 1)) / nativeWidth;
                 if (ratio >= 1) {
                     this.layoutDoc.nativeWidth = nativeWidth * ratio;
                     this.layoutDoc.nativeHeight = nativeHeight * (1 + ratio);
@@ -916,7 +922,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     });
     @action
     onZoomWheel = (e: React.WheelEvent) => {
-        if (this.props.isContentActive(true)) {
+        if (this._props.isContentActive(true)) {
             e.stopPropagation();
         }
     };
@@ -924,14 +930,14 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         if (!this.SidebarShown) return 0;
         if (this._previewWidth) return WebBox.sidebarResizerWidth + WebBox.openSidebarWidth; // return default sidebar if previewing (as in viewing a link target)
         const nativeDiff = NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc);
-        return WebBox.sidebarResizerWidth + nativeDiff * (this.props.NativeDimScaling?.() || 1);
+        return WebBox.sidebarResizerWidth + nativeDiff * (this._props.NativeDimScaling?.() || 1);
     };
     _innerCollectionView: CollectionFreeFormView | undefined;
     zoomScaling = () => this._innerCollectionView?.zoomScaling() ?? 1;
     setInnerContent = (component: DocComponentView) => (this._innerCollectionView = component as CollectionFreeFormView);
 
     @computed get content() {
-        const interactive = this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && Doc.ActiveTool === InkTool.None;
+        const interactive = this._props.isContentActive() && this._props.pointerEvents?.() !== 'none' && Doc.ActiveTool === InkTool.None;
         return (
             <div
                 className={'webBox-cont' + (interactive ? '-interactive' : '')}
@@ -959,7 +965,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 {this.inlineTextAnnotations
                     .sort((a, b) => NumCast(a.y) - NumCast(b.y))
                     .map(anno => (
-                        <Annotation {...this.props} fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} dataDoc={this.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />
+                        <Annotation {...this._props} fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} dataDoc={this.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />
                     ))}
             </div>
         );
@@ -969,13 +975,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     }
     renderAnnotations = (childFilters: () => string[]) => (
         <CollectionFreeFormView
-            {...this.props}
+            {...this._props}
             setContentView={this.setInnerContent}
             NativeWidth={returnZero}
             NativeHeight={returnZero}
             originTopLeft={false}
             isAnnotationOverlayScrollable={true}
-            renderDepth={this.props.renderDepth + 1}
+            renderDepth={this._props.renderDepth + 1}
             isAnnotationOverlay={true}
             fieldKey={this.annotationKey}
             setPreviewCursor={this.setPreviewCursor}
@@ -1004,11 +1010,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @computed get renderTransparentAnnotations() {
         return this.renderAnnotations(this.transparentFilter);
     }
-    childPointerEvents = () => (this.props.isContentActive() ? 'all' : undefined);
+    childPointerEvents = () => (this._props.isContentActive() ? 'all' : undefined);
     @computed get webpage() {
         const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
-        const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any);
-        const scale = previewScale * (this.props.NativeDimScaling?.() || 1);
+        const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this._props.pointerEvents?.() as any);
+        const scale = previewScale * (this._props.NativeDimScaling?.() || 1);
         return (
             <div
                 className="webBox-outerContent"
@@ -1021,7 +1027,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 onWheel={this.onZoomWheel}
                 onScroll={e => this.setDashScrollTop(this._outerRef.current?.scrollTop || 0)}
                 onPointerDown={this.onMarqueeDown}>
-                <div className="webBox-innerContent" style={{ height: (this._webPageHasBeenRendered && this._scrollHeight > this.props.PanelHeight() && this._scrollHeight) || '100%', pointerEvents }}>
+                <div className="webBox-innerContent" style={{ height: (this._webPageHasBeenRendered && this._scrollHeight > this._props.PanelHeight() && this._scrollHeight) || '100%', pointerEvents }}>
                     {this.content}
                     <div style={{ display: SnappingManager.GetCanEmbed() ? 'none' : undefined, mixBlendMode: 'multiply' }}>{this.renderTransparentAnnotations}</div>
                     {this.renderOpaqueAnnotations}
@@ -1033,7 +1039,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     @computed get searchUI() {
         return (
-            <div className="webBox-ui" onPointerDown={e => e.stopPropagation()} style={{ display: this.props.isContentActive() ? 'flex' : 'none' }}>
+            <div className="webBox-ui" onPointerDown={e => e.stopPropagation()} style={{ display: this._props.isContentActive() ? 'flex' : 'none' }}>
                 <div className="webBox-overlayCont" onPointerDown={e => e.stopPropagation()} style={{ left: `${this._searching ? 0 : 100}%` }}>
                     <button className="webBox-overlayButton" title={'search'} />
                     <input
@@ -1067,27 +1073,27 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     }
     searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => (this._searchString = e.currentTarget.value);
     setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void) => (this._setPreviewCursor = func);
-    panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth() + WebBox.sidebarResizerWidth;
-    panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
-    scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop));
+    panelWidth = () => this._props.PanelWidth() / (this._props.NativeDimScaling?.() || 1) - this.sidebarWidth() + WebBox.sidebarResizerWidth;
+    panelHeight = () => this._props.PanelHeight() / (this._props.NativeDimScaling?.() || 1);
+    scrollXf = () => this._props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop));
     anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
-    transparentFilter = () => [...this.props.childFilters(), Utils.TransparentBackgroundFilter];
-    opaqueFilter = () => [...this.props.childFilters(), Utils.noDragDocsFilter, ...(SnappingManager.GetCanEmbed() ? [] : [Utils.OpaqueBackgroundFilter])];
+    transparentFilter = () => [...this._props.childFilters(), Utils.TransparentBackgroundFilter];
+    opaqueFilter = () => [...this._props.childFilters(), Utils.noDragDocsFilter, ...(SnappingManager.GetCanEmbed() ? [] : [Utils.OpaqueBackgroundFilter])];
     childStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => {
         if (doc instanceof Doc && property === StyleProp.PointerEvents) {
             if (this.inlineTextAnnotations.includes(doc)) return 'none';
         }
-        return this.props.styleProvider?.(doc, props, property);
+        return this._props.styleProvider?.(doc, props, property);
     };
     pointerEvents = () =>
-        !this._draggingSidebar && this.props.isContentActive() && !MarqueeOptionsMenu.Instance?.isShown()
+        !this._draggingSidebar && this._props.isContentActive() && !MarqueeOptionsMenu.Instance?.isShown()
             ? 'all' //
             : 'none';
-    annotationPointerEvents = () => (this.props.isContentActive() && (SnappingManager.GetIsDragging() || Doc.ActiveTool !== InkTool.None) ? 'all' : 'none');
+    annotationPointerEvents = () => (this._props.isContentActive() && (SnappingManager.GetIsDragging() || Doc.ActiveTool !== InkTool.None) ? 'all' : 'none');
     render() {
         const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
-        const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any);
-        const scale = previewScale * (this.props.NativeDimScaling?.() || 1);
+        const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this._props.pointerEvents?.() as any);
+        const scale = previewScale * (this._props.NativeDimScaling?.() || 1);
         return (
             <div
                 className="webBox"
@@ -1096,7 +1102,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     pointerEvents: this.pointerEvents(), //
                     position: SnappingManager.GetIsDragging() ? 'absolute' : undefined,
                 }}>
-                <div className="webBox-background" style={{ backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor) }} />
+                <div className="webBox-background" style={{ backgroundColor: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor) }} />
                 <div
                     className="webBox-container"
                     style={{
@@ -1115,9 +1121,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                             anchorMenuClick={this.anchorMenuClick}
                             scrollTop={NumCast(this.layoutDoc.layout_scrollTop)}
                             annotationLayerScrollTop={0}
-                            scaling={this.props.NativeDimScaling}
+                            scaling={this._props.NativeDimScaling}
                             addDocument={this.addDocumentWrapper}
-                            docView={this.props.DocumentView!}
+                            docView={this._props.DocumentView!}
                             finishMarquee={this.finishMarquee}
                             savedAnnotations={this.savedAnnotationsCreator}
                             selectionText={this.selectionText}
@@ -1135,25 +1141,25 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     }}
                     onPointerDown={e => this.sidebarBtnDown(e, false)}
                 />
-                <div style={{ position: 'absolute', height: '100%', right: 0, top: 0, width: `calc(100 * ${this.sidebarWidth() / this.props.PanelWidth()}%` }}>
+                <div style={{ position: 'absolute', height: '100%', right: 0, top: 0, width: `calc(100 * ${this.sidebarWidth() / this._props.PanelWidth()}%` }}>
                     <SidebarAnnos
                         ref={this._sidebarRef}
-                        {...this.props}
+                        {...this._props}
                         whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
                         fieldKey={this.fieldKey + '_' + this._urlHash}
                         Document={this.Document}
                         layoutDoc={this.layoutDoc}
                         dataDoc={this.dataDoc}
                         setHeight={emptyFunction}
-                        nativeWidth={this._previewNativeWidth ?? NumCast(this.layoutDoc._nativeWidth) - WebBox.sidebarResizerWidth / (this.props.NativeDimScaling?.() || 1)}
+                        nativeWidth={this._previewNativeWidth ?? NumCast(this.layoutDoc._nativeWidth) - WebBox.sidebarResizerWidth / (this._props.NativeDimScaling?.() || 1)}
                         showSidebar={this.SidebarShown}
                         sidebarAddDocument={this.sidebarAddDocument}
                         moveDocument={this.moveDocument}
                         removeDocument={this.removeDocument}
                     />
                 </div>
-                {!this.props.isContentActive() || SnappingManager.GetIsDragging() ? null : this.sidebarHandle}
-                {!this.props.isContentActive() || SnappingManager.GetIsDragging() ? null : this.searchUI}
+                {!this._props.isContentActive() || SnappingManager.GetIsDragging() ? null : this.sidebarHandle}
+                {!this._props.isContentActive() || SnappingManager.GetIsDragging() ? null : this.searchUI}
             </div>
         );
     }
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 19e14d5a7..e2cceb906 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -1,6 +1,6 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@mui/material';
-import { action, computed, IReactionDisposer, observable } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as ReactDOM from 'react-dom/client';
 import { Doc } from '../../../../fields/Doc';
@@ -8,7 +8,7 @@ import { List } from '../../../../fields/List';
 import { listSpec } from '../../../../fields/Schema';
 import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
 import { Cast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents } from '../../../../Utils';
+import { copyProps, emptyFunction, returnFalse, returnZero, setupMoveUpEvents } from '../../../../Utils';
 import { DocServer } from '../../../DocServer';
 import { CollectionViewType } from '../../../documents/DocumentTypes';
 import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
@@ -70,10 +70,10 @@ export class DashFieldView {
             } catch {}
         });
     }
-    @action deselectNode() {
+    deselectNode() {
         this.dom.classList.remove('ProseMirror-selectednode');
     }
-    @action selectNode() {
+    selectNode() {
         this.dom.classList.add('ProseMirror-selectednode');
     }
 }
@@ -100,17 +100,24 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
     @observable _dashDoc: Doc | undefined = undefined;
     @observable _expanded = false;
 
+    _prevProps: IDashFieldViewInternal;
+    @observable _props: IDashFieldViewInternal;
     constructor(props: IDashFieldViewInternal) {
         super(props);
-        this._fieldKey = this.props.fieldKey;
-        this._textBoxDoc = this.props.tbox.Document;
+        this._props = this._prevProps = props;
+        makeObservable(this);
+        this._fieldKey = this._props.fieldKey;
+        this._textBoxDoc = this._props.tbox.Document;
 
-        if (this.props.docId) {
-            DocServer.GetRefField(this.props.docId).then(action(dashDoc => dashDoc instanceof Doc && (this._dashDoc = dashDoc)));
+        if (this._props.docId) {
+            DocServer.GetRefField(this._props.docId).then(action(dashDoc => dashDoc instanceof Doc && (this._dashDoc = dashDoc)));
         } else {
-            this._dashDoc = this.props.tbox.Document;
+            this._dashDoc = this._props.tbox.Document;
         }
     }
+    componentDidUpdate(prevProps: Readonly<IDashFieldViewInternal>, prevState: Readonly<{}>, snapshot?: any): void {
+        copyProps(this);
+    }
     componentWillUnmount() {
         this._reactionDisposer?.();
     }
@@ -119,18 +126,18 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
     // set the display of the field's value (checkbox for booleans, span of text for strings)
     @computed get fieldValueContent() {
         return !this._dashDoc ? null : (
-            <div onClick={action(e => (this._expanded = !this.props.editable ? !this._expanded : true))} style={{ fontSize: 'smaller', width: this.props.hideKey ? this.props.tbox.props.PanelWidth() - 20 : undefined }}>
+            <div onClick={action(e => (this._expanded = !this._props.editable ? !this._expanded : true))} style={{ fontSize: 'smaller', width: this._props.hideKey ? this._props.tbox._props.PanelWidth() - 20 : undefined }}>
                 <SchemaTableCell
                     Document={this._dashDoc}
                     col={0}
                     deselectCell={emptyFunction}
                     selectCell={emptyFunction}
-                    maxWidth={this.props.hideKey ? undefined : this.return100}
-                    columnWidth={this.props.hideKey ? () => this.props.tbox._props.PanelWidth() - 20 : returnZero}
+                    maxWidth={this._props.hideKey ? undefined : this.return100}
+                    columnWidth={this._props.hideKey ? () => this._props.tbox._props.PanelWidth() - 20 : returnZero}
                     selectedCell={() => [this._dashDoc!, 0]}
                     fieldKey={this._fieldKey}
                     rowHeight={returnZero}
-                    isRowActive={() => this._expanded && this.props.editable}
+                    isRowActive={() => this._expanded && this._props.editable}
                     padding={0}
                     getFinfo={emptyFunction}
                     setColumnValues={returnFalse}
@@ -145,7 +152,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
     }
 
     createPivotForField = (e: React.MouseEvent) => {
-        let container = this.props.tbox._props.DocumentView?.()._props.docViewPath().lastElement();
+        let container = this._props.tbox._props.DocumentView?.()._props.docViewPath().lastElement();
         if (container) {
             const embedding = Doc.MakeEmbedding(container.Document);
             embedding._type_collection = CollectionViewType.Time;
@@ -157,7 +164,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
             list.map(c => c.heading).indexOf(this._fieldKey) === -1 && list.push(new SchemaHeaderField(this._fieldKey, '#f1efeb'));
             list.map(c => c.heading).indexOf('text') === -1 && list.push(new SchemaHeaderField('text', '#f1efeb'));
             embedding._pivotField = this._fieldKey.startsWith('#') ? 'tags' : this._fieldKey;
-            this.props.tbox._props.addDocTab(embedding, OpenWhere.addRight);
+            this._props.tbox._props.addDocTab(embedding, OpenWhere.addRight);
         }
     };
 
@@ -175,17 +182,17 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
             <div
                 className="dashFieldView"
                 style={{
-                    width: this.props.width,
-                    height: this.props.height,
-                    pointerEvents: this.props.tbox._props.isSelected() || this.props.tbox.isAnyChildContentActive?.() ? undefined : 'none',
+                    width: this._props.width,
+                    height: this._props.height,
+                    pointerEvents: this._props.tbox._props.isSelected() || this._props.tbox.isAnyChildContentActive?.() ? undefined : 'none',
                 }}>
-                {this.props.hideKey ? null : (
+                {this._props.hideKey ? null : (
                     <span className="dashFieldView-labelSpan" title="click to see related tags" onPointerDown={this.onPointerDownLabelSpan}>
                         {this._fieldKey}
                     </span>
                 )}
 
-                {this.props.fieldKey.startsWith('#') ? null : this.fieldValueContent}
+                {this._props.fieldKey.startsWith('#') ? null : this.fieldValueContent}
             </div>
         );
     }
@@ -198,7 +205,7 @@ export class DashFieldViewMenu extends AntimodeMenu<AntimodeMenuProps> {
         super(props);
         DashFieldViewMenu.Instance = this;
     }
-    @action
+
     showFields = (e: React.MouseEvent) => {
         DashFieldViewMenu.createFieldView(e);
         DashFieldViewMenu.Instance.fadeOut(true);
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 244de7849..6b1df4560 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -55,7 +55,7 @@ import { StyleProp } from '../../StyleProvider';
 import { media_state } from '../AudioBox';
 import { DocFocusOptions, DocumentView, DocumentViewInternal, OpenWhere } from '../DocumentView';
 import { FieldView, FieldViewProps } from '../FieldView';
-import { LinkDocPreview } from '../LinkDocPreview';
+import { LinkDocPreview, LinkInfo } from '../LinkDocPreview';
 import { PinProps, PresBox } from '../trails';
 import { DashDocCommentView } from './DashDocCommentView';
 import { DashDocView } from './DashDocView';
@@ -1855,7 +1855,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         e.stopPropagation(); // drag n drop of text within text note will generate a new note if not caughst, as will dragging in from outside of Dash.
     };
     onScroll = (e: React.UIEvent) => {
-        if (!LinkDocPreview.LinkInfo && this._scrollRef.current) {
+        if (!LinkInfo.Instance?.LinkInfo && this._scrollRef.current) {
             if (!this._props.dontSelectOnLoad) {
                 this._ignoreScroll = true;
                 this.layoutDoc._layout_scrollTop = this._scrollRef.current.scrollTop;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index 4212d46eb..be8736525 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -3,7 +3,7 @@ import { EditorState, NodeSelection } from 'prosemirror-state';
 import { EditorView } from 'prosemirror-view';
 import { Doc } from '../../../../fields/Doc';
 import { DocServer } from '../../../DocServer';
-import { LinkDocPreview } from '../LinkDocPreview';
+import { LinkDocPreview, LinkInfo } from '../LinkDocPreview';
 import { FormattedTextBox } from './FormattedTextBox';
 import './FormattedTextBoxComment.scss';
 import { schema } from './schema_rts';
@@ -133,7 +133,7 @@ export class FormattedTextBoxComment {
             const naft = findEndOfMark(state.selection.$from, view, findLinkMark) || nbef;
             //nbef &&
             naft &&
-                LinkDocPreview.SetLinkInfo({
+                LinkInfo.SetLinkInfo({
                     docProps: textBox.props,
                     linkSrc: textBox.Document,
                     linkDoc: linkDoc ? (DocServer.GetCachedRefField(linkDoc) as Doc) : undefined,
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 62df77c0a..db815eae3 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -1,6 +1,5 @@
 import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
-import * as Pdfjs from 'pdfjs-dist';
 import 'pdfjs-dist/web/pdf_viewer.css';
 import { Doc, DocListCast, Opt } from '../../../fields/Doc';
 import { Height } from '../../../fields/DocSymbols';
@@ -17,20 +16,20 @@ import { CollectionFreeFormView } from '../collections/collectionFreeForm/Collec
 import { MarqueeAnnotator } from '../MarqueeAnnotator';
 import { DocFocusOptions, DocumentViewProps } from '../nodes/DocumentView';
 import { FieldViewProps } from '../nodes/FieldView';
-import { LinkDocPreview } from '../nodes/LinkDocPreview';
+import { LinkDocPreview, LinkInfo } from '../nodes/LinkDocPreview';
 import { StyleProp } from '../StyleProvider';
 import { AnchorMenu } from './AnchorMenu';
 import { Annotation } from './Annotation';
 import { GPTPopup } from './GPTPopup/GPTPopup';
 import './PDFViewer.scss';
 import * as React from 'react';
-const PDFJSViewer = require('pdfjs-dist/web/pdf_viewer');
-const pdfjsLib = require('pdfjs-dist');
+import * as Pdfjs from 'pdfjs-dist';
+import * as PDFJSViewer from 'pdfjs-dist/web/pdf_viewer.mjs';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 //pdfjsLib.GlobalWorkerOptions.workerSrc = `/assets/pdf.worker.js`;
 // The workerSrc property shall be specified.
-pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://unpkg.com/pdfjs-dist@2.16.105/build/pdf.worker.js';
+Pdfjs.GlobalWorkerOptions.workerSrc = 'https://unpkg.com/pdfjs-dist@4.0.269/build/pdf.worker.mjs';
 
 interface IViewerProps extends FieldViewProps {
     Document: Doc;
@@ -52,11 +51,6 @@ interface IViewerProps extends FieldViewProps {
 @observer
 export class PDFViewer extends React.Component<IViewerProps> {
     static _annotationStyle: any = addStyleSheet();
-    @observable private _pageSizes: { width: number; height: number }[] = [];
-    @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
-    @observable private _textSelecting = true;
-    @observable private _showWaiting = true;
-    @observable private Index: number = -1;
 
     _prevProps: IViewerProps;
     @observable _props: IViewerProps;
@@ -65,6 +59,13 @@ export class PDFViewer extends React.Component<IViewerProps> {
         this._prevProps = this._props = props;
         makeObservable(this);
     }
+
+    @observable _pageSizes: { width: number; height: number }[] = [];
+    @observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
+    @observable _textSelecting = true;
+    @observable _showWaiting = true;
+    @observable Index: number = -1;
+
     componentDidUpdate() {
         copyProps(this);
     }
@@ -101,7 +102,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         return this.allAnnotations.filter(a => a.text_inlineAnnotations);
     }
 
-    componentDidMount = async () => {
+    componentDidMount() {
         runInAction(() => (this._showWaiting = true));
         this.setupPdfJsViewer();
         this._mainCont.current?.addEventListener('scroll', e => ((e.target as any).scrollLeft = 0));
@@ -126,7 +127,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
             page => page !== undefined && page !== this._pdfViewer?.currentPageNumber && this.gotoPage(page),
             { fireImmediately: true }
         );
-    };
+    }
 
     componentWillUnmount = () => {
         Object.values(this._disposers).forEach(disposer => disposer?.());
@@ -262,7 +263,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         }
         document.removeEventListener('copy', this.copy);
         document.addEventListener('copy', this.copy);
-        const eventBus = new PDFJSViewer.EventBus(true);
+        const eventBus = new PDFJSViewer.EventBus();
         eventBus._on('pagesinit', this.pagesinit);
         eventBus._on(
             'pagerendered',
@@ -272,10 +273,9 @@ export class PDFViewer extends React.Component<IViewerProps> {
         const pdfFindController = new PDFJSViewer.PDFFindController({ linkService: pdfLinkService, eventBus });
         this._pdfViewer = new PDFJSViewer.PDFViewer({
             container: this._mainCont.current,
-            viewer: this._viewer.current,
+            viewer: this._viewer.current || undefined,
             linkService: pdfLinkService,
             findController: pdfFindController,
-            renderer: 'canvas',
             eventBus,
         });
         pdfLinkService.setViewer(this._pdfViewer);
@@ -313,7 +313,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
     onScroll = (e: React.UIEvent<HTMLElement>) => {
         if (this._mainCont.current && !this._forcedScroll) {
             this._ignoreScroll = true; // the pdf scrolled, so we need to tell the Doc to scroll but we don't want the doc to then try to set the PDF scroll pos (which would interfere with the smooth scroll animation)
-            if (!LinkDocPreview.LinkInfo) {
+            if (!LinkInfo.Instance?.LinkInfo) {
                 this._props.layoutDoc._layout_scrollTop = this._mainCont.current.scrollTop;
             }
             this._ignoreScroll = false;
diff --git a/src/server/ApiManagers/PDFManager.ts b/src/server/ApiManagers/PDFManager.ts
index e419d3ac4..d634a241d 100644
--- a/src/server/ApiManagers/PDFManager.ts
+++ b/src/server/ApiManagers/PDFManager.ts
@@ -1,31 +1,26 @@
-import ApiManager, { Registration } from "./ApiManager";
-import { Method } from "../RouteManager";
-import RouteSubscriber from "../RouteSubscriber";
-import { existsSync, createReadStream, createWriteStream } from "fs";
-import * as Pdfjs from 'pdfjs-dist/legacy/build/pdf';
-import { createCanvas } from "canvas";
-const imageSize = require("probe-image-size");
-import * as express from "express";
-import * as path from "path";
-import { Directory, serverPathToFile, clientPathToFile, pathToDirectory } from "./UploadManager";
-import { red } from "colors";
-import { resolve } from "path";
+import ApiManager, { Registration } from './ApiManager';
+import { Method } from '../RouteManager';
+import RouteSubscriber from '../RouteSubscriber';
+import { existsSync, createReadStream, createWriteStream } from 'fs';
+//import * as Pdfjs from 'pdfjs-dist';
+import { createCanvas } from 'canvas';
+const imageSize = require('probe-image-size');
+import * as express from 'express';
+import { Directory, serverPathToFile, clientPathToFile, pathToDirectory } from './UploadManager';
+import { red } from 'colors';
+import { resolve } from 'path';
 
 export default class PDFManager extends ApiManager {
-
     protected initialize(register: Registration): void {
-
         register({
             method: Method.POST,
-            subscription: new RouteSubscriber("thumbnail"),
+            subscription: new RouteSubscriber('thumbnail'),
             secureHandler: async ({ req, res }) => {
                 const { coreFilename, pageNum, subtree } = req.body;
                 return getOrCreateThumbnail(coreFilename, pageNum, res, subtree);
-            }
+            },
         });
-
     }
-
 }
 
 async function getOrCreateThumbnail(coreFilename: string, pageNum: number, res: express.Response, subtree?: string): Promise<void> {
@@ -51,7 +46,7 @@ async function getOrCreateThumbnail(coreFilename: string, pageNum: number, res:
 }
 
 async function CreateThumbnail(coreFilename: string, pageNum: number, res: express.Response, subtree?: string) {
-    const part1 = subtree ?? "";
+    const part1 = subtree ?? '';
     const filename = `${part1}${coreFilename}.pdf`;
     const sourcePath = resolve(pathToDirectory(Directory.pdfs), filename);
     const documentProxy = await Pdfjs.getDocument(sourcePath).promise;
@@ -62,7 +57,7 @@ async function CreateThumbnail(coreFilename: string, pageNum: number, res: expre
     const renderContext = {
         canvasContext: context,
         canvasFactory: factory,
-        viewport
+        viewport,
     };
     await page.render(renderContext).promise;
     const pngStream = canvas.createPNGStream();
@@ -71,11 +66,11 @@ async function CreateThumbnail(coreFilename: string, pageNum: number, res: expre
     const out = createWriteStream(pngFile);
     pngStream.pipe(out);
     return new Promise<void>((resolve, reject) => {
-        out.on("finish", () => {
+        out.on('finish', () => {
             dispatchThumbnail(res, viewport, resolved);
             resolve();
         });
-        out.on("error", error => {
+        out.on('error', error => {
             console.log(red(`In PDF thumbnail creation, encountered the following error when piping ${pngFile}:`));
             console.log(error);
             reject();
@@ -87,30 +82,29 @@ function dispatchThumbnail(res: express.Response, { width, height }: Pdfjs.PageV
     res.send({
         path: clientPathToFile(Directory.pdf_thumbnails, thumbnailName),
         width,
-        height
+        height,
     });
 }
 
 class NodeCanvasFactory {
-
     create = (width: number, height: number) => {
         const canvas = createCanvas(width, height);
         const context = canvas.getContext('2d');
         return {
             canvas,
-            context
+            context,
         };
-    }
+    };
 
     reset = (canvasAndContext: any, width: number, height: number) => {
         canvasAndContext.canvas.width = width;
         canvasAndContext.canvas.height = height;
-    }
+    };
 
     destroy = (canvasAndContext: any) => {
         canvasAndContext.canvas.width = 0;
         canvasAndContext.canvas.height = 0;
         canvasAndContext.canvas = null;
         canvasAndContext.context = null;
-    }
+    };
 }
diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts
index dd2a857d9..46f521bc9 100644
--- a/src/server/DashUploadUtils.ts
+++ b/src/server/DashUploadUtils.ts
@@ -607,19 +607,22 @@ export namespace DashUploadUtils {
         )); // prettier-ignore
 
         const fileIn = fs.readFileSync(sourcePath);
-        return autorotate.rotate(fileIn, { quality: 30 }).then(({ buffer }: { buffer: any }) =>
-            Jimp.read(buffer)
-                .then(async img => {
-                    await Promise.all( sizes.filter(({ width }) => width)  .map(({ width, suffix }) => 
+        let buffer: any;
+        try {
+            const { buffer2 } = await autorotate.rotate(fileIn, { quality: 30 });
+            buffer = buffer2;
+        } catch (e) {}
+        return Jimp.read(buffer ?? fileIn)
+            .then(async img => {
+                await Promise.all( sizes.filter(({ width }) => width)  .map(({ width, suffix }) => 
                         img = img.resize(width, Jimp.AUTO).write(outputPath(suffix))
                     )); // prettier-ignore
-                    return writtenFiles;
-                })
-                .catch(e => {
-                    console.log('ERROR' + e);
-                    return writtenFiles;
-                })
-        );
+                return writtenFiles;
+            })
+            .catch(e => {
+                console.log('ERROR' + e);
+                return writtenFiles;
+            });
     }
 
     /**
diff --git a/src/typings/index.d.ts b/src/typings/index.d.ts
index 284aae338..710b0211e 100644
--- a/src/typings/index.d.ts
+++ b/src/typings/index.d.ts
@@ -8,6 +8,7 @@ declare module 'bezier-curve';
 declare module 'fit-curve';
 declare module 'react-audio-waveform';
 declare module 'iink-js';
+declare module 'pdfjs-dist/web/pdf_viewer';
 
 declare module 'reveal';
 declare module 'react-reveal';
diff --git a/webpack.config.js b/webpack.config.js
index 57dfd222b..c1464ac2a 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -9,17 +9,17 @@ const plugins = [
     new HtmlWebpackPlugin({
         title: 'Caching',
     }),
-    new ForkTsCheckerWebpackPlugin({
-        // typescript: {
-        //     //useTypescriptIncrementalApi: true,
-        //     //tslint: true,
-        //     //memoryLimit: 4096,
-        // },
-        tslint: true,
-        memoryLimit: 4096,
-        useTypescriptIncrementalApi: true,
-        tslintAutoFix: true,
-    }),
+    // new ForkTsCheckerWebpackPlugin({
+    //     typescript: {
+    //         // useTypescriptIncrementalApi: true,
+    //         // tslint: true,
+    //         memoryLimit: 4096,
+    //     },
+    //     // tslint: true,
+    //     // memoryLimit: 4096,
+    //     // useTypescriptIncrementalApi: true,
+    //     // tslintAutoFix: true,
+    // }),
     new webpack.ProvidePlugin({ Buffer: ['buffer', 'Buffer'] }),
     new webpack.ProvidePlugin({ process: 'process/browser' }),
     new webpack.HotModuleReplacementPlugin(),
-- 
cgit v1.2.3-70-g09d2


From 5b5730a7df073659cbb6c326f748f7fcbe6625e8 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 18 Jan 2024 15:05:53 -0500
Subject: lots of changes to try to simplify API for viewPaths and related

---
 src/Utils.ts                                       |   3 +-
 src/client/documents/Documents.ts                  |   6 +-
 src/client/util/CurrentUserUtils.ts                |   4 +-
 src/client/util/DocumentManager.ts                 |  15 +-
 .../util/Import & Export/DirectoryImportBox.tsx    |   2 +-
 src/client/views/DocComponent.tsx                  |  56 +++-
 src/client/views/DocumentButtonBar.tsx             |   2 +-
 src/client/views/DocumentDecorations.tsx           |   6 +-
 src/client/views/GestureOverlay.tsx                |   2 +-
 src/client/views/InkControlPtHandles.tsx           |  12 +-
 src/client/views/InkStrokeProperties.ts            |   3 +-
 src/client/views/InkTangentHandles.tsx             |   4 +-
 src/client/views/InkingStroke.tsx                  |   9 +-
 src/client/views/LightboxView.tsx                  |   2 +-
 src/client/views/MainView.tsx                      |  47 +---
 src/client/views/MarqueeAnnotator.tsx              |   2 +-
 src/client/views/OverlayView.tsx                   |   2 +-
 src/client/views/PreviewCursor.tsx                 |  25 +-
 src/client/views/PropertiesButtons.tsx             |  46 ++-
 src/client/views/PropertiesDocContextSelector.tsx  |   2 +-
 src/client/views/PropertiesView.tsx                |   2 +-
 src/client/views/StyleProvider.tsx                 |  33 ++-
 src/client/views/TemplateMenu.tsx                  |   2 -
 .../views/collections/CollectionCarouselView.tsx   |   4 +-
 .../views/collections/CollectionDockingView.tsx    |   2 +-
 src/client/views/collections/CollectionMenu.tsx    |   2 -
 .../views/collections/CollectionNoteTakingView.tsx |  10 +-
 .../views/collections/CollectionPileView.tsx       |   2 +-
 .../collections/CollectionStackedTimeline.tsx      |   4 +-
 .../views/collections/CollectionStackingView.tsx   |  12 +-
 src/client/views/collections/CollectionSubView.tsx |   4 +-
 .../views/collections/CollectionTreeView.tsx       |   4 +-
 src/client/views/collections/CollectionView.tsx    |  10 +-
 src/client/views/collections/TabDocView.tsx        |   9 +-
 src/client/views/collections/TreeView.tsx          |  12 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  51 ++--
 .../collectionLinear/CollectionLinearView.tsx      |   6 +-
 .../CollectionMulticolumnView.tsx                  |   6 +-
 .../CollectionMultirowView.tsx                     |   7 +-
 .../collectionSchema/CollectionSchemaView.tsx      |   7 +-
 .../collections/collectionSchema/SchemaRowBox.tsx  |   8 +-
 .../collectionSchema/SchemaTableCell.tsx           |   1 -
 src/client/views/global/globalScripts.ts           |   4 +-
 src/client/views/linking/LinkMenuItem.tsx          |   4 +-
 src/client/views/linking/LinkPopup.tsx             |   2 -
 src/client/views/newlightbox/NewLightboxView.tsx   |   2 +-
 src/client/views/nodes/AudioBox.tsx                |   4 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |   8 +-
 src/client/views/nodes/ComparisonBox.tsx           |   5 +-
 src/client/views/nodes/DataVizBox/DataVizBox.tsx   |  41 +--
 src/client/views/nodes/DocumentContentsView.tsx    |  11 +-
 src/client/views/nodes/DocumentIcon.tsx            |   2 +-
 src/client/views/nodes/DocumentLinksButton.tsx     |   4 +-
 src/client/views/nodes/DocumentView.tsx            | 308 +++++++++++----------
 src/client/views/nodes/EquationBox.tsx             |   2 +-
 src/client/views/nodes/FieldView.tsx               |   9 +-
 src/client/views/nodes/ImageBox.tsx                |   8 +-
 src/client/views/nodes/KeyValuePair.tsx            |  65 ++---
 src/client/views/nodes/LinkAnchorBox.tsx           |  14 +-
 src/client/views/nodes/LinkBox.tsx                 |   9 +-
 src/client/views/nodes/LinkDocPreview.tsx          |  12 +-
 src/client/views/nodes/MapBox/MapBox.tsx           |   4 +-
 src/client/views/nodes/MapBox/MapPushpinBox.tsx    |   4 +-
 .../views/nodes/MapboxMapBox/MapboxContainer.tsx   |   4 +-
 src/client/views/nodes/PDFBox.tsx                  |   8 +-
 src/client/views/nodes/VideoBox.tsx                |  18 +-
 src/client/views/nodes/WebBox.tsx                  |  36 +--
 .../views/nodes/formattedText/DashDocView.tsx      |   4 +-
 .../views/nodes/formattedText/DashFieldView.tsx    |   2 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  16 +-
 .../formattedText/FormattedTextBoxComment.tsx      |   3 +-
 src/client/views/nodes/trails/PresBox.tsx          |  12 +-
 src/client/views/nodes/trails/PresElementBox.tsx   |  10 +-
 src/client/views/pdf/PDFViewer.tsx                 |  12 +-
 src/client/views/topbar/TopBar.tsx                 |   2 -
 src/mobile/MobileInterface.tsx                     |   2 +-
 src/server/DashStats.ts                            |   4 +-
 77 files changed, 566 insertions(+), 537 deletions(-)

(limited to 'src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx')

diff --git a/src/Utils.ts b/src/Utils.ts
index b5c218e01..852083834 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -7,6 +7,7 @@ import { Socket } from '../node_modules/socket.io/dist/index';
 import { DocumentType } from './client/documents/DocumentTypes';
 import { Colors } from './client/views/global/globalEnums';
 import { Message } from './server/Message';
+import { DocumentView } from './client/views/nodes/DocumentView';
 
 export namespace Utils {
     export let CLICK_TIME = 300;
@@ -583,7 +584,7 @@ export function returnEmptyDoclist() {
     return [] as any[];
 }
 
-export let emptyPath = [];
+export let emptyPath: DocumentView[] = [];
 
 export function emptyFunction() {
     return undefined;
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 92a9ddfe8..40cffcf45 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -244,7 +244,6 @@ export class DocumentOptions {
     _layout_reflowVertical?: BOOLt = new BoolInfo('native height can be changed independent of width by dragging decoration resizers');
     _layout_reflowHorizontal?: BOOLt = new BoolInfo('whether a doc with a native size can be horizonally resized, causing some form of reflow');
     layout_boxShadow?: string; // box-shadow css string OR "standard" to use dash standard box shadow
-    layout_maxAutoHeight?: NUMt = new NumInfo('maximum height for newly created (eg, from pasting) text documents', false);
     _layout_autoHeight?: BOOLt = new BoolInfo('whether document automatically resizes vertically to display contents');
     _layout_curPage?: NUMt = new NumInfo('current page of a PDF or other? paginated document', false);
     _layout_currentTimecode?: NUMt = new NumInfo('the current timecode of a time-based document (e.g., current time of a video)  value is in seconds', false);
@@ -290,7 +289,7 @@ export class DocumentOptions {
     dontPlayLinkOnSelect?: BOOLt = new BoolInfo('whether an audio/video should start playing when a link is followed to it.');
     openFactoryLocation?: string; // an OpenWhere value to place the factory created document
     openFactoryAsDelegate?: boolean; //
-    updateContentsScript?: ScriptField; // reactive script invoked when viewing a document that can update contents of a collection (or do anything)
+    onViewMounted?: ScriptField; // reactive script invoked Doc is viewed (used by showBackLinks view to update collection of links to Doc)
     toolTip?: string; // tooltip to display on hover
     toolType?: string; // type of pen tool
     expertMode?: BOOLt = new BoolInfo('something available only in expert (not novice) mode');
@@ -1895,12 +1894,11 @@ export namespace DocUtils {
         }
     }
 
-    export function GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean, annotationOn?: Doc, maxHeight?: number, backgroundColor?: string) {
+    export function GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean, annotationOn?: Doc, backgroundColor?: string) {
         const tbox = Docs.Create.TextDocument('', {
             _xMargin: noMargins ? 0 : undefined,
             _yMargin: noMargins ? 0 : undefined,
             annotationOn,
-            layout_maxAutoHeight: maxHeight,
             backgroundColor,
             _width: width || 200,
             _height: 35,
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 4391e87d6..2dfd91314 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -111,7 +111,7 @@ export class CurrentUserUtils {
         const tempClicks = DocCast(doc[field]);
         const reqdClickOpts:DocumentOptions = {_width: 300, _height:200, isSystem: true};
         const reqdTempOpts:{opts:DocumentOptions, script: string}[] = [
-            { opts: { title: "Open In Target",        targetScriptKey: "onChildClick"},       script: "docCastAsync(documentView?.props.docViewPath().lastElement()?.Document.target).then((target) => target && (target.proto.data = new List([self])))"},
+            { opts: { title: "Open In Target",        targetScriptKey: "onChildClick"},       script: "docCastAsync(documentView?.containerViewPath().lastElement()?.Document.target).then((target) => target && (target.proto.data = new List([self])))"},
             { opts: { title: "Open Detail On Right",  targetScriptKey: "onChildDoubleClick"}, script: `openDoc(this.doubleClickView.${OpenWhere.addRight})`}];
         const reqdClickList = reqdTempOpts.map(opts => {
             const allOpts = {...reqdClickOpts, ...opts.opts};
@@ -285,7 +285,7 @@ export class CurrentUserUtils {
                                                                                                 treeView_HasOverlay: true, _text_fontSize: "20px", _layout_autoHeight: true, 
                                                                                                 dropAction:'move', treeView_Type: TreeViewType.outline, 
                                                                                                 backgroundColor: "white", _xMargin: 0, _yMargin: 0, _createDocOnCR: true 
-                                                                                            }, funcs: {title: 'this.text?.Text'}},
+                                                                                            }, funcs: {title: 'pptSlide'}},//'this.text?.Text'}},
             ];
 
         emptyThings.forEach(thing => DocUtils.AssignDocField(doc, "empty"+thing.key, (opts) => thing.creator(opts), {...standardOps(thing.key), ...thing.opts}, undefined, thing.scripts, thing.funcs));
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 7fef5cc79..07357a764 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -153,8 +153,8 @@ export class DocumentManager {
         return passes.reduce(
             (toReturn, pass) =>
                 toReturn ??
-                docViewArray.filter(view => view.Document === target).find(view => !pass || view._props.docViewPath().lastElement() === preferredCollection) ??
-                docViewArray.filter(view => Doc.AreProtosEqual(view.Document, target)).find(view => !pass || view._props.docViewPath().lastElement() === preferredCollection),
+                docViewArray.filter(view => view.Document === target).find(view => !pass || view.containerViewPath?.().lastElement() === preferredCollection) ??
+                docViewArray.filter(view => Doc.AreProtosEqual(view.Document, target)).find(view => !pass || view.containerViewPath?.().lastElement() === preferredCollection),
             undefined as Opt<DocumentView>
         );
     }
@@ -224,7 +224,7 @@ export class DocumentManager {
     }
 
     public static removeOverlayViews() {
-        DocumentManager._overlayViews?.forEach(action(view => (view.textHtmlOverlay = undefined)));
+        DocumentManager._overlayViews?.forEach(view => view.setTextHtmlOverlay(undefined, undefined));
         DocumentManager._overlayViews?.clear();
     }
     static _overlayViews = new ObservableSet<DocumentView>();
@@ -274,7 +274,7 @@ export class DocumentManager {
             // even if we found the document view, if the target is a lightbox, we try to open it in the lightbox to preserve lightbox semantics (eg, there's only one active doc in the lightbox)
             const target = DocCast(targetDoc.annotationOn, targetDoc);
             const contextView = this.getDocumentView(DocCast(target.embedContainer));
-            if (contextView?.docView?._componentView?.addDocTab?.(target, OpenWhere.lightbox)) {
+            if (contextView?.ComponentView?.addDocTab?.(target, OpenWhere.lightbox)) {
                 await new Promise<void>(waitres => setTimeout(() => waitres()));
             }
         }
@@ -327,15 +327,12 @@ export class DocumentManager {
 
             if (options.zoomTextSelections && Doc.UnhighlightTimer && contextView && targetDoc.text_html) {
                 // if the docView is a text anchor, the contextView is the PDF/Web/Text doc
-                contextView.htmlOverlayEffect = options.effect;
-                contextView.textHtmlOverlayTime = options.zoomTime;
-                contextView.textHtmlOverlay = StrCast(targetDoc.text_html);
+                contextView.setTextHtmlOverlay(StrCast(targetDoc.text_html), options.effect);
                 DocumentManager._overlayViews.add(contextView);
             }
             Doc.AddUnHighlightWatcher(() => {
                 docView.Document[Animation] = undefined;
                 DocumentManager.removeOverlayViews();
-                contextView && (contextView.htmlOverlayEffect = undefined);
             });
         }
     }
@@ -344,7 +341,7 @@ export function DocFocusOrOpen(doc: Doc, options: DocFocusOptions = { willZoomCe
     const func = () => {
         const cv = DocumentManager.Instance.getDocumentView(containingDoc);
         const dv = DocumentManager.Instance.getDocumentView(doc, cv);
-        if (dv && (!containingDoc || dv._props.docViewPath().lastElement()?.Document === containingDoc)) {
+        if (dv && (!containingDoc || dv.containerViewPath?.().lastElement()?.Document === containingDoc)) {
             DocumentManager.Instance.showDocumentView(dv, options).then(() => dv && Doc.linkFollowHighlight(dv.Document));
         } else {
             const container = DocCast(containingDoc ?? doc.embedContainer ?? Doc.BestEmbedding(doc));
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index b6dbea33a..398ba3c04 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx	
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx	
@@ -156,7 +156,7 @@
 //             x: NumCast(doc.x),
 //             y: NumCast(doc.y) + offset,
 //         };
-//         const parent = this.props.DocumentView?.()._props.docViewPath().lastElement();
+//         const parent = this.props.DocumentView?.().containerViewPath().lastElement();
 //         if (parent?.Document.type === DocumentType.COL) {
 //             let importContainer: Doc;
 //             if (docs.length < 50) {
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index dfc298840..fb8ef7c36 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -1,6 +1,6 @@
 import { action, computed, makeObservable, observable } from 'mobx';
 import * as React from 'react';
-import { returnFalse } from '../../Utils';
+import { emptyPath, returnFalse } from '../../Utils';
 import { DateField } from '../../fields/DateField';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
 import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, DocData } from '../../fields/DocSymbols';
@@ -9,17 +9,20 @@ import { GetEffectiveAcl, inheritParentAcls } from '../../fields/util';
 import { DocumentType } from '../documents/DocumentTypes';
 import { DocUtils } from '../documents/Documents';
 import { DocumentManager } from '../util/DocumentManager';
+import { Transform } from '../util/Transform';
 import { ObservableReactComponent } from './ObservableReactComponent';
 import { CollectionFreeFormView } from './collections/collectionFreeForm';
 import { DocumentView } from './nodes/DocumentView';
-import { Transform } from '../util/Transform';
 
-///  DocComponent returns a generic React base class used by views that don't have 'fieldKey' props (e.g.,CollectionFreeFormDocumentView, DocumentView)
+/**
+ * DocComponent returns a generic React base class used by Doc views (not the 'Box' views that render the contents of Doc views)
+ *    (e.g.,CollectionFreeFormDocumentView, DocumentViewInternal)
+ *
+ * */
 export interface DocComponentProps {
     Document: Doc;
     LayoutTemplate?: () => Opt<Doc>;
     LayoutTemplateString?: string;
-    ScreenToLocalTransform: () => Transform;
 }
 export function DocComponent<P extends DocComponentProps>() {
     class Component extends ObservableReactComponent<React.PropsWithChildren<P>> {
@@ -28,8 +31,6 @@ export function DocComponent<P extends DocComponentProps>() {
             makeObservable(this);
         }
 
-        ScreenToLocalBoxXf = () => this._props.ScreenToLocalTransform();
-
         //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then
         get Document() {
             return this._props.Document;
@@ -46,21 +47,40 @@ export function DocComponent<P extends DocComponentProps>() {
     return Component;
 }
 
-/// FieldViewBoxProps  -  a generic base class for field views that are not annotatable (e.g. InkingStroke, ColorBox)
-interface ViewBoxBaseProps {
+/**
+ *  ViewBoxBaseComponent  -  base class for non-annotatable views that render the interior contents of a DocumentView
+ *                           (e.g. InkingStroke, ColorBox)
+ */
+export interface ViewBoxBaseProps {
     Document: Doc;
     TemplateDataDocument?: Doc;
     DocumentView?: () => DocumentView;
+    containerViewPath?: () => DocumentView[];
     fieldKey: string;
     isSelected: () => boolean;
     isContentActive: () => boolean | undefined;
     ScreenToLocalTransform: () => Transform;
     renderDepth: number;
 }
+function returnEmptyDocViewList() {
+    return [] as DocumentView[];
+}
 export function ViewBoxBaseComponent<P extends ViewBoxBaseProps>() {
     class Component extends ObservableReactComponent<React.PropsWithChildren<P>> {
         ScreenToLocalBoxXf = () => this._props.ScreenToLocalTransform();
 
+        get DocumentView() {
+            return this._props.DocumentView;
+        }
+        get docViewPath() {
+            return this.DocumentView?.().docViewPath ?? emptyPath;
+        }
+        get docViewPathFunc() {
+            return this.DocumentView?.().docViewPathFunc ?? returnEmptyDocViewList;
+        }
+        get containerViewPath() {
+            return this._props.containerViewPath;
+        }
         //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then
         get Document() {
             return this._props.Document;
@@ -81,10 +101,15 @@ export function ViewBoxBaseComponent<P extends ViewBoxBaseProps>() {
     return Component;
 }
 
-///  DocAnnotatbleComponent -return a base class for React views of document fields that are annotatable *and* interactive when selected (e.g., pdf, image)
+/**
+ * DocAnnotatableComponent - base class for annotatable views that render the interior contents of a DocumentView
+ *                       (e.g., PdfBox, ImageBox)
+ * These views should be interactive (respond to pointerEvents) when their conatainer DocumentView is selected
+ */
 export interface ViewBoxAnnotatableProps {
     Document: Doc;
     TemplateDataDocument?: Doc;
+    DocumentView?: () => DocumentView;
     fieldKey: string;
     filterAddDocument?: (doc: Doc[]) => boolean; // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example)
     isContentActive: () => boolean | undefined;
@@ -106,6 +131,19 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
 
         @observable _annotationKeySuffix = () => 'annotations';
         @observable _isAnyChildContentActive = false;
+
+        get DocumentView() {
+            return this._props.DocumentView;
+        }
+        get docViewPath() {
+            return this.DocumentView?.().docViewPath ?? emptyPath;
+        }
+        get docViewPathFunc() {
+            return this.DocumentView?.().docViewPathFunc ?? returnEmptyDocViewList;
+        }
+        get containerViewPath() {
+            return this.DocumentView?.().containerViewPath ?? returnEmptyDocViewList;
+        }
         //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then
         @computed get Document() {
             return this._props.Document;
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index bb28d9ec9..8a4b42ae0 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -72,7 +72,7 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
                 <div
                     className="documentButtonBar-icon documentButtonBar-follow"
                     style={{ backgroundColor: followLink ? Colors.LIGHT_BLUE : Colors.DARK_GRAY, color: followLink ? Colors.BLACK : Colors.WHITE }}
-                    onClick={undoBatch(e => this._props.views().map(view => view?.docView?.toggleFollowLink(undefined, false)))}>
+                    onClick={undoBatch(e => this._props.views().map(view => view?.toggleFollowLink(undefined, false)))}>
                     <div className="documentButtonBar-followTypes">
                         {followBtn(
                             true,
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 788e78ed2..c96253405 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -303,7 +303,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
         SelectionManager.DeselectAll();
     };
 
-    onSelectContainerDocClick = () => SelectionManager.Views?.[0]?._props.docViewPath?.().lastElement()?.select(false);
+    onSelectContainerDocClick = () => SelectionManager.Views?.[0]?.containerViewPath?.().lastElement()?.select(false);
     /**
      * sets up events when user clicks on the border radius editor
      */
@@ -677,7 +677,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
             seldocview._props.hideDeleteButton ||
             seldocview.Document.hideDeleteButton ||
             SelectionManager.Views.some(docView => {
-                const collectionAcl = docView._props.docViewPath()?.lastElement() ? GetEffectiveAcl(docView._props.docViewPath().lastElement().dataDoc) : AclEdit;
+                const collectionAcl = docView.containerViewPath?.()?.lastElement() ? GetEffectiveAcl(docView.containerViewPath?.().lastElement().dataDoc) : AclEdit;
                 return collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.Document) !== AclAdmin;
             });
         const topBtn = (key: string, icon: string, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: any) => void), title: string) => (
@@ -805,7 +805,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
                                     <div key="br" className="documentDecorations-bottomRightResizer" onPointerDown={this.onPointerDown} />
                                 </>
                             )}
-                            {seldocview._props.renderDepth <= 1 || !seldocview._props.docViewPath().lastElement() ? null : topBtn('selector', 'arrow-alt-circle-up', undefined, this.onSelectContainerDocClick, 'tap to select containing document')}
+                            {seldocview._props.renderDepth <= 1 || !seldocview.containerViewPath?.().lastElement() ? null : topBtn('selector', 'arrow-alt-circle-up', undefined, this.onSelectContainerDocClick, 'tap to select containing document')}
                             {useRounding && (
                                 <div
                                     className="documentDecorations-borderRadius"
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index b1b04237c..3c7b5eeca 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -476,7 +476,7 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
                 isContentActive={returnFalse}
                 renderDepth={0}
                 styleProvider={returnEmptyString}
-                docViewPath={returnEmptyDoclist}
+                containerViewPath={returnEmptyDoclist}
                 focus={emptyFunction}
                 whenChildContentsActiveChanged={emptyFunction}
                 bringToFront={emptyFunction}
diff --git a/src/client/views/InkControlPtHandles.tsx b/src/client/views/InkControlPtHandles.tsx
index a5c2d99d2..7dd57e04d 100644
--- a/src/client/views/InkControlPtHandles.tsx
+++ b/src/client/views/InkControlPtHandles.tsx
@@ -27,7 +27,7 @@ export interface InkControlProps {
 export class InkControlPtHandles extends React.Component<InkControlProps> {
     @observable private _overControl = -1;
     get docView() {
-        return this.props.inkView.props.docViewPath().lastElement();
+        return this.props.inkView.DocumentView?.();
     }
 
     componentDidMount() {
@@ -58,11 +58,11 @@ export class InkControlPtHandles extends React.Component<InkControlProps> {
                     if (!this.props.inkView.controlUndo) this.props.inkView.controlUndo = UndoManager.StartBatch('drag ink ctrl pt');
                     const inkMoveEnd = ptFromScreen({ X: delta[0], Y: delta[1] });
                     const inkMoveStart = ptFromScreen({ X: 0, Y: 0 });
-                    InkStrokeProperties.Instance.moveControlPtHandle(this.docView, inkMoveEnd.X - inkMoveStart.X, inkMoveEnd.Y - inkMoveStart.Y, controlIndex, origInk);
+                    this.docView && InkStrokeProperties.Instance.moveControlPtHandle(this.docView, inkMoveEnd.X - inkMoveStart.X, inkMoveEnd.Y - inkMoveStart.Y, controlIndex, origInk);
                     return false;
                 }),
                 action(() => {
-                    if (this.props.inkView.controlUndo) {
+                    if (this.props.inkView.controlUndo && this.docView) {
                         InkStrokeProperties.Instance.snapControl(this.docView, controlIndex);
                     }
                     this.props.inkView.controlUndo?.end();
@@ -78,11 +78,11 @@ export class InkControlPtHandles extends React.Component<InkControlProps> {
                         } else {
                             if (brokenIndices?.includes(equivIndex)) {
                                 if (!this.props.inkView.controlUndo) this.props.inkView.controlUndo = UndoManager.StartBatch('make smooth');
-                                InkStrokeProperties.Instance.snapHandleTangent(this.docView, equivIndex, handleIndexA, handleIndexB);
+                                this.docView && InkStrokeProperties.Instance.snapHandleTangent(this.docView, equivIndex, handleIndexA, handleIndexB);
                             }
                             if (equivIndex !== controlIndex && brokenIndices?.includes(controlIndex)) {
                                 if (!this.props.inkView.controlUndo) this.props.inkView.controlUndo = UndoManager.StartBatch('make smooth');
-                                InkStrokeProperties.Instance.snapHandleTangent(this.docView, controlIndex, handleIndexA, handleIndexB);
+                                this.docView && InkStrokeProperties.Instance.snapHandleTangent(this.docView, controlIndex, handleIndexA, handleIndexB);
                             }
                         }
                         this.props.inkView.controlUndo?.end();
@@ -113,7 +113,7 @@ export class InkControlPtHandles extends React.Component<InkControlProps> {
     @action
     onDelete = (e: KeyboardEvent) => {
         if (['-', 'Backspace', 'Delete'].includes(e.key)) {
-            InkStrokeProperties.Instance.deletePoints(this.docView, e.shiftKey);
+            this.docView && InkStrokeProperties.Instance.deletePoints(this.docView, e.shiftKey);
             e.stopPropagation();
         }
     };
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts
index f31ea2994..52ea89cde 100644
--- a/src/client/views/InkStrokeProperties.ts
+++ b/src/client/views/InkStrokeProperties.ts
@@ -241,6 +241,7 @@ export class InkStrokeProperties {
      */
     @undoBatch
     moveControlPtHandle = (inkView: DocumentView, deltaX: number, deltaY: number, controlIndex: number, origInk?: InkData) =>
+        inkView &&
         this.applyFunction(inkView, (view: DocumentView, ink: InkData) => {
             const order = controlIndex % 4;
             const closed = InkingStroke.IsClosed(ink);
@@ -369,7 +370,7 @@ export class InkStrokeProperties {
 
     snapToAllCurves = (screenDragPt: { X: number; Y: number }, inkView: DocumentView, snapData: { nearestPt: { X: number; Y: number }; distance: number }, ink: InkData, controlIndex: number) => {
         const containingCollection = inkView.CollectionFreeFormView;
-        const containingDocView = containingCollection?.props.DocumentView?.();
+        const containingDocView = containingCollection?.DocumentView?.();
         containingCollection?.childDocs
             .filter(doc => doc.type === DocumentType.INK)
             .forEach(doc => {
diff --git a/src/client/views/InkTangentHandles.tsx b/src/client/views/InkTangentHandles.tsx
index a00c26a07..c20399698 100644
--- a/src/client/views/InkTangentHandles.tsx
+++ b/src/client/views/InkTangentHandles.tsx
@@ -23,7 +23,7 @@ export interface InkHandlesProps {
 @observer
 export class InkTangentHandles extends React.Component<InkHandlesProps> {
     get docView() {
-        return this.props.inkView.props.docViewPath().lastElement();
+        return this.props.inkView.DocumentView?.();
     }
     /**
      * Handles the movement of a selected handle point when the user clicks and drags.
@@ -42,7 +42,7 @@ export class InkTangentHandles extends React.Component<InkHandlesProps> {
                 if (e.altKey) this.onBreakTangent(controlIndex);
                 const inkMoveEnd = this.props.inkView.ptFromScreen({ X: delta[0], Y: delta[1] });
                 const inkMoveStart = this.props.inkView.ptFromScreen({ X: 0, Y: 0 });
-                InkStrokeProperties.Instance.moveTangentHandle(this.docView, -(inkMoveEnd.X - inkMoveStart.X), -(inkMoveEnd.Y - inkMoveStart.Y), handleIndex, oppositeHandleIndex, controlIndex);
+                this.docView && InkStrokeProperties.Instance.moveTangentHandle(this.docView, -(inkMoveEnd.X - inkMoveStart.X), -(inkMoveEnd.Y - inkMoveStart.Y), handleIndex, oppositeHandleIndex, controlIndex);
                 return false;
             }),
             action(() => {
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index c652e8966..11b05352d 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -34,7 +34,7 @@ import { InteractionUtils } from '../util/InteractionUtils';
 import { SnappingManager } from '../util/SnappingManager';
 import { UndoManager } from '../util/UndoManager';
 import { ContextMenu } from './ContextMenu';
-import { ViewBoxBaseComponent } from './DocComponent';
+import { ViewBoxBaseComponent, ViewBoxBaseProps } from './DocComponent';
 import { Colors } from './global/globalEnums';
 import { InkControlPtHandles, InkEndPtHandles } from './InkControlPtHandles';
 import './InkStroke.scss';
@@ -48,7 +48,7 @@ import { StyleProp } from './StyleProvider';
 import { Transform } from '../util/Transform';
 const { default: { INK_MASK_SIZE } } = require('./global/globalCssVariables.module.scss'); // prettier-ignore
 @observer
-export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
+export class InkingStroke extends ViewBoxBaseComponent<ViewBoxBaseProps & FieldViewProps>() {
     static readonly MaskDim = INK_MASK_SIZE; // choose a really big number to make sure mask fits over container (which in theory can be arbitrarily big)
     public static LayoutString(fieldStr: string) {
         return FieldView.LayoutString(InkingStroke, fieldStr);
@@ -122,7 +122,8 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     onPointerDown = (e: React.PointerEvent) => {
         this._handledClick = false;
-        const inkView = this._props.docViewPath().lastElement();
+        const inkView = this.DocumentView?.();
+        if (!inkView) return;
         const { inkData, inkScaleX, inkScaleY, inkStrokeWidth, inkTop, inkLeft } = this.inkScaledData();
         const screenPts = inkData
             .map(point =>
@@ -162,7 +163,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
                     InkStrokeProperties.Instance._currentPoint = -1;
                     this._handledClick = true; // mark the double-click pseudo pointerevent so we can block the real mouse event from propagating to DocumentView
                     if (isEditing) {
-                        this._nearestT && this._nearestSeg !== undefined && InkStrokeProperties.Instance.addPoints(this._props.docViewPath().lastElement(), this._nearestT, this._nearestSeg, this.inkScaledData().inkData.slice());
+                        this._nearestT && this._nearestSeg !== undefined && InkStrokeProperties.Instance.addPoints(inkView, this._nearestT, this._nearestSeg, this.inkScaledData().inkData.slice());
                     }
                 }
             }),
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index ab3be3d7a..aec9e32fc 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -257,7 +257,7 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> {
                             styleProvider={DefaultStyleProvider}
                             ScreenToLocalTransform={this.lightboxScreenToLocal}
                             renderDepth={0}
-                            docViewPath={returnEmptyDoclist}
+                            containerViewPath={returnEmptyDoclist}
                             childFilters={returnEmptyFilter}
                             childFiltersByRanges={returnEmptyFilter}
                             searchFilterDocs={returnEmptyDoclist}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 35ffab337..cd6aeea41 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -606,7 +606,7 @@ export class MainView extends ObservableReactComponent<{}> {
                     Document={this.headerBarDoc}
                     addDocTab={DocumentViewInternal.addDocTabFunc}
                     pinToPres={emptyFunction}
-                    docViewPath={returnEmptyDoclist}
+                    containerViewPath={returnEmptyDoclist}
                     styleProvider={DefaultStyleProvider}
                     addDocument={this.addHeaderDoc}
                     removeDocument={this.removeHeaderDoc}
@@ -641,7 +641,7 @@ export class MainView extends ObservableReactComponent<{}> {
                     addDocument={undefined}
                     addDocTab={DocumentViewInternal.addDocTabFunc}
                     pinToPres={emptyFunction}
-                    docViewPath={returnEmptyDoclist}
+                    containerViewPath={returnEmptyDoclist}
                     styleProvider={this._hideUI ? DefaultStyleProvider : undefined}
                     isContentActive={returnTrue}
                     removeDocument={undefined}
@@ -735,7 +735,7 @@ export class MainView extends ObservableReactComponent<{}> {
                         addDocument={undefined}
                         addDocTab={DocumentViewInternal.addDocTabFunc}
                         pinToPres={TabDocView.PinDoc}
-                        docViewPath={returnEmptyDoclist}
+                        containerViewPath={returnEmptyDoclist}
                         styleProvider={this._sidebarContent.proto === Doc.MyDashboards || this._sidebarContent.proto === Doc.MyFilesystem || this._sidebarContent.proto === Doc.MyTrails ? DashboardStyleProvider : DefaultStyleProvider}
                         removeDocument={returnFalse}
                         ScreenToLocalTransform={this.mainContainerXf}
@@ -769,7 +769,7 @@ export class MainView extends ObservableReactComponent<{}> {
                     PanelWidth={this.leftMenuWidth}
                     PanelHeight={this.leftMenuHeight}
                     renderDepth={0}
-                    docViewPath={returnEmptyDoclist}
+                    containerViewPath={returnEmptyDoclist}
                     focus={emptyFunction}
                     styleProvider={DefaultStyleProvider}
                     isContentActive={returnTrue}
@@ -900,14 +900,13 @@ export class MainView extends ObservableReactComponent<{}> {
                     Document={Doc.MyDockedBtns}
                     fieldKey="data"
                     dropAction="embed"
-                    setHeight={returnFalse}
                     styleProvider={DefaultStyleProvider}
                     bringToFront={emptyFunction}
                     select={emptyFunction}
                     isAnyChildContentActive={returnFalse}
                     isContentActive={emptyFunction}
                     isSelected={returnFalse}
-                    docViewPath={returnEmptyDoclist}
+                    containerViewPath={returnEmptyDoclist}
                     moveDocument={this.moveButtonDoc}
                     addDocument={this.addButtonDoc}
                     addDocTab={DocumentViewInternal.addDocTabFunc}
@@ -969,41 +968,6 @@ export class MainView extends ObservableReactComponent<{}> {
         );
     }
 
-    @observable mapBoxHackBool = false;
-    @computed get mapBoxHack() {
-        return this.mapBoxHackBool ? null : (
-            <MapBox
-                ref={action((r: any) => r && (this.mapBoxHackBool = true))}
-                fieldKey="data"
-                select={returnFalse}
-                isSelected={returnFalse}
-                Document={this.headerBarDoc}
-                addDocTab={returnFalse}
-                pinToPres={emptyFunction}
-                docViewPath={returnEmptyDoclist}
-                styleProvider={DefaultStyleProvider}
-                addDocument={returnFalse}
-                removeDocument={returnFalse}
-                fitContentsToBox={returnTrue}
-                isDocumentActive={returnTrue} // headerBar is always documentActive (ie, the docView gets pointer events)
-                isContentActive={returnTrue} // headerBar is awlays contentActive which means its items are always documentActive
-                ScreenToLocalTransform={Transform.Identity}
-                childHideResizeHandles={true}
-                childDragAction="move"
-                dontRegisterView={true}
-                PanelWidth={this.headerBarDocWidth}
-                PanelHeight={this.headerBarDocHeight}
-                renderDepth={0}
-                focus={emptyFunction}
-                whenChildContentsActiveChanged={emptyFunction}
-                bringToFront={emptyFunction}
-                childFilters={returnEmptyFilter}
-                childFiltersByRanges={returnEmptyFilter}
-                searchFilterDocs={returnEmptyDoclist}
-            />
-        );
-    }
-
     render() {
         return (
             <div
@@ -1070,7 +1034,6 @@ export class MainView extends ObservableReactComponent<{}> {
                 <LightboxView key="lightbox" PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} />
                 <CollectionFreeFormLinksView />
                 <OverlayView />
-                {/* {this.mapBoxHack} */}
                 <GPTPopup key="gptpopup" />
                 <SchemaCSVPopUp key="schemacsvpopup" />
                 <GenerativeFill imageEditorOpen={ImageBox.imageEditorOpen} imageEditorSource={ImageBox.imageEditorSource} imageRootDoc={ImageBox.imageRootDoc} addDoc={ImageBox.addDoc} />
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 9d828364d..cfdc648b4 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -198,7 +198,7 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
             const sourceAnchorCreator = () => this.highlight(this.props.highlightDragSrcColor ?? 'rgba(173, 216, 230, 0.75)', true, undefined, true); // hyperlink color
 
             const targetCreator = (annotationOn: Doc | undefined) => {
-                const target = DocUtils.GetNewTextDoc('Note linked to ' + this.props.Document.title, 0, 0, 100, 100, undefined, annotationOn, undefined, 'yellow');
+                const target = DocUtils.GetNewTextDoc('Note linked to ' + this.props.Document.title, 0, 0, 100, 100, undefined, annotationOn, 'yellow');
                 FormattedTextBox.SetSelectOnLoad(target);
                 return target;
             };
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 915c3c18f..89527c415 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -234,7 +234,7 @@ export class OverlayView extends ObservableReactComponent<{}> {
                         whenChildContentsActiveChanged={emptyFunction}
                         focus={emptyFunction}
                         styleProvider={DefaultStyleProvider}
-                        docViewPath={returnEmptyDoclist}
+                        containerViewPath={returnEmptyDoclist}
                         addDocTab={DocumentViewInternal.addDocTabFunc}
                         pinToPres={emptyFunction}
                         childFilters={returnEmptyFilter}
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index a69a5f5e8..456b753b4 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -60,18 +60,17 @@ export class PreviewCursor extends ObservableReactComponent<{}> {
                 } else if (re.test(plain)) {
                     const url = plain;
                     if (!url.startsWith(window.location.href)) {
-                        undoBatch(
-                            () =>
-                                this._addDocument?.(
-                                    Docs.Create.WebDocument(url, {
-                                        title: url,
-                                        _width: 500,
-                                        _height: 300,
-                                        data_useCors: true,
-                                        x: newPoint[0],
-                                        y: newPoint[1],
-                                    })
-                                )
+                        undoBatch(() =>
+                            this._addDocument?.(
+                                Docs.Create.WebDocument(url, {
+                                    title: url,
+                                    _width: 500,
+                                    _height: 300,
+                                    data_useCors: true,
+                                    x: newPoint[0],
+                                    y: newPoint[1],
+                                })
+                            )
                         )();
                     } else alert('cannot paste dash into itself');
                 } else if (plain.startsWith('__DashDocId(') || plain.startsWith('__DashCloneId(')) {
@@ -86,7 +85,7 @@ export class PreviewCursor extends ObservableReactComponent<{}> {
                 } else {
                     FormattedTextBox.PasteOnLoad = e;
                     if (e.clipboardData.getData('dash/pdfAnchor')) e.preventDefault();
-                    UndoManager.RunInBatch(() => this._addLiveTextDoc?.(DocUtils.GetNewTextDoc('', newPoint[0], newPoint[1], 500, undefined, undefined, undefined, 750)), 'paste');
+                    UndoManager.RunInBatch(() => this._addLiveTextDoc?.(DocUtils.GetNewTextDoc('', newPoint[0], newPoint[1], 500, undefined, undefined, undefined)), 'paste');
                 }
             }
             //pasting in images
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 5905f8dbc..bba6285c2 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -345,9 +345,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     @undoBatch
     handlePerspectiveChange = (e: any) => {
         this.selectedDoc && (this.selectedDoc._type_collection = e.target.value);
-        SelectionManager.Views.filter(dv => dv.docView)
-            .map(dv => dv.docView!)
-            .forEach(docView => (docView.layoutDoc._type_collection = e.target.value));
+        SelectionManager.Views.forEach(docView => (docView.layoutDoc._type_collection = e.target.value));
     };
     @computed get onClickVal() {
         const linkButton = IsFollowLinkScript(this.selectedDoc.onClick);
@@ -408,28 +406,26 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     @undoBatch
     @action
     handleOptionChange = (onClick: string) => {
-        SelectionManager.Views.filter(dv => dv.docView)
-            .map(dv => dv.docView!)
-            .forEach(docView => {
-                const linkButton = IsFollowLinkScript(docView.Document.onClick);
-                docView.noOnClick();
-                switch (onClick) {
-                    case 'enterPortal':
-                        docView.makeIntoPortal();
-                        break;
-                    case 'toggleDetail':
-                        docView.setToggleDetail();
-                        break;
-                    case 'linkInPlace':
-                        docView.toggleFollowLink(false, false);
-                        docView.Document.followLinkLocation = linkButton ? OpenWhere.lightbox : undefined;
-                        break;
-                    case 'linkOnRight':
-                        docView.toggleFollowLink(false, false);
-                        docView.Document.followLinkLocation = linkButton ? OpenWhere.addRight : undefined;
-                        break;
-                }
-            });
+        SelectionManager.Views.forEach(docView => {
+            const linkButton = IsFollowLinkScript(docView.Document.onClick);
+            docView.noOnClick();
+            switch (onClick) {
+                case 'enterPortal':
+                    docView.makeIntoPortal();
+                    break;
+                case 'toggleDetail':
+                    docView.setToggleDetail();
+                    break;
+                case 'linkInPlace':
+                    docView.toggleFollowLink(false, false);
+                    docView.Document.followLinkLocation = linkButton ? OpenWhere.lightbox : undefined;
+                    break;
+                case 'linkOnRight':
+                    docView.toggleFollowLink(false, false);
+                    docView.Document.followLinkLocation = linkButton ? OpenWhere.addRight : undefined;
+                    break;
+            }
+        });
     };
 
     @undoBatch
diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx
index a710e7816..54f141a36 100644
--- a/src/client/views/PropertiesDocContextSelector.tsx
+++ b/src/client/views/PropertiesDocContextSelector.tsx
@@ -27,7 +27,7 @@ export class PropertiesDocContextSelector extends ObservableReactComponent<Prope
     @computed get _docs() {
         if (!this._props.DocView) return [];
         const target = this._props.DocView._props.Document;
-        const targetContext = this._props.DocView._props.docViewPath().lastElement()?.Document;
+        const targetContext = this._props.DocView.containerViewPath?.().lastElement()?.Document;
         const embeddings = DocListCast(target.proto_embeddings);
         const containerProtos = embeddings.filter(embedding => embedding.embedContainer && embedding.embedContainer instanceof Doc).reduce((set, embedding) => set.add(Cast(embedding.embedContainer, Doc, null)), new Set<Doc>());
         const containerSets = Array.from(containerProtos.keys()).map(container => DocListCast(container.proto_embeddings));
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 2fcb5d12a..13057ffbf 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -299,7 +299,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
                         renderDepth={1}
                         fitContentsToBox={returnTrue}
                         styleProvider={DefaultStyleProvider}
-                        docViewPath={returnEmptyDoclist}
+                        containerViewPath={returnEmptyDoclist}
                         dontCenter={'y'}
                         isDocumentActive={returnFalse}
                         isContentActive={emptyFunction}
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index d077543f1..572971391 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -20,7 +20,7 @@ import { SnappingManager } from '../util/SnappingManager';
 import { undoBatch, UndoManager } from '../util/UndoManager';
 import { TreeSort } from './collections/TreeSort';
 import { Colors } from './global/globalEnums';
-import { DocumentViewInternalProps, DocumentViewProps } from './nodes/DocumentView';
+import { DocumentViewProps } from './nodes/DocumentView';
 import { FieldViewProps } from './nodes/FieldView';
 import { KeyValueBox } from './nodes/KeyValueBox';
 import { PropertiesView } from './PropertiesView';
@@ -80,12 +80,14 @@ export function wavyBorderPath(pw: number, ph: number, inset: number = 0.05) {
 
 // a preliminary implementation of a dash style sheet for setting rendering properties of documents nested within a Tab
 //
-export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewInternalProps | FieldViewProps>, property: string): any {
+export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string): any {
     const remoteDocHeader = 'author;author_date;noMargin';
     const docProps = testDocProps(props) ? props : undefined;
     const fieldProps = testFieldProps(props) ? props : undefined;
     const isCaption = property.includes(':caption');
     const isAnchor = property.includes(':anchor');
+    const isNonTransparent = property.includes(':nonTransparent');
+    const isNonTransparentLevel = isNonTransparent ? Number(property.replace(/.*:nonTransparent([0-9]+).*/, '$1')) : 0; // property.includes(':nonTransparent');
     const isContent = property.includes(':content');
     const isAnnotated = property.includes(':annotated');
     const isInk = () => doc?._layout_isSvg && !docProps?.LayoutTemplateString;
@@ -95,7 +97,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewInter
     const fieldKey = fieldProps?.fieldKey ? fieldProps.fieldKey + '_' : isCaption ? 'caption_' : '';
     const lockedPosition = () => doc && BoolCast(doc._lockedPosition);
     const titleHeight = () => props?.styleProvider?.(doc, props, StyleProp.TitleHeight);
-    const backgroundCol = () => props?.styleProvider?.(doc, props, StyleProp.BackgroundColor);
+    const backgroundCol = () => props?.styleProvider?.(doc, props, StyleProp.BackgroundColor + ':nonTransparent' + (isNonTransparentLevel + 1));
     const color = () => props?.styleProvider?.(doc, props, StyleProp.Color);
     const opacity = () => props?.styleProvider?.(doc, props, StyleProp.Opacity);
     const layout_showTitle = () => props?.styleProvider?.(doc, props, StyleProp.ShowTitle);
@@ -140,7 +142,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewInter
         case StyleProp.FontFamily: return StrCast(doc?.[fieldKey + 'fontFamily'], StrCast(doc?._text_fontFamily, StrCast(Doc.UserDoc().fontFamily)));
         case StyleProp.FontWeight: return StrCast(doc?.[fieldKey + 'fontWeight'], StrCast(doc?._text_fontWeight, StrCast(Doc.UserDoc().fontWeight)));
         case StyleProp.FillColor:  return StrCast(doc?._fillColor, StrCast(doc?.fillColor, 'transparent'));
-        case StyleProp.ShowCaption:return doc?._type_collection === CollectionViewType.Carousel || props?.hideCaptions ? undefined : StrCast(doc?._layout_showCaption);
+        case StyleProp.ShowCaption:return doc?._type_collection === CollectionViewType.Carousel ? undefined: StrCast(doc?._layout_showCaption);
         case StyleProp.TitleHeight:
             return (props?.DocumentView?.().screenToViewTransform().Scale ?? 1) * NumCast(Doc.UserDoc().headerHeight,30)
         case StyleProp.ShowTitle:
@@ -167,8 +169,8 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewInter
             if (doc?.type === DocumentType.FONTICON) return SettingsManager.userColor;
             const docColor: Opt<string> = StrCast(doc?.[fieldKey + 'color'], StrCast(doc?._color));
             if (docColor) return docColor;
-            const docView = props?.DocumentView?.();
-            const backColor = backgroundCol() || docView?._props.styleProvider?.(docView._props.treeViewDoc, docView.docView?._props, StyleProp.BackgroundColor);
+            const parView = props?.DocumentView?.();
+            const backColor = backgroundCol();
             return backColor ? lightOrDark(backColor) : undefined;
         case StyleProp.BorderRounding:
             return StrCast(doc?.[fieldKey + 'borderRounding'], StrCast(doc?.layout_borderRounding, doc?._type_collection === CollectionViewType.Pile ? '50%' : ''));
@@ -198,18 +200,18 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewInter
                 : 0;
         case StyleProp.BackgroundColor: {
             if (SettingsManager.Instance.LastPressedBtn === doc) return SettingsManager.userColor; // hack to indicate active menu panel item
-            let docColor: Opt<string> = StrCast(doc?.[fieldKey + '_backgroundColor'], StrCast(doc?._backgroundColor, isCaption ? 'rgba(0,0,0,0.4)' : ''));
+            let docColor: Opt<string> = StrCast(doc?.[fieldKey + 'backgroundColor'], StrCast(doc?._backgroundColor, isCaption ? 'rgba(0,0,0,0.4)' : ''));
             // prettier-ignore
             switch (doc?.type) {
                 case DocumentType.PRESELEMENT: docColor = docColor || ""; break;
                 case DocumentType.PRES:        docColor = docColor || 'transparent'; break;
                 case DocumentType.FONTICON:    docColor = boxBackground ? undefined : docColor || Colors.DARK_GRAY; break;
-                case DocumentType.RTF:         docColor = docColor ||  Colors.LIGHT_GRAY; break;
+                case DocumentType.RTF:         docColor = docColor || Colors.LIGHT_GRAY; break;
                 case DocumentType.LINK:        docColor = (isAnchor ? docColor : undefined); break;
                 case DocumentType.INK:         docColor = doc?.stroke_isInkMask ? 'rgba(0,0,0,0.7)' : undefined; break;
                 case DocumentType.EQUATION:    docColor = docColor || 'transparent'; break;
                 case DocumentType.LABEL:       docColor = docColor || Colors.LIGHT_GRAY; break;
-                case DocumentType.BUTTON:      docColor = docColor ||  Colors.LIGHT_GRAY; break;
+                case DocumentType.BUTTON:      docColor = docColor || Colors.LIGHT_GRAY; break;
                 case DocumentType.IMG:
                 case DocumentType.WEB:
                 case DocumentType.PDF:
@@ -230,6 +232,9 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewInter
                 //if (doc._type_collection !== CollectionViewType.Freeform && doc._type_collection !== CollectionViewType.Time) return "rgb(62,62,62)";
                 default: docColor = docColor || (Colors.WHITE);
             }
+            if (isNonTransparent && isNonTransparentLevel < 9 && (!docColor || docColor === 'transparent') && doc?.embedContainer && props?.styleProvider) {
+                return props.styleProvider(DocCast(doc.embedContainer), props, StyleProp.BackgroundColor+":nonTransparent"+(isNonTransparentLevel+1));
+            }
             return (docColor && !doc) ? DashColor(docColor).fade(0.5).toString() : docColor;
         }
         case StyleProp.BoxShadow: {
@@ -252,7 +257,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewInter
                 default:
                     return doc.z
                         ? `#9c9396  ${StrCast(doc?.layout_boxShadow, '10px 10px 0.9vw')}` // if it's a floating doc, give it a big shadow
-                        : props?.docViewPath().lastElement()?.Document._freeform_useClusters
+                        : props?.DocumentView?.().containerViewPath?.().lastElement()?.Document._freeform_useClusters
                         ? `${backgroundCol()} ${StrCast(doc.layout_boxShadow, `0vw 0vw ${(lockedPosition() ? 100 : 50) / (docProps?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent
                         : NumCast(doc.group, -1) !== -1
                         ? `gray ${StrCast(doc.layout_boxShadow, `0vw 0vw ${(lockedPosition() ? 100 : 50) / (docProps?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent
@@ -277,7 +282,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewInter
             return undefined; // fixes problem with tree view elements getting pointer events when the tree view is not active
         case StyleProp.Decorations:
             const lock = () => {
-                if (props?.docViewPath().lastElement()?.Document?._type_collection === CollectionViewType.Freeform) {
+                if (props?.DocumentView?.().containerViewPath?.().lastElement()?.Document?._type_collection === CollectionViewType.Freeform) {
                     return doc?.pointerEvents !== 'none' ? null : (
                         <div className="styleProvider-lock" onClick={() => toggleLockedPosition(doc)}>
                             <FontAwesomeIcon icon='lock' size="lg" />
@@ -321,7 +326,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewInter
                                 "this view inherits filters from one of its parents"}
                             color={SettingsManager.userColor}
                             background={showFilterIcon}
-                            items={[ ...(dashView ? [dashView]: []), ...(props?.docViewPath?.()??[]), ...(props?.DocumentView?[props?.DocumentView?.()]:[])]
+                            items={[ ...(dashView ? [dashView]: []), ...(props?.DocumentView?.().docViewPath??[])]
                                     .filter(dv => StrListCast(dv?.Document.childFilters).length || StrListCast(dv?.Document.childRangeFilters).length)
                                     .map(dv => ({
                                 text: StrCast(dv?.Document.title),
@@ -339,7 +344,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewInter
                 const audioIconColors: { [key: string]: string } = { recording: 'red', playing: 'green', stopped: 'blue' };
                 return (
                     <Tooltip title={<div>{StrListCast(doc[Doc.LayoutFieldKey(doc) + '_audioAnnotations_text']).lastElement()}</div>}>
-                        <div className="styleProvider-audio" onPointerDown={() => DocumentManager.Instance.getFirstDocumentView(doc)?.docView?.playAnnotation()}>
+                        <div className="styleProvider-audio" onPointerDown={() => DocumentManager.Instance.getFirstDocumentView(doc)?.playAnnotation()}>
                             <FontAwesomeIcon className="documentView-audioFont" style={{ color: audioIconColors[audioAnnoState(doc)] }} icon={'file-audio'} size="sm" />
                         </div>
                     </Tooltip>
@@ -374,7 +379,7 @@ export function DashboardToggleButton(doc: Doc, field: string, onIcon: IconProp,
 /**
  * add hide button decorations for the "Dashboards" flyout TreeView
  */
-export function DashboardStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps | DocumentViewInternalProps>, property: string) {
+export function DashboardStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string) {
     if (doc && property.split(':')[0] === StyleProp.Decorations) {
         return doc._type_collection === CollectionViewType.Docking || Doc.IsSystem(doc)
             ? null
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index b42f3c1e9..e5154efcb 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -114,8 +114,6 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
                 <CollectionTreeView
                     Document={Doc.MyTemplates}
                     styleProvider={DefaultStyleProvider}
-                    setHeight={returnFalse}
-                    docViewPath={returnEmptyDoclist}
                     childFilters={returnEmptyFilter}
                     childFiltersByRanges={returnEmptyFilter}
                     searchFilterDocs={returnEmptyDoclist}
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index a125f1356..208fc45b5 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -7,7 +7,7 @@ import { Doc, Opt } from '../../../fields/Doc';
 import { DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { DragManager } from '../../util/DragManager';
 import { StyleProp } from '../StyleProvider';
-import { DocumentView, DocumentViewInternalProps, DocumentViewProps } from '../nodes/DocumentView';
+import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import './CollectionCarouselView.scss';
 import { CollectionSubView } from './CollectionSubView';
@@ -41,7 +41,7 @@ export class CollectionCarouselView extends CollectionSubView() {
         e.stopPropagation();
         this.layoutDoc._carousel_index = (NumCast(this.layoutDoc._carousel_index) - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length;
     };
-    captionStyleProvider = (doc: Doc | undefined, captionProps: Opt<DocumentViewInternalProps | FieldViewProps>, property: string): any => {
+    captionStyleProvider = (doc: Doc | undefined, captionProps: Opt<FieldViewProps>, property: string): any => {
         // first look for properties on the document in the carousel, then fallback to properties on the container
         const childValue = doc?.['caption-' + property] ? this._props.styleProvider?.(doc, captionProps, property) : undefined;
         return childValue ?? this._props.styleProvider?.(this.layoutDoc, captionProps, property);
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 874cdffd9..1f867fc44 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -431,7 +431,7 @@ export class CollectionDockingView extends CollectionSubView() {
     };
 
     public CaptureThumbnail() {
-        const content = this._props.DocumentView?.()?.ContentDiv;
+        const content = this.DocumentView?.()?.ContentDiv;
         if (content) {
             const _width = Number(getComputedStyle(content).width.replace('px', ''));
             const _height = Number(getComputedStyle(content).height.replace('px', ''));
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 98ae01591..a54a5ec81 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -97,14 +97,12 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
                     Document={selDoc}
                     fieldKey="data"
                     dropAction="embed"
-                    setHeight={returnFalse}
                     styleProvider={DefaultStyleProvider}
                     bringToFront={emptyFunction}
                     select={emptyFunction}
                     isContentActive={returnTrue}
                     isAnyChildContentActive={returnFalse}
                     isSelected={returnFalse}
-                    docViewPath={returnEmptyDoclist}
                     moveDocument={returnFalse}
                     addDocument={returnFalse}
                     addDocTab={DocumentViewInternal.addDocTabFunc}
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index 302ccd2db..0795104d9 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -18,7 +18,7 @@ import { undoBatch } from '../../util/UndoManager';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
 import { LightboxView } from '../LightboxView';
-import { DocFocusOptions, DocumentView, DocumentViewInternalProps, DocumentViewProps } from '../nodes/DocumentView';
+import { DocFocusOptions, DocumentView, DocumentViewProps } from '../nodes/DocumentView';
 import { FieldViewProps } from '../nodes/FieldView';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { StyleProp } from '../StyleProvider';
@@ -203,7 +203,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
         }
     };
 
-    styleProvider = (doc: Doc | undefined, props: Opt<DocumentViewInternalProps | FieldViewProps>, property: string) => {
+    styleProvider = (doc: Doc | undefined, props: Opt<FieldViewProps>, property: string) => {
         switch (property) {
             case StyleProp.BoxShadow:
                 if (doc && DragManager.docsBeingDragged.includes(doc)) {
@@ -238,7 +238,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                 PanelWidth={width}
                 PanelHeight={height}
                 styleProvider={this.styleProvider}
-                docViewPath={this._props.docViewPath}
+                containerViewPath={this.docViewPathFunc}
                 layout_fitWidth={this._props.childLayoutFitWidth}
                 isContentActive={emptyFunction}
                 onKey={this.onKeyDown}
@@ -511,7 +511,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                             action((entries: any) => {
                                 if (this.layoutDoc._layout_autoHeight && ref && this.refList.length && !SnappingManager.IsDragging) {
                                     const height = this.headerMargin + Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace('px', '')))));
-                                    if (!LightboxView.IsLightboxDocView(this._props.docViewPath())) {
+                                    if (!LightboxView.IsLightboxDocView(this.docViewPath)) {
                                         this._props.setHeight?.(height);
                                     }
                                 }
@@ -643,7 +643,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                 onDragOver={e => this.onPointerMove(true, e.clientX, e.clientY)}
                 onDrop={this.onExternalDrop.bind(this)}
                 onContextMenu={this.onContextMenu}
-                onWheel={e => this._props.isContentActive(true) && e.stopPropagation()}>
+                onWheel={e => this._props.isContentActive() && e.stopPropagation()}>
                 {this.renderedSections}
             </div>
         );
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index b1d379631..d0df77cbe 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -48,7 +48,7 @@ export class CollectionPileView extends CollectionSubView() {
     removePileDoc = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => {
         (doc instanceof Doc ? [doc] : doc).forEach(d => Doc.deiconifyView(d));
         const ret = this._props.moveDocument?.(doc, targetCollection, addDoc) || false;
-        if (ret && !DocListCast(this.dataDoc[this.fieldKey ?? 'data']).length) this._props.DocumentView?.()._props.removeDocument?.(this.Document);
+        if (ret && !DocListCast(this.dataDoc[this.fieldKey ?? 'data']).length) this.DocumentView?.()._props.removeDocument?.(this.Document);
         return ret;
     };
 
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index 22a67c501..1239a038a 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -23,7 +23,7 @@ import { undoBatch, UndoManager } from '../../util/UndoManager';
 import { CollectionSubView } from '../collections/CollectionSubView';
 import { LightboxView } from '../LightboxView';
 import { AudioWaveform } from '../nodes/audio/AudioWaveform';
-import { DocFocusFunc, DocFocusOptions, DocumentView, DocumentViewInternalProps, DocumentViewProps, OpenWhere, StyleProviderFunc } from '../nodes/DocumentView';
+import { DocFocusFunc, DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere, StyleProviderFunc } from '../nodes/DocumentView';
 import { LabelBox } from '../nodes/LabelBox';
 import { VideoBox } from '../nodes/VideoBox';
 import { ObservableReactComponent } from '../ObservableReactComponent';
@@ -825,7 +825,7 @@ class StackedTimelineAnchor extends ObservableReactComponent<StackedTimelineAnch
                     ref={action((r: DocumentView | null) => (anchor.view = r))}
                     Document={mark}
                     TemplateDataDocument={undefined}
-                    docViewPath={returnEmptyDoclist}
+                    containerViewPath={returnEmptyDoclist}
                     pointerEvents={this.noEvents ? returnNone : undefined}
                     styleProvider={this._props.styleProvider}
                     renderDepth={this._props.renderDepth + 1}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 995f071ca..11ca62b39 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -24,7 +24,7 @@ import { ContextMenuProps } from '../ContextMenuItem';
 import { EditableView } from '../EditableView';
 import { LightboxView } from '../LightboxView';
 import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView';
-import { DocFocusOptions, DocumentView, DocumentViewInternalProps, DocumentViewProps } from '../nodes/DocumentView';
+import { DocFocusOptions, DocumentView, DocumentViewProps } from '../nodes/DocumentView';
 import { FieldViewProps } from '../nodes/FieldView';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { StyleProp } from '../StyleProvider';
@@ -265,7 +265,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         return undefined;
     };
 
-    styleProvider = (doc: Doc | undefined, props: Opt<DocumentViewInternalProps | FieldViewProps>, property: string) => {
+    styleProvider = (doc: Doc | undefined, props: Opt<FieldViewProps>, property: string) => {
         if (property === StyleProp.Opacity && doc) {
             if (this._props.childOpacity) {
                 return this._props.childOpacity();
@@ -325,9 +325,9 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                 renderDepth={this._props.renderDepth + 1}
                 PanelWidth={panelWidth}
                 PanelHeight={panelHeight}
-                pointerEvents={this._props.DocumentView?.()._props.onClick?.() ? returnNone : undefined} // if the stack has an onClick, then we don't want the contents to be interactive (see CollectionPileView)
+                pointerEvents={this.DocumentView?.()._props.onClick?.() ? returnNone : undefined} // if the stack has an onClick, then we don't want the contents to be interactive (see CollectionPileView)
                 styleProvider={this.styleProvider}
-                docViewPath={this._props.docViewPath}
+                containerViewPath={this.docViewPathFunc}
                 layout_fitWidth={this.childFitWidth}
                 isContentActive={doc.onClick ? this.isChildButtonContentActive : this.isChildContentActive}
                 onKey={this.onKeyDown}
@@ -540,7 +540,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                             action((entries: any) => {
                                 if (this.layoutDoc._layout_autoHeight && ref && this.refList.length && !SnappingManager.IsDragging) {
                                     const height = this.headerMargin + Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace('px', '')))));
-                                    if (!LightboxView.IsLightboxDocView(this._props.docViewPath())) {
+                                    if (!LightboxView.IsLightboxDocView(this.docViewPath)) {
                                         this._props.setHeight?.(height);
                                     }
                                 }
@@ -681,7 +681,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                     renderDepth={this._props.renderDepth}
                     focus={emptyFunction}
                     styleProvider={this._props.styleProvider}
-                    docViewPath={returnEmptyDoclist}
+                    containerViewPath={returnEmptyDoclist}
                     whenChildContentsActiveChanged={emptyFunction}
                     bringToFront={emptyFunction}
                     childFilters={this._props.childFilters}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 570330174..2e0a75281 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -21,7 +21,7 @@ import { ImageUtils } from '../../util/Import & Export/ImageUtils';
 import { SelectionManager } from '../../util/SelectionManager';
 import { SnappingManager } from '../../util/SnappingManager';
 import { UndoManager, undoBatch } from '../../util/UndoManager';
-import { ViewBoxBaseComponent } from '../DocComponent';
+import { ViewBoxBaseComponent, ViewBoxBaseProps } from '../DocComponent';
 import { LoadingBox } from '../nodes/LoadingBox';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { CollectionView, CollectionViewProps } from './CollectionView';
@@ -31,7 +31,7 @@ export interface SubCollectionViewProps extends CollectionViewProps {
 }
 
 export function CollectionSubView<X>(moreProps?: X) {
-    class CollectionSubView extends ViewBoxBaseComponent<X & SubCollectionViewProps>() {
+    class CollectionSubView extends ViewBoxBaseComponent<X & SubCollectionViewProps & ViewBoxBaseProps>() {
         private dropDisposer?: DragManager.DragDropDisposer;
         private gestureDisposer?: GestureUtils.GestureEventDisposer;
         protected _mainCont?: HTMLDivElement;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 18e0b98ef..76b934802 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -169,7 +169,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
             this._props.removeDocument?.(doc);
             if (ind > 0 && prev) {
                 FormattedTextBox.SetSelectOnLoad(prev);
-                DocumentManager.Instance.getDocumentView(prev, this._props.DocumentView?.())?.select(false);
+                DocumentManager.Instance.getDocumentView(prev, this.DocumentView?.())?.select(false);
             }
             return true;
         }
@@ -347,7 +347,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                     renderDepth={this._props.renderDepth + 1}
                     focus={emptyFunction}
                     styleProvider={this._props.styleProvider}
-                    docViewPath={returnEmptyDoclist}
+                    containerViewPath={returnEmptyDoclist}
                     whenChildContentsActiveChanged={emptyFunction}
                     bringToFront={emptyFunction}
                     childFilters={this._props.childFilters}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 4a239e4b1..7c9ef9f48 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -232,13 +232,9 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
     childLayoutTemplate = () => this._props.childLayoutTemplate?.() || Cast(this.Document.childLayoutTemplate, Doc, null);
     isContentActive = (outsideReaction?: boolean) => this._isContentActive;
 
-    pointerEvents = () => {
-        const viewPath = this._props.DocumentView?.()?._props.docViewPath();
-        return (
-            this.layoutDoc._lockedPosition && //
-            viewPath?.lastElement()?.Document?._type_collection === CollectionViewType.Freeform
-        );
-    };
+    pointerEvents = () =>
+        this.layoutDoc._lockedPosition && //
+        this.Document?._type_collection === CollectionViewType.Freeform;
 
     render() {
         TraceMobx();
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 80808be92..2441c67e6 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -29,7 +29,7 @@ import { LightboxView } from '../LightboxView';
 import { ObservableReactComponent } from '../ObservableReactComponent';
 import { DefaultStyleProvider, StyleProp } from '../StyleProvider';
 import { Colors } from '../global/globalEnums';
-import { DocFocusOptions, DocumentView, DocumentViewInternalProps, DocumentViewProps, OpenWhere, OpenWhereMod } from '../nodes/DocumentView';
+import { DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere, OpenWhereMod } from '../nodes/DocumentView';
 import { KeyValueBox } from '../nodes/KeyValueBox';
 import { DashFieldView } from '../nodes/formattedText/DashFieldView';
 import { PinProps, PresBox, PresMovement } from '../nodes/trails';
@@ -469,7 +469,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
                     dontCenter={'y'}
                     whenChildContentsActiveChanged={this.whenChildContentActiveChanges}
                     focus={this.focusFunc}
-                    docViewPath={returnEmptyDoclist}
+                    containerViewPath={returnEmptyDoclist}
                     bringToFront={emptyFunction}
                     pinToPres={TabDocView.PinDoc}
                 />
@@ -528,7 +528,7 @@ class TabMiniThumb extends React.Component<TabMiniThumbProps> {
 }
 @observer
 export class TabMinimapView extends ObservableReactComponent<TabMinimapViewProps> {
-    static miniStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewInternalProps | FieldViewProps>, property: string): any => {
+    static miniStyleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string): any => {
         if (doc) {
             switch (property.split(':')[0]) {
                 default:
@@ -595,10 +595,9 @@ export class TabMinimapView extends ObservableReactComponent<TabMinimapViewProps
             <div className="miniMap" style={{ width: miniSize, height: miniSize, background: this._props.background() }}>
                 <CollectionFreeFormView
                     Document={this._props.document}
-                    docViewPath={returnEmptyDoclist}
+                    containerViewPath={returnEmptyDoclist}
                     childLayoutTemplate={this.childLayoutTemplate} // bcz: Ugh .. should probably be rendering a CollectionView or the minimap should be part of the collectionFreeFormView to avoid having to set stuff like this.
                     noOverlay={true} // don't render overlay Docs since they won't scale
-                    setHeight={returnFalse}
                     isContentActive={emptyFunction}
                     isAnyChildContentActive={returnFalse}
                     select={emptyFunction}
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 01b80e209..205d536de 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -27,7 +27,7 @@ import { UndoManager, undoBatch, undoable } from '../../util/UndoManager';
 import { EditableView } from '../EditableView';
 import { ObservableReactComponent } from '../ObservableReactComponent';
 import { StyleProp } from '../StyleProvider';
-import { DocumentView, DocumentViewInternal, DocumentViewInternalProps, DocumentViewProps, OpenWhere, StyleProviderFunc } from '../nodes/DocumentView';
+import { DocumentView, DocumentViewInternal, OpenWhere, StyleProviderFunc } from '../nodes/DocumentView';
 import { FieldViewProps } from '../nodes/FieldView';
 import { KeyValueBox } from '../nodes/KeyValueBox';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
@@ -198,7 +198,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
         const ind = DocListCast(this.dataDoc[key]).indexOf(doc instanceof Doc ? doc : doc.lastElement());
 
         const res = (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.RemoveDocFromList(this.dataDoc, key, doc), true);
-        res && ind > 0 && DocumentManager.Instance.getDocumentView(DocListCast(this.dataDoc[key])[ind - 1], this.treeView._props.DocumentView?.())?.select(false);
+        res && ind > 0 && DocumentManager.Instance.getDocumentView(DocListCast(this.dataDoc[key])[ind - 1], this.treeView.DocumentView?.())?.select(false);
         return res;
     };
 
@@ -867,7 +867,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
             e.preventDefault();
         }
     };
-    titleStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewInternalProps | FieldViewProps>, property: string): any => {
+    titleStyleProvider = (doc: Doc | undefined, props: Opt<FieldViewProps>, property: string): any => {
         if (!doc || doc !== this.Document) return this._props?.treeView?._props.styleProvider?.(doc, props, property); // properties are inherited from the CollectionTreeView, not the hierarchical parent in the treeView
 
         const treeView = this.treeView;
@@ -899,7 +899,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
         }
         return treeView._props.styleProvider?.(doc, props, property);
     };
-    embeddedStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewInternalProps | FieldViewProps>, property: string): any => {
+    embeddedStyleProvider = (doc: Doc | undefined, props: Opt<FieldViewProps>, property: string): any => {
         if (property.startsWith(StyleProp.Decorations)) return null;
         return this._props?.treeView?._props.styleProvider?.(doc, props, property); // properties are inherited from the CollectionTreeView, not the hierarchical parent in the treeView
     };
@@ -977,7 +977,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
                 hideResizeHandles={this.treeView.outlineMode}
                 styleProvider={this.titleStyleProvider}
                 onClickScriptDisable="never" // tree docViews have a script to show fields, etc.
-                docViewPath={this.treeView._props.docViewPath}
+                containerViewPath={this.treeView.docViewPathFunc}
                 treeViewDoc={this.treeView.Document}
                 addDocument={undefined}
                 addDocTab={this._props.addDocTab}
@@ -1084,7 +1084,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
                     ScreenToLocalTransform={this.docTransform}
                     renderDepth={this._props.renderDepth + 1}
                     treeViewDoc={this.treeView?.Document}
-                    docViewPath={this.treeView._props.docViewPath}
+                    containerViewPath={this.treeView.docViewPathFunc}
                     childFilters={returnEmptyFilter}
                     childFiltersByRanges={returnEmptyFilter}
                     searchFilterDocs={returnEmptyDoclist}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 8268a47d8..a8f22aaae 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -38,7 +38,7 @@ import { ActiveInkWidth, InkingStroke, SetActiveInkColor, SetActiveInkWidth } fr
 import { LightboxView } from '../../LightboxView';
 import { CollectionFreeFormDocumentView, CollectionFreeFormDocumentViewWrapper } from '../../nodes/CollectionFreeFormDocumentView';
 import { SchemaCSVPopUp } from '../../nodes/DataVizBox/SchemaCSVPopUp';
-import { DocFocusOptions, DocumentView, DocumentViewInternalProps, DocumentViewProps, OpenWhere } from '../../nodes/DocumentView';
+import { DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere } from '../../nodes/DocumentView';
 import { FieldViewProps } from '../../nodes/FieldView';
 import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
 import { PinProps, PresBox } from '../../nodes/trails/PresBox';
@@ -162,7 +162,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         return this._props.isAnnotationOverlay || this._props.originTopLeft ? 0 : this._props.PanelWidth() / 2 / scaling; // shift so pan position is at center of window for non-overlay collections
     }
     @computed get cachedCenteringShiftY(): number {
-        const dv = this._props.DocumentView?.();
+        const dv = this.DocumentView?.();
         const fitWidth = this._props.layout_fitWidth?.(this.Document) ?? dv?.layoutDoc.layout_fitWidth;
         const scaling = !this.nativeDimScaling ? 1 : this.nativeDimScaling;
         // if freeform has a native aspect, then the panel height needs to be adjusted to match it
@@ -250,7 +250,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     };
     selectDocuments = (docs: Doc[]) => {
         SelectionManager.DeselectAll();
-        docs.map(doc => DocumentManager.Instance.getDocumentView(doc, this._props.DocumentView?.())).forEach(dv => dv && SelectionManager.SelectView(dv, true));
+        docs.map(doc => DocumentManager.Instance.getDocumentView(doc, this.DocumentView?.())).forEach(dv => dv && SelectionManager.SelectView(dv, true));
     };
     addDocument = (newBox: Doc | Doc[]) => {
         let retVal = false;
@@ -409,7 +409,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @undoBatch
     internalLinkDrop(e: Event, de: DragManager.DropEvent, linkDragData: DragManager.LinkDragData) {
-        if (linkDragData.linkDragView.props.docViewPath().includes(this._props.docViewPath().lastElement())) {
+        if (this.DocumentView?.() && linkDragData.linkDragView.containerViewPath?.().includes(this.DocumentView())) {
             const [x, y] = this.screenToFreeformContentsXf.transformPoint(de.x, de.y);
             let added = false;
             //  do nothing if link is dropped into any freeform view parent of dragged document
@@ -485,7 +485,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             const ptsParent = e;
             if (ptsParent) {
                 const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this._props.Document._freeform_useClusters ? NumCast(cd.layout_cluster) : NumCast(cd.group, -1)) === cluster);
-                const clusterDocs = eles.map(ele => DocumentManager.Instance.getDocumentView(ele, this._props.DocumentView?.())!);
+                const clusterDocs = eles.map(ele => DocumentManager.Instance.getDocumentView(ele, this.DocumentView?.())!);
                 const { left, top } = clusterDocs[0].getBounds() || { left: 0, top: 0 };
                 const de = new DragManager.DocumentDragData(eles, e.ctrlKey || e.altKey ? 'embed' : undefined);
                 de.moveDocument = this._props.moveDocument;
@@ -586,7 +586,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         }
     };
 
-    clusterStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewInternalProps | FieldViewProps>, property: string) => {
+    clusterStyleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string) => {
         let styleProp = this._props.styleProvider?.(doc, props, property); // bcz: check 'props'  used to be renderDepth + 1
         if (doc && this.childDocList?.includes(doc))
             switch (property) {
@@ -629,7 +629,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         this._downY = this._lastY = e.pageY;
         this._downTime = Date.now();
         const scrollMode = e.altKey ? (Doc.UserDoc().freeformScrollMode === freeformScrollMode.Pan ? freeformScrollMode.Zoom : freeformScrollMode.Pan) : Doc.UserDoc().freeformScrollMode;
-        if (e.button === 0 && (!(e.ctrlKey && !e.metaKey) || scrollMode !== freeformScrollMode.Pan) && this._props.isContentActive(true)) {
+        if (e.button === 0 && (!(e.ctrlKey && !e.metaKey) || scrollMode !== freeformScrollMode.Pan) && this._props.isContentActive()) {
             if (!this.Document.isGroup) {
                 // group freeforms don't pan when dragged -- instead let the event go through to allow the group itself to drag
                 // prettier-ignore
@@ -704,7 +704,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     };
     @action
     onEraserUp = (e: PointerEvent): void => {
-        this._deleteList.forEach(ink => ink.props.removeDocument?.(ink.Document));
+        this._deleteList.forEach(ink => ink._props.removeDocument?.(ink.Document));
         this._deleteList = [];
         this._batch?.end();
     };
@@ -714,7 +714,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         if (this._lightboxDoc) this._lightboxDoc = undefined;
         if (Utils.isClick(e.pageX, e.pageY, this._downX, this._downY, this._downTime)) {
             if (this.onBrowseClickHandler()) {
-                this.onBrowseClickHandler().script.run({ documentView: this._props.DocumentView?.(), clientX: e.clientX, clientY: e.clientY });
+                this.onBrowseClickHandler().script.run({ documentView: this.DocumentView?.(), clientX: e.clientX, clientY: e.clientY });
                 e.stopPropagation();
                 e.preventDefault();
             } else if (this.isContentActive() && e.shiftKey) {
@@ -737,7 +737,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const ctrlKey = e.ctrlKey && !e.shiftKey;
         const shiftKey = e.shiftKey && !e.ctrlKey;
         PresBox.Instance?.pauseAutoPres();
-        this._props.DocumentView?.().clearViewTransition();
+        this.DocumentView?.().clearViewTransition();
         const [dxi, dyi] = this.screenToFreeformContentsXf.transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
         const { x: dx, y: dy } = Utils.rotPt(dxi, dyi, this.ScreenToLocalBoxXf().Rotate);
         this.setPan(NumCast(this.Document[this.panXFieldKey]) - (ctrlKey ? 0 : dx), NumCast(this.Document[this.panYFieldKey]) - (shiftKey ? 0 : dy), 0, true);
@@ -808,7 +808,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const eraserMax = { X: Math.max(lastPoint.X, currPoint.X), Y: Math.max(lastPoint.Y, currPoint.Y) };
 
         return this.childDocs
-            .map(doc => DocumentManager.Instance.getDocumentView(doc, this._props.DocumentView?.()))
+            .map(doc => DocumentManager.Instance.getDocumentView(doc, this.DocumentView?.()))
             .filter(inkView => inkView?.ComponentView instanceof InkingStroke)
             .map(inkView => ({ inkViewBounds: inkView!.getBounds(), inkStroke: inkView!.ComponentView as InkingStroke, inkView: inkView! }))
             .filter(
@@ -905,7 +905,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         this.childDocs
             .filter(doc => doc.type === DocumentType.INK && !doc.dontIntersect)
             .forEach(doc => {
-                const otherInk = DocumentManager.Instance.getDocumentView(doc, this._props.DocumentView?.())?.ComponentView as InkingStroke;
+                const otherInk = DocumentManager.Instance.getDocumentView(doc, this.DocumentView?.())?.ComponentView as InkingStroke;
                 const { inkData: otherInkData } = otherInk?.inkScaledData() ?? { inkData: [] };
                 const otherScreenPts = otherInkData.map(point => otherInk.ptToScreen(point));
                 const otherCtrlPts = otherScreenPts.map(spt => (ink.ComponentView as InkingStroke).ptFromScreen(spt));
@@ -979,7 +979,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                         freeformScrollMode.Zoom : freeformScrollMode.Pan // prettier-ignore
         ) {
             case freeformScrollMode.Pan:
-                if (((!e.metaKey && !e.altKey) || Doc.UserDoc().freeformScrollMode === freeformScrollMode.Zoom) && this._props.isContentActive(true)) {
+                if (((!e.metaKey && !e.altKey) || Doc.UserDoc().freeformScrollMode === freeformScrollMode.Zoom) && this._props.isContentActive()) {
                     const deltaX = e.shiftKey ? e.deltaX : e.ctrlKey ? 0 : e.deltaX;
                     const deltaY = e.shiftKey ? 0 : e.ctrlKey ? e.deltaY : e.deltaY;
                     this.scrollPan({ deltaX: -deltaX * this.screenToFreeformContentsXf.Scale, deltaY: e.shiftKey ? 0 : -deltaY * this.screenToFreeformContentsXf.Scale });
@@ -987,7 +987,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 }
             default:
             case freeformScrollMode.Zoom:
-                if ((e.ctrlKey || !scrollable) && this._props.isContentActive(true)) {
+                if ((e.ctrlKey || !scrollable) && this._props.isContentActive()) {
                     this.zoom(e.clientX, e.clientY, Math.max(-1, Math.min(1, e.deltaY))); // if (!this._props.isAnnotationOverlay) // bcz: do we want to zoom in on images/videos/etc?
                     e.preventDefault();
                 }
@@ -1065,7 +1065,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     nudge = (x: number, y: number, nudgeTime: number = 500) => {
-        const collectionDoc = this._props.docViewPath().lastElement().Document;
+        const collectionDoc = this.Document;
         if (collectionDoc?._type_collection !== CollectionViewType.Freeform) {
             this.setPan(
                 NumCast(this.layoutDoc[this.panXFieldKey]) + ((this._props.PanelWidth() / 2) * x) / this.zoomScaling(), // nudge x,y as a function of panel dimension and scale
@@ -1244,7 +1244,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 moveDocument={this._props.moveDocument}
                 pinToPres={this._props.pinToPres}
                 whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
-                docViewPath={this._props.docViewPath}
+                containerViewPath={this.docViewPathFunc}
                 styleProvider={this.clusterStyleProvider}
                 dragAction={(this.Document.childDragAction ?? this._props.childDragAction) as dropActionType}
                 bringToFront={this.bringToFront}
@@ -1540,7 +1540,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     updateIcon = () =>
         CollectionFreeFormView.UpdateIcon(
             this.layoutDoc[Id] + '-icon' + new Date().getTime(),
-            this._props.docViewPath().lastElement().ContentDiv!,
+            this.DocumentView?.().ContentDiv!,
             NumCast(this.layoutDoc._width),
             NumCast(this.layoutDoc._height),
             this._props.PanelWidth(),
@@ -1741,7 +1741,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     incrementalRendering = () => this.childDocs.filter(doc => !this._renderCutoffData.get(doc[Id])).length !== 0;
 
     incrementalRender = action(() => {
-        if (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this._props.docViewPath())) {
+        if (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.docViewPath)) {
             const layout_unrendered = this.childDocs.filter(doc => !this._renderCutoffData.get(doc[Id]));
             const loadIncrement = 5;
             for (var i = 0; i < Math.min(layout_unrendered.length, loadIncrement); i++) {
@@ -1755,7 +1755,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     // which will be a DocumentView.  In this sitation, this freeform views acts as an annotation overlay for
     // the underlying DocumentView and will pan and scoll with the underlying Documen tView.
     @computed get underlayViews() {
-        return this._props.children ? [toJS(this._props.children)] : [];
+        return this._props.children ? this._props.children : [];
     }
 
     @computed get placeholder() {
@@ -1798,7 +1798,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 brushedView={this.brushedView}
                 isAnnotationOverlay={this.isAnnotationOverlay}
                 transform={this.PanZoomCenterXf}
-                transition={this._panZoomTransition ? `transform ${this._panZoomTransition}ms` : Cast(this.layoutDoc._viewTransition, 'string', Cast(this._props.DocumentView?.()?.Document._viewTransition, 'string', null))}
+                transition={this._panZoomTransition ? `transform ${this._panZoomTransition}ms` : Cast(this.layoutDoc._viewTransition, 'string', Cast(this.Document._viewTransition, 'string', null))}
                 viewDefDivClick={this._props.viewDefDivClick}>
                 {this.underlayViews}
                 {this.contentViews}
@@ -1896,6 +1896,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                         <DocumentView
                             {...this._props}
                             Document={this._lightboxDoc}
+                            containerViewPath={this.docViewPathFunc}
                             TemplateDataDocument={undefined}
                             PanelWidth={this.lightboxPanelWidth}
                             PanelHeight={this.lightboxPanelHeight}
@@ -1942,15 +1943,15 @@ export function CollectionBrowseClick(dv: DocumentView, clientX: number, clientY
         DocumentManager.Instance.showDocument(dv.Document, { zoomScale: 0.8, willZoomCentered: true }, (focused: boolean) => {
             if (!focused) {
                 const selfFfview = !dv.Document.isGroup && dv.ComponentView instanceof CollectionFreeFormView ? dv.ComponentView : undefined;
-                let containers = dv.props.docViewPath();
+                let containers = dv.containerViewPath?.() ?? [];
                 let parFfview = dv.CollectionFreeFormView;
                 for (var cont of containers) {
                     parFfview = parFfview ?? cont.CollectionFreeFormView;
                 }
-                while (parFfview?.Document.isGroup) parFfview = parFfview.props.DocumentView?.().CollectionFreeFormView;
+                while (parFfview?.Document.isGroup) parFfview = parFfview.DocumentView?.().CollectionFreeFormView;
                 const ffview = selfFfview && selfFfview.layoutDoc[selfFfview.scaleFieldKey] !== 0.5 ? selfFfview : parFfview; // if focus doc is a freeform that is not at it's default 0.5 scale, then zoom out on it.  Otherwise, zoom out on the parent ffview
                 ffview?.zoomSmoothlyAboutPt(ffview.screenToFreeformContentsXf.transformPoint(clientX, clientY), ffview?.isAnnotationOverlay ? 1 : 0.5, browseTransitionTime);
-                Doc.linkFollowHighlight(dv?.props.Document, false);
+                Doc.linkFollowHighlight(dv?.Document, false);
             }
         });
 }
@@ -1968,13 +1969,13 @@ ScriptingGlobals.add(function curKeyFrame(readOnly: boolean) {
 });
 ScriptingGlobals.add(function pinWithView(pinContent: boolean) {
     SelectionManager.Views.forEach(view =>
-        view.props.pinToPres(view.Document, {
+        view._props.pinToPres(view.Document, {
             currentFrame: Cast(view.Document.currentFrame, 'number', null),
             pinData: {
                 poslayoutview: pinContent,
                 dataview: pinContent,
             },
-            pinViewport: MarqueeView.CurViewBounds(view.Document, view.props.PanelWidth(), view.props.PanelHeight()),
+            pinViewport: MarqueeView.CurViewBounds(view.Document, view._props.PanelWidth(), view._props.PanelHeight()),
         })
     );
 });
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index d105b04f7..54ade031b 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -147,8 +147,8 @@ export class CollectionLinearView extends CollectionSubView() {
         switch (doc.layout) {
             case '<LinkingUI>':          return this.getLinkUI();
             case '<CurrentlyPlayingUI>': return this.getCurrentlyPlayingUI();
-            case '<UndoStack>':          return <UndoStack />;
-            case '<Branching>':          return Doc.UserDoc().isBranchingMode  ? <BranchingTrailManager /> : null;
+            case '<UndoStack>':          return <UndoStack key={doc[Id]}/>;
+            case '<Branching>':          return Doc.UserDoc().isBranchingMode  ? <BranchingTrailManager key={doc[Id]} /> : null;
         }
 
         const nested = doc._type_collection === CollectionViewType.Linear;
@@ -189,7 +189,7 @@ export class CollectionLinearView extends CollectionSubView() {
                     dontRegisterView={BoolCast(this.Document.childDontRegisterViews)}
                     focus={emptyFunction}
                     styleProvider={this._props.styleProvider}
-                    docViewPath={returnEmptyDoclist}
+                    containerViewPath={this.docViewPathFunc}
                     whenChildContentsActiveChanged={emptyFunction}
                     bringToFront={emptyFunction}
                     childFilters={this._props.childFilters}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 563084af8..dd1305465 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -127,7 +127,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
     private get totalRatioAllocation(): number | undefined {
         const layoutInfoLen = this.resolvedLayoutInformation.widthSpecifiers.length;
         if (layoutInfoLen > 0 && this.totalFixedAllocation !== undefined) {
-            return this._props.PanelWidth() - (this.totalFixedAllocation + resizerWidth * (layoutInfoLen - 1)) - 2 * NumCast(this._props.Document._xMargin);
+            return this._props.PanelWidth() - (this.totalFixedAllocation + resizerWidth * (layoutInfoLen - 1)) - 2 * NumCast(this.Document._xMargin);
         }
     }
 
@@ -259,14 +259,14 @@ export class CollectionMulticolumnView extends CollectionSubView() {
                 Document={childLayout}
                 TemplateDataDocument={childLayout.resolvedDataDoc as Doc}
                 styleProvider={this._props.styleProvider}
-                docViewPath={this._props.docViewPath}
+                containerViewPath={this.docViewPathFunc}
                 LayoutTemplate={this._props.childLayoutTemplate}
                 LayoutTemplateString={this._props.childLayoutString}
                 renderDepth={this._props.renderDepth + 1}
                 PanelWidth={width}
                 PanelHeight={height}
                 rootSelected={this.rootSelected}
-                dragAction={(this._props.Document.childDragAction ?? this._props.childDragAction) as dropActionType}
+                dragAction={StrCast(this.Document.childDragAction, this._props.childDragAction) as dropActionType}
                 onClick={this.onChildClickHandler}
                 onDoubleClick={this.onChildDoubleClickHandler}
                 suppressSetHeight={true}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index bf0d39197..48c4173d6 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -122,7 +122,7 @@ export class CollectionMultirowView extends CollectionSubView() {
     private get totalRatioAllocation(): number | undefined {
         const layoutInfoLen = this.resolvedLayoutInformation.heightSpecifiers.length;
         if (layoutInfoLen > 0 && this.totalFixedAllocation !== undefined) {
-            return this._props.PanelHeight() - (this.totalFixedAllocation + resizerHeight * (layoutInfoLen - 1)) - 2 * NumCast(this._props.Document._yMargin);
+            return this._props.PanelHeight() - (this.totalFixedAllocation + resizerHeight * (layoutInfoLen - 1)) - 2 * NumCast(this.Document._yMargin);
         }
     }
 
@@ -254,14 +254,14 @@ export class CollectionMultirowView extends CollectionSubView() {
                 Document={layout}
                 TemplateDataDocument={layout.resolvedDataDoc as Doc}
                 styleProvider={this._props.styleProvider}
-                docViewPath={this._props.docViewPath}
+                containerViewPath={this.docViewPathFunc}
                 LayoutTemplate={this._props.childLayoutTemplate}
                 LayoutTemplateString={this._props.childLayoutString}
                 renderDepth={this._props.renderDepth + 1}
                 PanelWidth={width}
                 PanelHeight={height}
                 rootSelected={this.rootSelected}
-                dropAction={StrCast(this.Document.childDragAction) as dropActionType}
+                dragAction={StrCast(this.Document.childDragAction, this._props.childDragAction) as dropActionType}
                 onClick={this.onChildClickHandler}
                 onDoubleClick={this.onChildDoubleClickHandler}
                 ScreenToLocalTransform={dxf}
@@ -270,7 +270,6 @@ export class CollectionMultirowView extends CollectionSubView() {
                 hideResizeHandles={layout.layout_fitWidth || this._props.childHideResizeHandles ? true : false}
                 hideDecorationTitle={this._props.childHideDecorationTitle}
                 fitContentsToBox={this._props.fitContentsToBox}
-                dragAction={this._props.childDragAction}
                 focus={this._props.focus}
                 childFilters={this.childDocFilters}
                 childFiltersByRanges={this.childDocRangeFilters}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 492aed0ea..09352d7a4 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -16,7 +16,7 @@ import { undoable, undoBatch } from '../../../util/UndoManager';
 import { ContextMenu } from '../../ContextMenu';
 import { EditableView } from '../../EditableView';
 import { Colors } from '../../global/globalEnums';
-import { DocFocusOptions, DocumentView, DocumentViewInternalProps, DocumentViewProps } from '../../nodes/DocumentView';
+import { DocFocusOptions, DocumentView } from '../../nodes/DocumentView';
 import { KeyValueBox } from '../../nodes/KeyValueBox';
 import { ObservableReactComponent } from '../../ObservableReactComponent';
 import { DefaultStyleProvider, StyleProp } from '../../StyleProvider';
@@ -907,7 +907,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                                 childFiltersByRanges={this.childDocRangeFilters}
                                 searchFilterDocs={this.searchFilterDocs}
                                 styleProvider={DefaultStyleProvider}
-                                docViewPath={returnEmptyDoclist}
+                                containerViewPath={returnEmptyDoclist}
                                 moveDocument={this._props.moveDocument}
                                 addDocument={this.addRow}
                                 removeDocument={this._props.removeDocument}
@@ -962,7 +962,7 @@ class CollectionSchemaViewDoc extends ObservableReactComponent<CollectionSchemaV
 
     tableWidthFunc = () => this._props.schema.tableWidth;
     screenToLocalXf = () => this._props.schema.ScreenToLocalBoxXf().translate(0, -this._props.rowHeight() - this._props.index * this._props.rowHeight());
-    noOpacityStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewInternalProps | FieldViewProps>, property: string) => {
+    noOpacityStyleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string) => {
         if (property === StyleProp.Opacity) return 1;
         return DefaultStyleProvider(doc, props, property);
     };
@@ -971,6 +971,7 @@ class CollectionSchemaViewDoc extends ObservableReactComponent<CollectionSchemaV
             <DocumentView
                 key={this._props.doc[Id]}
                 {...this._props.schema._props}
+                containerViewPath={this._props.schema.docViewPathFunc}
                 LayoutTemplate={this._props.schema._props.childLayoutTemplate}
                 LayoutTemplateString={SchemaRowBox.LayoutString(this._props.schema._props.fieldKey, this._props.index)}
                 Document={this._props.doc}
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 5a3be826b..8e1fba699 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -12,7 +12,7 @@ import { DragManager } from '../../../util/DragManager';
 import { SnappingManager } from '../../../util/SnappingManager';
 import { Transform } from '../../../util/Transform';
 import { undoable } from '../../../util/UndoManager';
-import { ViewBoxBaseComponent } from '../../DocComponent';
+import { ViewBoxBaseComponent, ViewBoxBaseProps } from '../../DocComponent';
 import { Colors } from '../../global/globalEnums';
 import { OpenWhere } from '../../nodes/DocumentView';
 import { FieldView, FieldViewProps } from '../../nodes/FieldView';
@@ -24,7 +24,7 @@ interface SchemaRowBoxProps {
     rowIndex: number;
 }
 @observer
-export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps & SchemaRowBoxProps>() {
+export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps & SchemaRowBoxProps & ViewBoxBaseProps>() {
     public static LayoutString(fieldKey: string, rowIndex: number) {
         return FieldView.LayoutString(SchemaRowBox, fieldKey).replace('fieldKey', `rowIndex={${rowIndex}} fieldKey`);
     }
@@ -38,11 +38,11 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps & SchemaRo
     bounds = () => this._ref?.getBoundingClientRect();
 
     @computed get schemaView() {
-        return this._props.DocumentView?.()._props.docViewPath().lastElement()?.ComponentView as CollectionSchemaView;
+        return this.DocumentView?.().containerViewPath?.().lastElement()?.ComponentView as CollectionSchemaView;
     }
 
     @computed get schemaDoc() {
-        return this._props.DocumentView?.()._props.docViewPath().lastElement()?.Document;
+        return this.DocumentView?.().containerViewPath?.().lastElement()?.Document;
     }
 
     @computed get rowIndex() {
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 85269028b..422c4155d 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -77,7 +77,6 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
             childFiltersByRanges: returnEmptyFilter,
             searchFilterDocs: returnEmptyDoclist,
             styleProvider: DefaultStyleProvider,
-            docViewPath: returnEmptyDoclist,
             isSelected: returnFalse,
             setHeight: returnFalse,
             select: emptyFunction,
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index e57ef4871..3084a7972 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -43,14 +43,14 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b
         if (checkResult) {
             const selView = selectedViews.lastElement();
             const fieldKey = selView.Document.type === DocumentType.INK ? 'fillColor' : 'backgroundColor';
-            const layoutFrameNumber = Cast(selView._props.docViewPath().lastElement()?.Document?._currentFrame, 'number'); // frame number that container is at which determines layout frame values
+            const layoutFrameNumber = Cast(selView.containerViewPath?.().lastElement()?.Document?._currentFrame, 'number'); // frame number that container is at which determines layout frame values
             const contentFrameNumber = Cast(selView.Document?._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
             return CollectionFreeFormDocumentView.getStringValues(selView?.Document, contentFrameNumber)[fieldKey] ?? 'transparent';
         }
         selectedViews.some(dv => dv.ComponentView instanceof InkingStroke) && SetActiveFillColor(color ?? 'transparent');
         selectedViews.forEach(dv => {
             const fieldKey = dv.Document.type === DocumentType.INK ? 'fillColor' : 'backgroundColor';
-            const layoutFrameNumber = Cast(dv._props.docViewPath().lastElement()?.Document?._currentFrame, 'number'); // frame number that container is at which determines layout frame values
+            const layoutFrameNumber = Cast(dv.containerViewPath?.().lastElement()?.Document?._currentFrame, 'number'); // frame number that container is at which determines layout frame values
             const contentFrameNumber = Cast(dv.Document?._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
             if (contentFrameNumber !== undefined) {
                 const obj: { [key: string]: Opt<string> } = {};
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 85e97f95f..dc4aee1ca 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -136,6 +136,7 @@ export class LinkMenuItem extends ObservableReactComponent<LinkMenuItemProps> {
 
     deleteLink = (e: React.PointerEvent): void => setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => LinkManager.Instance.deleteLink(this._props.linkDoc))));
     @observable _hover = false;
+    docView = () => this.props.docView;
     render() {
         const destinationIcon = Doc.toIcon(this._props.destinationDoc) as any as IconProp;
 
@@ -179,7 +180,8 @@ export class LinkMenuItem extends ObservableReactComponent<LinkMenuItemProps> {
                                 this._props.linkDoc &&
                                 this._props.clearLinkEditor &&
                                 LinkInfo.SetLinkInfo({
-                                    docProps: this._props.docView._props,
+                                    DocumentView: this.docView,
+                                    styleProvider: this._props.docView._props.styleProvider,
                                     linkSrc: this._props.sourceDoc,
                                     linkDoc: this._props.linkDoc,
                                     showHeader: false,
diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx
index 7e344dd7a..31841c596 100644
--- a/src/client/views/linking/LinkPopup.tsx
+++ b/src/client/views/linking/LinkPopup.tsx
@@ -69,7 +69,6 @@ export class LinkPopup extends React.Component<LinkPopupProps> {
                         isSelected={returnTrue}
                         isContentActive={returnTrue}
                         select={returnTrue}
-                        setHeight={returnFalse}
                         addDocument={undefined}
                         addDocTab={returnTrue}
                         pinToPres={emptyFunction}
@@ -81,7 +80,6 @@ export class LinkPopup extends React.Component<LinkPopupProps> {
                         PanelHeight={this.getPHeight}
                         renderDepth={0}
                         focus={emptyFunction}
-                        docViewPath={returnEmptyDoclist}
                         whenChildContentsActiveChanged={emptyFunction}
                         bringToFront={emptyFunction}
                         childFilters={returnEmptyFilter}
diff --git a/src/client/views/newlightbox/NewLightboxView.tsx b/src/client/views/newlightbox/NewLightboxView.tsx
index 3d159c3e3..6760a0b1e 100644
--- a/src/client/views/newlightbox/NewLightboxView.tsx
+++ b/src/client/views/newlightbox/NewLightboxView.tsx
@@ -302,7 +302,7 @@ export class NewLightboxView extends React.Component<LightboxViewProps> {
                         styleProvider={DefaultStyleProvider}
                         ScreenToLocalTransform={this.newLightboxScreenToLocal}
                         renderDepth={0}
-                        docViewPath={returnEmptyDoclist}
+                        containerViewPath={returnEmptyDoclist}
                         childFilters={this.docFilters}
                         childFiltersByRanges={returnEmptyFilter}
                         searchFilterDocs={returnEmptyDoclist}
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 908cd5dc0..1729af191 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -211,7 +211,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     // removes from currently playing display
     @action
     removeCurrentlyPlaying = () => {
-        const docView = this._props.DocumentView?.();
+        const docView = this.DocumentView?.();
         if (CollectionStackedTimeline.CurrentlyPlaying && docView) {
             const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(docView);
             index !== -1 && CollectionStackedTimeline.CurrentlyPlaying.splice(index, 1);
@@ -221,7 +221,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     // adds doc to currently playing display
     @action
     addCurrentlyPlaying = () => {
-        const docView = this._props.DocumentView?.();
+        const docView = this.DocumentView?.();
         if (!CollectionStackedTimeline.CurrentlyPlaying) {
             CollectionStackedTimeline.CurrentlyPlaying = [];
         }
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index ad5aabc21..83cabf355 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -15,7 +15,7 @@ import { DocComponent } from '../DocComponent';
 import { StyleProp } from '../StyleProvider';
 import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
 import './CollectionFreeFormDocumentView.scss';
-import { DocumentView, DocumentViewInternalProps, DocumentViewProps, OpenWhere } from './DocumentView';
+import { DocumentView, DocumentViewProps, OpenWhere } from './DocumentView';
 import { FieldViewProps } from './FieldView';
 
 export interface CollectionFreeFormDocumentViewWrapperProps extends DocumentViewProps {
@@ -149,7 +149,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
         return this._props.CollectionFreeFormView;
     }
 
-    styleProvider = (doc: Doc | undefined, props: Opt<DocumentViewInternalProps | FieldViewProps>, property: string) => {
+    styleProvider = (doc: Doc | undefined, props: Opt<FieldViewProps>, property: string) => {
         if (doc === this.layoutDoc) {
             switch (property) {
                 case StyleProp.Opacity:         return this._props.w_Opacity(); // only change the opacity for this specific document, not its children
@@ -229,7 +229,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
 
     @action public float = () => {
         const topDoc = this.Document;
-        const containerDocView = this._props.docViewPath().lastElement();
+        const containerDocView = this._props.containerViewPath?.().lastElement();
         const screenXf = containerDocView?.screenToContentsTransform();
         if (screenXf) {
             SelectionManager.DeselectAll();
@@ -252,7 +252,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     };
 
     nudge = (x: number, y: number) => {
-        const [locX, locY] = this.ScreenToLocalBoxXf().transformDirection(x, y);
+        const [locX, locY] = this._props.ScreenToLocalTransform().transformDirection(x, y);
         this._props.Document.x = this._props.w_X() + locX;
         this._props.Document.y = this._props.w_Y() + locY;
     };
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 5e7e568b0..b171db17c 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -11,7 +11,7 @@ import { undoBatch } from '../../util/UndoManager';
 import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
 import { StyleProp } from '../StyleProvider';
 import './ComparisonBox.scss';
-import { DocumentView, DocumentViewInternalProps, DocumentViewProps } from './DocumentView';
+import { DocumentView } from './DocumentView';
 import { FieldView, FieldViewProps } from './FieldView';
 import { PinProps, PresBox } from './trails';
 
@@ -148,7 +148,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
             e => this.clearDoc(which)
         );
     };
-    docStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewInternalProps | FieldViewProps>, property: string): any => {
+    docStyleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string): any => {
         if (property === StyleProp.PointerEvents) return 'none';
         return this._props.styleProvider?.(doc, props, property);
     };
@@ -178,6 +178,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
                         {...this._props}
                         Document={targetDoc}
                         TemplateDataDocument={undefined}
+                        containerViewPath={this.docViewPathFunc}
                         moveDocument={which.endsWith('1') ? this.moveDoc1 : this.moveDoc2}
                         removeDocument={which.endsWith('1') ? this.remDoc1 : this.remDoc2}
                         NativeWidth={returnZero}
diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
index 5a55ca764..5ceec98cb 100644
--- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx
+++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
@@ -1,6 +1,6 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Colors, Toggle, ToggleType, Type } from 'browndash-components';
-import { ObservableMap, action, computed, observable, runInAction } from 'mobx';
+import { ObservableMap, action, computed, makeObservable, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { emptyFunction, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents } from '../../../../Utils';
@@ -14,10 +14,9 @@ import { TraceMobx } from '../../../../fields/util';
 import { Docs } from '../../../documents/Documents';
 import { DocumentManager } from '../../../util/DocumentManager';
 import { UndoManager, undoable } from '../../../util/UndoManager';
-import { ViewBoxAnnotatableComponent } from '../../DocComponent';
+import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent';
 import { MarqueeAnnotator } from '../../MarqueeAnnotator';
 import { SidebarAnnos } from '../../SidebarAnnos';
-import { CollectionFreeFormView } from '../../collections/collectionFreeForm';
 import { AnchorMenu } from '../../pdf/AnchorMenu';
 import { GPTPopup } from '../../pdf/GPTPopup/GPTPopup';
 import { DocFocusOptions, DocumentView } from '../DocumentView';
@@ -37,22 +36,28 @@ export enum DataVizView {
 }
 
 @observer
-export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
+export class DataVizBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
     private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
-    private _ffref = React.createRef<CollectionFreeFormView>();
     private _marqueeref = React.createRef<MarqueeAnnotator>();
     private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
     anchorMenuClick?: () => undefined | ((anchor: Doc) => void);
     crop: ((region: Doc | undefined, addCrop?: boolean) => Doc | undefined) | undefined;
-    @observable schemaDataVizChildren: any = undefined;
+    @observable _schemaDataVizChildren: any = undefined;
     @observable _marqueeing: number[] | undefined = undefined;
     @observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
+
+    constructor(props: ViewBoxAnnotatableProps & FieldViewProps) {
+        super(props);
+        makeObservable(this);
+        this._props.setContentView?.(this);
+    }
+
     @computed get annotationLayer() {
         TraceMobx();
         return <div className="dataVizBox-annotationLayer" style={{ height: this._props.PanelHeight(), width: this._props.PanelWidth() }} ref={this._annotationLayer} />;
     }
     marqueeDown = (e: React.PointerEvent) => {
-        if (!e.altKey && e.button === 0 && NumCast(this.Document._freeform_scale, 1) <= NumCast(this.Document.freeform_scaleMin, 1) && this._props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
+        if (!e.altKey && e.button === 0 && NumCast(this.Document._freeform_scale, 1) <= NumCast(this.Document.freeform_scaleMin, 1) && this._props.isContentActive() && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
             setupMoveUpEvents(
                 this,
                 e,
@@ -211,12 +216,12 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                         .ScreenToLocalTransform()
                         .scale(this._props.NativeDimScaling?.() || 1)
                         .transformDirection(delta[0], delta[1]);
-                    const fullWidth = this._props.width;
-                    const mapWidth = fullWidth! - this.sidebarWidth();
+                    const fullWidth = NumCast(this.layoutDoc._width);
+                    const mapWidth = fullWidth - this.sidebarWidth();
                     if (this.sidebarWidth() + localDelta[0] > 0) {
                         this.layoutDoc._layout_showSidebar = true;
-                        this.layoutDoc._width = fullWidth! + localDelta[0];
-                        this.layoutDoc._layout_sidebarWidthPercent = ((100 * (this.sidebarWidth() + localDelta[0])) / (fullWidth! + localDelta[0])).toString() + '%';
+                        this.layoutDoc._layout_sidebarWidthPercent = ((100 * (this.sidebarWidth() + localDelta[0])) / (fullWidth + localDelta[0])).toString() + '%';
+                        this.layoutDoc._width = fullWidth + localDelta[0];
                     } else {
                         this.layoutDoc._layout_showSidebar = false;
                         this.layoutDoc._width = mapWidth;
@@ -269,12 +274,12 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
             axes: this.axes,
             //width: this.SidebarShown? this._props.PanelWidth()*.9/1.2: this._props.PanelWidth() * 0.9,
             height: (this._props.PanelHeight() / scale - 32) /* height of 'change view' button */ * 0.9,
-            width: (this._props.PanelWidth() / scale) * 0.9,
+            width: ((this._props.PanelWidth() - this.sidebarWidth()) / scale) * 0.9,
             margin: { top: 10, right: 25, bottom: 75, left: 45 },
         };
         if (!this.records.length) return 'no data/visualization';
         switch (this.dataVizView) {
-            case DataVizView.TABLE:     return <TableBox  {...sharedProps} docView={this._props.DocumentView} selectAxes={this.selectAxes} />;
+            case DataVizView.TABLE:     return <TableBox  {...sharedProps} docView={this.DocumentView} selectAxes={this.selectAxes} />;
             case DataVizView.LINECHART: return <LineChart {...sharedProps} dataDoc={this.dataDoc} fieldKey={this.fieldKey} ref={r => (this._vizRenderer = r ?? undefined)} vizBox={this} />;
             case DataVizView.HISTOGRAM: return <Histogram {...sharedProps} dataDoc={this.dataDoc} fieldKey={this.fieldKey} ref={r => (this._vizRenderer = r ?? undefined)} />;
             case DataVizView.PIECHART:  return <PieChart  {...sharedProps} dataDoc={this.dataDoc} fieldKey={this.fieldKey} ref={r => (this._vizRenderer = r ?? undefined)} 
@@ -285,7 +290,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     @action
     onPointerDown = (e: React.PointerEvent): void => {
         if ((this.Document._freeform_scale || 1) !== 1) return;
-        if (!e.altKey && e.button === 0 && this._props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
+        if (!e.altKey && e.button === 0 && this._props.isContentActive() && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
             this._props.select(false);
             MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
             this._marqueeing = [e.clientX, e.clientY];
@@ -345,7 +350,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
 
     render() {
         if (this.layoutDoc && this.layoutDoc.dataViz_asSchema) {
-            this.schemaDataVizChildren = DocListCast(DocCast(this.layoutDoc.dataViz_asSchema)[Doc.LayoutFieldKey(DocCast(this.layoutDoc.dataViz_asSchema))]).length;
+            this._schemaDataVizChildren = DocListCast(DocCast(this.layoutDoc.dataViz_asSchema)[Doc.LayoutFieldKey(DocCast(this.layoutDoc.dataViz_asSchema))]).length;
             this.updateSchemaViz();
         }
 
@@ -366,7 +371,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                 }}
                 onWheel={e => e.stopPropagation()}
                 ref={this._mainCont}>
-                <div className={'datatype-button'}>
+                <div className="datatype-button">
                     <Toggle text={'  TABLE  '} toggleType={ToggleType.BUTTON} type={Type.SEC} color={'black'} onClick={e => (this.layoutDoc._dataViz = DataVizView.TABLE)} toggleStatus={this.layoutDoc._dataViz === DataVizView.TABLE} />
                     <Toggle text={'LINECHART'} toggleType={ToggleType.BUTTON} type={Type.SEC} color={'black'} onClick={e => (this.layoutDoc._dataViz = DataVizView.LINECHART)} toggleStatus={this.layoutDoc._dataViz === DataVizView.LINECHART} />
                     <Toggle text={'HISTOGRAM'} toggleType={ToggleType.BUTTON} type={Type.SEC} color={'black'} onClick={e => (this.layoutDoc._dataViz = DataVizView.HISTOGRAM)} toggleStatus={this.layoutDoc._dataViz === DataVizView.HISTOGRAM} />
@@ -414,7 +419,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                 </div>
                 {this.sidebarHandle}
                 {this.annotationLayer}
-                {!this._mainCont.current || !this._annotationLayer.current ? null : (
+                {!this._mainCont.current || !this.DocumentView || !this._annotationLayer.current ? null : (
                     <MarqueeAnnotator
                         ref={this._marqueeref}
                         Document={this.Document}
@@ -422,7 +427,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                         scrollTop={0}
                         annotationLayerScrollTop={NumCast(this.Document._layout_scrollTop)}
                         scaling={returnOne}
-                        docView={this._props.DocumentView!}
+                        docView={this.DocumentView}
                         addDocument={this.sidebarAddDocument}
                         finishMarquee={this.finishMarquee}
                         savedAnnotations={this.savedAnnotations}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 4cc7fe4c8..0d76959af 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -116,11 +116,12 @@ export class HTMLtag extends React.Component<HTMLtagProps> {
 
 @observer
 export class DocumentContentsView extends ObservableReactComponent<
-    DocumentViewProps &
-        FieldViewProps & {
-            setHeight?: (height: number) => void;
-            layout_fieldKey: string;
-        }
+    FieldViewProps & {
+        layout_fieldKey: string;
+        LayoutTemplateString?: string;
+        onClick?: () => ScriptField;
+        LayoutTemplate?: () => Opt<Doc>;
+    }
 > {
     constructor(props: any) {
         super(props);
diff --git a/src/client/views/nodes/DocumentIcon.tsx b/src/client/views/nodes/DocumentIcon.tsx
index dfd610581..25f56e69a 100644
--- a/src/client/views/nodes/DocumentIcon.tsx
+++ b/src/client/views/nodes/DocumentIcon.tsx
@@ -25,7 +25,7 @@ export class DocumentIcon extends ObservableReactComponent<DocumentIconProps> {
     }
 
     static get DocViews() {
-        return LightboxView.LightboxDoc ? DocumentManager.Instance.DocumentViews.filter(v => LightboxView.IsLightboxDocView(v._props.docViewPath())) : DocumentManager.Instance.DocumentViews;
+        return LightboxView.LightboxDoc ? DocumentManager.Instance.DocumentViews.filter(v => LightboxView.IsLightboxDocView(v.docViewPath)) : DocumentManager.Instance.DocumentViews;
     }
     render() {
         const view = this._props.view;
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index c549a146a..f5949a451 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -152,8 +152,8 @@ export class DocumentLinksButton extends ObservableReactComponent<DocumentLinksB
                 DocumentLinksButton.AnnotationUri = undefined;
                 //!this._props.StartLink
             } else if (startLink !== endLink) {
-                endLink = endLinkView?.docView?._componentView?.getAnchor?.(true, pinProps) || endLink;
-                startLink = DocumentLinksButton.StartLinkView?.docView?._componentView?.getAnchor?.(true) || startLink;
+                endLink = endLinkView?.ComponentView?.getAnchor?.(true, pinProps) || endLink;
+                startLink = DocumentLinksButton.StartLinkView?.ComponentView?.getAnchor?.(true) || startLink;
                 const linkDoc = DocUtils.MakeLink(startLink, endLink, { link_relationship: DocumentLinksButton.AnnotationId ? 'hypothes.is annotation' : undefined });
 
                 LinkManager.currentLink = linkDoc;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 6cc61ec62..5345ef0d8 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -109,7 +109,7 @@ export interface DocFocusOptions {
     easeFunc?: 'linear' | 'ease'; // transition method for scrolling
 }
 export type DocFocusFunc = (doc: Doc, options: DocFocusOptions) => Opt<number>;
-export type StyleProviderFunc = (doc: Opt<Doc>, props: Opt<DocumentViewInternalProps | FieldViewProps>, property: string) => any;
+export type StyleProviderFunc = (doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string) => any;
 export interface DocComponentView {
     fieldKey?: string;
     annotationKey?: string;
@@ -143,20 +143,31 @@ export interface DocComponentView {
     snapPt?: (pt: { X: number; Y: number }, excludeSegs?: number[]) => { nearestPt: { X: number; Y: number }; distance: number };
     search?: (str: string, bwd?: boolean, clear?: boolean) => boolean;
 }
-// These props are passed to both FieldViews and DocumentViews
+/**
+ * props that DocumentViews, DocumentInternalViews and FieldViews all can use
+ * */
 export interface DocumentViewSharedProps {
-    renderDepth: number;
     Document: Doc;
     TemplateDataDocument?: Doc;
+    renderDepth: number;
     scriptContext?: any; // can be assigned anything and will be passed as 'scriptContext' to any OnClick script that executes on this document
-    DocumentView?: () => DocumentView;
+    treeViewDoc?: Doc;
+    xPadding?: number;
+    yPadding?: number;
+    dontRegisterView?: boolean;
+    dropAction?: dropActionType;
+    dragAction?: dropActionType;
+    forceAutoHeight?: boolean;
+    ignoreAutoHeight?: boolean;
+    disableBrushing?: boolean; // should highlighting for this view be disabled when same document in another view is hovered over.
     CollectionFreeFormDocumentView?: () => CollectionFreeFormDocumentView;
     fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _freeform_fitContentsToBox property on a Document
     isGroupActive?: () => string | undefined; // is this document part of a group that is active
     setContentView?: (view: DocComponentView) => any;
     PanelWidth: () => number;
     PanelHeight: () => number;
-    docViewPath: () => DocumentView[];
+    isDocumentActive?: () => boolean | undefined; // whether a document should handle pointer events
+    isContentActive: () => boolean | undefined; // whether document contents should handle pointer events
     childFilters: () => string[];
     childFiltersByRanges: () => string[];
     styleProvider: Opt<StyleProviderFunc>;
@@ -178,29 +189,12 @@ export interface DocumentViewSharedProps {
     waitForDoubleClickToClick?: () => 'never' | 'always' | undefined;
     defaultDoubleClick?: () => 'default' | 'ignore' | undefined;
     pointerEvents?: () => Opt<string>;
-    treeViewDoc?: Doc;
-    xPadding?: number;
-    yPadding?: number;
-    dontRegisterView?: boolean;
-    childHideDecorationTitle?: boolean;
-    childHideResizeHandles?: boolean;
-    childDragAction?: dropActionType; // allows child documents to be dragged out of collection without holding the embedKey or dragging the doc decorations title bar.
-    dropAction?: dropActionType;
-    dragAction?: dropActionType;
-    dragWhenActive?: boolean;
-    dontHideOnDrag?: boolean;
-    hideLinkButton?: boolean;
-    hideCaptions?: boolean;
-    ignoreAutoHeight?: boolean;
-    forceAutoHeight?: boolean;
-    suppressSetHeight?: boolean;
-    disableBrushing?: boolean; // should highlighting for this view be disabled when same document in another view is hovered over.
-    onClickScriptDisable?: 'never' | 'always'; // undefined = only when selected
 }
 
-// these props are specific to DocuentViews
+/**
+ * props that are used by DocumentViews and DocumentInternalViews but not by the contents of those views (FieldViews).
+ */
 export interface DocumentViewProps extends DocumentViewSharedProps {
-    // properties specific to DocumentViews but not to FieldView
     hideDecorations?: boolean; // whether to suppress all DocumentDecorations when doc is selected
     hideResizeHandles?: boolean; // whether to suppress resized handles on doc decorations when this document is selected
     hideTitle?: boolean; // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings
@@ -209,15 +203,22 @@ export interface DocumentViewProps extends DocumentViewSharedProps {
     hideOpenButton?: boolean;
     hideDeleteButton?: boolean;
     hideLinkAnchors?: boolean;
+    hideLinkButton?: boolean;
+    hideCaptions?: boolean;
     contentPointerEvents?: 'none' | 'all' | undefined; // pointer events allowed for content of a document view.  eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents
     LayoutTemplateString?: string;
     dontCenter?: 'x' | 'y' | 'xy';
-    isDocumentActive?: () => boolean | undefined; // whether a document should handle pointer events
-    isContentActive: () => boolean | undefined; // whether document contents should handle pointer events
+    childHideDecorationTitle?: boolean;
+    childHideResizeHandles?: boolean;
+    childDragAction?: dropActionType; // allows child documents to be dragged out of collection without holding the embedKey or dragging the doc decorations title bar.
+    dragWhenActive?: boolean;
+    dontHideOnDrag?: boolean;
+    suppressSetHeight?: boolean;
+    onClickScriptDisable?: 'never' | 'always'; // undefined = only when selected
     NativeWidth?: () => number;
     NativeHeight?: () => number;
-    NativeDimScaling?: () => number; // scaling the DocumentView does to transform its contents into its panel & needed by ScreenToLocal NOTE: Must also be added to FieldViewProps
     LayoutTemplate?: () => Opt<Doc>;
+    containerViewPath?: () => DocumentView[];
     contextMenuItems?: () => { script: ScriptField; filter?: ScriptField; label: string; icon: string }[];
     onClick?: () => ScriptField;
     onDoubleClick?: () => ScriptField;
@@ -229,12 +230,18 @@ export interface DocumentViewProps extends DocumentViewSharedProps {
     dragEnding?: () => void;
 }
 
-// these props are only available in DocumentViewIntenral
-export interface DocumentViewInternalProps extends DocumentViewProps {
-    isSelected: () => boolean;
+/**
+ * props used by DocInternalViews and FieldViews but not DocumentViews
+ * these props correspond to things that the DocumentView creates and thus doesn't need to receive as a prop
+ */
+export interface DocumentViewInternalSharedProps {
+    DocumentView?: () => DocumentView;
     select: (ctrlPressed: boolean, shiftPress?: boolean) => void;
-    DocumentView: () => DocumentView;
-    viewPath: () => DocumentView[];
+    isSelected: () => boolean;
+    NativeDimScaling?: () => number; // scaling the DocumentView does to transform its contents into its panel & needed by ScreenToLocal
+}
+export interface DocumentViewInternalProps extends DocumentViewProps, DocumentViewInternalSharedProps {
+    docViewPath: () => DocumentView[];
 }
 
 @observer
@@ -265,17 +272,24 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         return this._animateScaleTime ?? 100;
     }
     public get displayName() {
-        return 'DocumentViewInternal(' + this._props.Document.title + ')';
+        return 'DocumentViewInternal(' + this.Document.title + ')';
     } // this makes mobx trace() statements more descriptive
 
+    public get DocumentView() {
+        return this._props.DocumentView!;
+    }
+
     public get ContentDiv() {
         return this._mainCont.current;
     }
     public get LayoutFieldKey() {
         return Doc.LayoutFieldKey(this.layoutDoc);
     }
+    @computed get styleProps(): FieldViewProps {
+        return { ...this._props, fieldKey: '' };
+    }
     @computed get layout_showTitle() {
-        return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.ShowTitle) as Opt<string>;
+        return this._props.styleProvider?.(this.layoutDoc, this.styleProps, StyleProp.ShowTitle) as Opt<string>;
     }
     @computed get NativeDimScaling() {
         return this._props.NativeDimScaling?.() || 1;
@@ -284,32 +298,32 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         return ImageCast(this.layoutDoc['thumb-frozen'], ImageCast(this.layoutDoc.thumb))?.url?.href.replace('.png', '_m.png');
     }
     @computed get opacity() {
-        return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Opacity);
+        return this._props.styleProvider?.(this.layoutDoc, this.styleProps, StyleProp.Opacity);
     }
     @computed get boxShadow() {
-        return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BoxShadow);
+        return this._props.styleProvider?.(this.layoutDoc, this.styleProps, StyleProp.BoxShadow);
     }
     @computed get borderRounding() {
-        return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BorderRounding);
+        return this._props.styleProvider?.(this.layoutDoc, this.styleProps, StyleProp.BorderRounding);
     }
     @computed get widgetDecorations() {
         TraceMobx();
-        return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Decorations);
+        return this._props.styleProvider?.(this.layoutDoc, this.styleProps, StyleProp.Decorations);
     }
     @computed get backgroundBoxColor() {
-        return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor + ':box');
+        return this._props.styleProvider?.(this.layoutDoc, this.styleProps, StyleProp.BackgroundColor + ':box');
     }
     @computed get docContents() {
-        return this._props.styleProvider?.(this.Document, this._props, StyleProp.DocContents);
+        return this._props.styleProvider?.(this.Document, this.styleProps, StyleProp.DocContents);
     }
     @computed get headerMargin() {
-        return this._props?.styleProvider?.(this.layoutDoc, this._props, StyleProp.HeaderMargin) || 0;
+        return this._props?.styleProvider?.(this.layoutDoc, this.styleProps, StyleProp.HeaderMargin) || 0;
     }
     @computed get layout_showCaption() {
-        return this._props?.styleProvider?.(this.layoutDoc, this._props, StyleProp.ShowCaption) || 0;
+        return this._props?.hideCaptions ? undefined : this._props?.styleProvider?.(this.layoutDoc, this.styleProps, StyleProp.ShowCaption) || 0;
     }
     @computed get titleHeight() {
-        return this._props?.styleProvider?.(this.layoutDoc, this._props, StyleProp.TitleHeight) || 0;
+        return this._props?.styleProvider?.(this.layoutDoc, this.styleProps, StyleProp.TitleHeight) || 0;
     }
     @observable _pointerEvents: 'none' | 'all' | 'visiblePainted' | undefined = undefined;
     @computed get pointerEvents(): 'none' | 'all' | 'visiblePainted' | undefined {
@@ -363,7 +377,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             { fireImmediately: true }
         );
         this._disposers.pointerevents = reaction(
-            () => this._props.styleProvider?.(this.Document, this._props, StyleProp.PointerEvents),
+            () => this._props.styleProvider?.(this.Document, this.styleProps, StyleProp.PointerEvents),
             pointerevents => (this._pointerEvents = pointerevents),
             { fireImmediately: true }
         );
@@ -378,32 +392,32 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     setupHandlers() {
         this.cleanupHandlers(false);
         if (this._mainCont.current) {
-            this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this), this._props.Document, this.preDropFunc);
+            this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this), this.Document, this.preDropFunc);
         }
     }
 
     cleanupHandlers(unbrush: boolean) {
         this._dropDisposer?.();
-        unbrush && Doc.UnBrushDoc(this._props.Document);
+        unbrush && Doc.UnBrushDoc(this.Document);
         Object.values(this._disposers).forEach(disposer => disposer?.());
     }
 
     startDragging(x: number, y: number, dropAction: dropActionType, hideSource = false) {
         if (this._mainCont.current) {
-            const views = SelectionManager.Views.filter(dv => dv.docView?._mainCont.current);
-            const selected = views.length > 1 && views.some(dv => dv.Document === this.Document) ? views : [this._props.DocumentView()];
+            const views = SelectionManager.Views.filter(dv => dv.ContentDiv);
+            const selected = views.length > 1 && views.some(dv => dv.Document === this.Document) ? views : [this.DocumentView()];
             const dragData = new DragManager.DocumentDragData(selected.map(dv => dv.Document));
-            const screenXf = this.props.DocumentView().screenToViewTransform();
+            const screenXf = this.DocumentView().screenToViewTransform();
             const [left, top] = screenXf.inverse().transformPoint(0, 0);
             dragData.offset = screenXf.transformDirection(x - left, y - top);
             dragData.dropAction = dropAction;
             dragData.treeViewDoc = this._props.treeViewDoc;
             dragData.removeDocument = this._props.removeDocument;
             dragData.moveDocument = this._props.moveDocument;
-            dragData.draggedViews = [this._props.DocumentView()];
+            dragData.draggedViews = [this.DocumentView()];
             dragData.canEmbed = this.Document.dragAction ?? this._props.dragAction ? true : false;
             DragManager.StartDocumentDrag(
-                selected.map(dv => dv.docView!._mainCont.current!),
+                selected.map(dv => dv.ContentDiv!),
                 dragData,
                 x,
                 y,
@@ -443,7 +457,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     const func = () => this.onDoubleClickHandler.script.run( {
                             this: this.Document,
                             scriptContext: this._props.scriptContext,
-                            documentView: this._props.DocumentView(),
+                            documentView: this.DocumentView(),
                             clientX, clientY,  altKey,  shiftKey,  ctrlKey,
                             value: undefined,
                         }, console.log  );
@@ -451,7 +465,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 } else if (!Doc.IsSystem(this.Document) && (defaultDblclick === undefined || defaultDblclick === 'default')) {
                     UndoManager.RunInBatch(() => LightboxView.Instance.AddDocTab(this.Document, OpenWhere.lightbox), 'double tap');
                     SelectionManager.DeselectAll();
-                    Doc.UnBrushDoc(this._props.Document);
+                    Doc.UnBrushDoc(this.Document);
                 } else {
                     this._singleClickFunc?.();
                 }
@@ -474,7 +488,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                                 this: this.Document,
                                 _readOnly_: false,
                                 scriptContext: this._props.scriptContext,
-                                documentView: this._props.DocumentView(),
+                                documentView: this.DocumentView(),
                                 clientX,
                                 clientY,
                                 shiftKey,
@@ -498,7 +512,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 const sendToBack = e.altKey;
                 this._singleClickFunc =
                     // prettier-ignore
-                    clickFunc ?? (() => (sendToBack ? this._props.DocumentView()._props.bringToFront(this.Document, true) : 
+                    clickFunc ?? (() => (sendToBack ? this.DocumentView()._props.bringToFront(this.Document, true) : 
                                                       this._componentView?.select?.(e.ctrlKey || e.metaKey, e.shiftKey) ?? 
                                         this._props.select(e.ctrlKey||e.shiftKey, e.metaKey)));
                 const waitFordblclick = this._props.waitForDoubleClickToClick?.() ?? this.Document.waitForDoubleClickToClick;
@@ -522,7 +536,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 this._props.select(false);
             }
         }, 1000);
-        if (!GestureOverlay.DownDocView) GestureOverlay.DownDocView = this._props.DocumentView();
+        if (!GestureOverlay.DownDocView) GestureOverlay.DownDocView = this.DocumentView();
 
         this._downX = e.clientX;
         this._downY = e.clientY;
@@ -600,7 +614,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         this.Document.onClick = Doc.GetProto(this.Document).onClick = undefined;
     };
 
-    @undoBatch deleteClicked = () => this._props.removeDocument?.(this._props.Document);
+    @undoBatch deleteClicked = () => this._props.removeDocument?.(this.Document);
     @undoBatch setToggleDetail = () =>
         (this.Document.onClick = ScriptField.MakeScript(
             `toggleDetail(documentView, "${StrCast(this.Document.layout_fieldKey)
@@ -612,7 +626,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     @undoBatch
     drop = (e: Event, de: DragManager.DropEvent) => {
         if (this._props.dontRegisterView || this._props.LayoutTemplateString?.includes(LinkAnchorBox.name)) return false;
-        if (this._props.Document === Doc.ActiveDashboard) {
+        if (this.Document === Doc.ActiveDashboard) {
             e.stopPropagation();
             e.preventDefault();
             alert(
@@ -634,7 +648,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     de.complete.linkDocument = DocUtils.MakeLink(linkdrag.linkSourceDoc, dropDoc, {}, undefined, [de.x, de.y - 50]);
                     if (de.complete.linkDocument) {
                         de.complete.linkDocument.layout_isSvg = true;
-                        this._props.DocumentView().CollectionFreeFormView?.addDocument(de.complete.linkDocument);
+                        this.DocumentView().CollectionFreeFormView?.addDocument(de.complete.linkDocument);
                     }
                 }
                 e.stopPropagation();
@@ -646,16 +660,16 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     @undoBatch
     makeIntoPortal = () => {
-        const portalLink = this.allLinks.find(d => d.link_anchor_1 === this._props.Document && d.link_relationship === 'portal to:portal from');
+        const portalLink = this.allLinks.find(d => d.link_anchor_1 === this.Document && d.link_relationship === 'portal to:portal from');
         if (!portalLink) {
             DocUtils.MakeLink(
-                this._props.Document,
+                this.Document,
                 Docs.Create.FreeformDocument([], {
                     _width: NumCast(this.layoutDoc._width) + 10,
                     _height: Math.max(NumCast(this.layoutDoc._height), NumCast(this.layoutDoc._width) + 10),
                     _isLightbox: true,
                     _layout_fitWidth: true,
-                    title: StrCast(this._props.Document.title) + ' [Portal]',
+                    title: StrCast(this.Document.title) + ' [Portal]',
                 }),
                 { link_relationship: 'portal to:portal from' }
             );
@@ -686,7 +700,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         if (e && this.layoutDoc._layout_hideContextMenu && Doc.noviceMode) {
             e.preventDefault();
             e.stopPropagation();
-            //!this._props.isSelected(true) && SelectionManager.SelectView(this._props.DocumentView(), false);
+            //!this._props.isSelected(true) && SelectionManager.SelectView(this.DocumentView(), false);
         }
         // the touch onContextMenu is button 0, the pointer onContextMenu is button 2
         if (e) {
@@ -708,7 +722,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
         if (e && !(e.nativeEvent as any).dash) {
             const onDisplay = () => {
-                if (this.Document.type !== DocumentType.MAP) DocumentViewInternal.SelectAfterContextMenu && !this._props.isSelected() && SelectionManager.SelectView(this._props.DocumentView(), false); // on a mac, the context menu is triggered on mouse down, but a YouTube video becaomes interactive when selected which means that the context menu won't show up.  by delaying the selection until hopefully after the pointer up, the context menu will appear.
+                if (this.Document.type !== DocumentType.MAP) DocumentViewInternal.SelectAfterContextMenu && !this._props.isSelected() && SelectionManager.SelectView(this.DocumentView(), false); // on a mac, the context menu is triggered on mouse down, but a YouTube video becaomes interactive when selected which means that the context menu won't show up.  by delaying the selection until hopefully after the pointer up, the context menu will appear.
                 setTimeout(() => simulateMouseClick(document.elementFromPoint(e.clientX, e.clientY), e.clientX, e.clientY, e.screenX, e.screenY));
             };
             if (navigator.userAgent.includes('Macintosh')) {
@@ -719,14 +733,14 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             return;
         }
 
-        const customScripts = Cast(this._props.Document.contextMenuScripts, listSpec(ScriptField), []);
+        const customScripts = Cast(this.Document.contextMenuScripts, listSpec(ScriptField), []);
         StrListCast(this.Document.contextMenuLabels).forEach((label, i) =>
             cm.addItem({ description: label, event: () => customScripts[i]?.script.run({ documentView: this, this: this.Document, scriptContext: this._props.scriptContext }), icon: 'sticky-note' })
         );
         this._props.contextMenuItems?.().forEach(item => item.label && cm.addItem({ description: item.label, event: () => item.script.script.run({ this: this.Document, scriptContext: this._props.scriptContext }), icon: item.icon as IconProp }));
 
-        if (!this._props.Document.isFolder) {
-            const templateDoc = Cast(this._props.Document[StrCast(this._props.Document.layout_fieldKey)], Doc, null);
+        if (!this.Document.isFolder) {
+            const templateDoc = Cast(this.Document[StrCast(this.Document.layout_fieldKey)], Doc, null);
             const appearance = cm.findByDescription('Appearance...');
             const appearanceItems: ContextMenuProps[] = appearance && 'subitems' in appearance ? appearance.subitems : [];
 
@@ -764,7 +778,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                         !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'compass' });
 
                         onClicks.push({ description: this.onClickHandler ? 'Remove Click Behavior' : 'Follow Link', event: () => this.toggleFollowLink(false, false), icon: 'link' });
-                        !Doc.noviceMode && onClicks.push({ description: 'Edit onClick Script', event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this._props.Document, undefined, 'onClick'), 'edit onClick'), icon: 'terminal' });
+                        !Doc.noviceMode && onClicks.push({ description: 'Edit onClick Script', event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.Document, undefined, 'onClick'), 'edit onClick'), icon: 'terminal' });
                         !existingOnClick && cm.addItem({ description: 'OnClick...', noexpand: true, subitems: onClicks, icon: 'mouse-pointer' });
                     } else if (LinkManager.Links(this.Document).length) {
                         onClicks.push({ description: 'Restore On Click default', event: () => this.noOnClick(), icon: 'link' });
@@ -786,15 +800,15 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             const moreItems = more && 'subitems' in more ? more.subitems : [];
             if (!Doc.IsSystem(this.Document)) {
                 if (!Doc.noviceMode) {
-                    moreItems.push({ description: 'Make View of Metadata Field', event: () => Doc.MakeMetadataFieldTemplate(this._props.Document, this._props.TemplateDataDocument), icon: 'concierge-bell' });
+                    moreItems.push({ description: 'Make View of Metadata Field', event: () => Doc.MakeMetadataFieldTemplate(this.Document, this._props.TemplateDataDocument), icon: 'concierge-bell' });
                     moreItems.push({ description: `${this.Document._chromeHidden ? 'Show' : 'Hide'} Chrome`, event: () => (this.Document._chromeHidden = !this.Document._chromeHidden), icon: 'project-diagram' });
 
-                    if (Cast(Doc.GetProto(this._props.Document).data, listSpec(Doc))) {
-                        moreItems.push({ description: 'Export to Google Photos Album', event: () => GooglePhotos.Export.CollectionToAlbum({ collection: this._props.Document }).then(console.log), icon: 'caret-square-right' });
-                        moreItems.push({ description: 'Tag Child Images via Google Photos', event: () => GooglePhotos.Query.TagChildImages(this._props.Document), icon: 'caret-square-right' });
-                        moreItems.push({ description: 'Write Back Link to Album', event: () => GooglePhotos.Transactions.AddTextEnrichment(this._props.Document), icon: 'caret-square-right' });
+                    if (Cast(Doc.GetProto(this.Document).data, listSpec(Doc))) {
+                        moreItems.push({ description: 'Export to Google Photos Album', event: () => GooglePhotos.Export.CollectionToAlbum({ collection: this.Document }).then(console.log), icon: 'caret-square-right' });
+                        moreItems.push({ description: 'Tag Child Images via Google Photos', event: () => GooglePhotos.Query.TagChildImages(this.Document), icon: 'caret-square-right' });
+                        moreItems.push({ description: 'Write Back Link to Album', event: () => GooglePhotos.Transactions.AddTextEnrichment(this.Document), icon: 'caret-square-right' });
                     }
-                    moreItems.push({ description: 'Copy ID', event: () => Utils.CopyText(Doc.globalServerPath(this._props.Document)), icon: 'fingerprint' });
+                    moreItems.push({ description: 'Copy ID', event: () => Utils.CopyText(Doc.globalServerPath(this.Document)), icon: 'fingerprint' });
                 }
             }
 
@@ -802,14 +816,14 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         }
         const constantItems: ContextMenuProps[] = [];
         if (!Doc.IsSystem(this.Document) && this.Document._type_collection !== CollectionViewType.Docking) {
-            constantItems.push({ description: 'Zip Export', icon: 'download', event: async () => Doc.Zip(this._props.Document) });
-            (this.Document._type_collection !== CollectionViewType.Docking || !Doc.noviceMode) && constantItems.push({ description: 'Share', event: () => SharingManager.Instance.open(this._props.DocumentView()), icon: 'users' });
-            if (this._props.removeDocument && Doc.ActiveDashboard !== this._props.Document) {
+            constantItems.push({ description: 'Zip Export', icon: 'download', event: async () => Doc.Zip(this.Document) });
+            (this.Document._type_collection !== CollectionViewType.Docking || !Doc.noviceMode) && constantItems.push({ description: 'Share', event: () => SharingManager.Instance.open(this.DocumentView()), icon: 'users' });
+            if (this._props.removeDocument && Doc.ActiveDashboard !== this.Document) {
                 // need option to gray out menu items ... preferably with a '?' that explains why they're grayed out (eg., no permissions)
                 constantItems.push({ description: 'Close', event: this.deleteClicked, icon: 'times' });
             }
         }
-        constantItems.push({ description: 'Show Metadata', event: () => this._props.addDocTab(this._props.Document, OpenWhere.addRightKeyvalue), icon: 'table-columns' });
+        constantItems.push({ description: 'Show Metadata', event: () => this._props.addDocTab(this.Document, OpenWhere.addRightKeyvalue), icon: 'table-columns' });
         cm.addItem({ description: 'General...', noexpand: false, subitems: constantItems, icon: 'question' });
 
         const help = cm.findByDescription('Help...');
@@ -820,7 +834,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
         let documentationDescription: string | undefined = undefined;
         let documentationLink: string | undefined = undefined;
-        switch (this._props.Document.type) {
+        switch (this.Document.type) {
             case DocumentType.COL:
                 documentationDescription = 'See collection documentation';
                 documentationLink = 'https://brown-dash.github.io/Dash-Documentation/views/';
@@ -874,7 +888,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     }
     rootSelected = () => this._rootSelected;
     panelHeight = () => this._props.PanelHeight() - this.headerMargin;
-    screenToLocalContent = () => this.ScreenToLocalBoxXf().translate(0, -this.headerMargin);
+    screenToLocalContent = () => this._props.ScreenToLocalTransform().translate(0, -this.headerMargin);
     onClickFunc: any = () => (this.disableClickScriptFunc ? undefined : this.onClickHandler);
     setHeight = (height: number) => !this._props.suppressSetHeight && (this.layoutDoc._height = height);
     setContentView = action((view: { getAnchor?: (addAsAnnotation: boolean) => Doc; forward?: () => boolean; back?: () => boolean }) => (this._componentView = view));
@@ -912,7 +926,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     {...this._props}
                     fieldKey=""
                     pointerEvents={this.contentPointerEvents}
-                    docViewPath={this._props.viewPath}
                     setContentView={this.setContentView}
                     childFilters={this.childFilters}
                     PanelHeight={this.panelHeight}
@@ -931,7 +944,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     anchorPanelWidth = () => this._props.PanelWidth() || 1;
     anchorPanelHeight = () => this._props.PanelHeight() || 1;
-    anchorStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewInternalProps | FieldViewProps>, property: string): any => {
+    anchorStyleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string): any => {
         // prettier-ignore
         switch (property.split(':')[0]) {
             case StyleProp.ShowTitle:     return '';
@@ -976,7 +989,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     {...this._props}
                     isContentActive={returnFalse}
                     Document={link}
-                    docViewPath={this._props.viewPath}
+                    containerViewPath={this._props.docViewPath}
                     PanelWidth={this.anchorPanelWidth}
                     PanelHeight={this.anchorPanelHeight}
                     dontRegisterView={false}
@@ -1066,7 +1079,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         }
     };
 
-    captionStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewInternalProps | FieldViewProps>, property: string) => this._props?.styleProvider?.(doc, props, property + ':caption');
+    captionStyleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string) => this._props?.styleProvider?.(doc, props, property + ':caption');
     @observable _changingTitleField = false;
     @observable _dropDownInnerWidth = 0;
     fieldsDropdown = (inputOptions: string[], dropdownWidth: number, placeholder: string, onChange: (val: string | number) => void, onClose: () => void) => {
@@ -1266,10 +1279,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         }
     }
     @computed get highlighting() {
-        return this._props.styleProvider?.(this.Document, this._props, StyleProp.Highlighting);
+        return this._props.styleProvider?.(this.Document, { ...this._props, fieldKey: '' }, StyleProp.Highlighting);
     }
     @computed get borderPath() {
-        return this._props.styleProvider?.(this.Document, this._props, StyleProp.BorderPath);
+        return this._props.styleProvider?.(this.Document, { ...this._props, fieldKey: '' }, StyleProp.BorderPath);
     }
     render() {
         TraceMobx();
@@ -1332,14 +1345,13 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> {
     @computed public static get exploreMode() {
         return () => (SnappingManager.ExploreMode ? ScriptField.MakeScript('CollectionBrowseClick(documentView, clientX, clientY)', { documentView: 'any', clientX: 'number', clientY: 'number' })! : undefined);
     }
-    @observable public docView: DocumentViewInternal | undefined | null = undefined;
-    @observable public textHtmlOverlay: Opt<string> = undefined;
-    @observable public textHtmlOverlayTime: Opt<number> = undefined;
+    @observable private _docViewInternal: DocumentViewInternal | undefined | null = undefined;
+    @observable private _htmlOverlayText: Opt<string> = undefined;
     @observable private _isHovering = false;
+    private _htmlOverlayEffect: Opt<Doc>;
 
-    public htmlOverlayEffect: Opt<Doc>;
     public get displayName() {
-        return 'DocumentView(' + this._props.Document?.title + ')';
+        return 'DocumentView(' + this.Document?.title + ')';
     } // this makes mobx trace() statements more descriptive
     public ContentRef = React.createRef<HTMLDivElement>();
     public ViewTimer: NodeJS.Timeout | undefined; // timer for res
@@ -1349,10 +1361,26 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> {
         this.ViewTimer && clearTimeout(this.ViewTimer);
         this.layoutDoc._viewTransition = undefined;
     };
-    public startDragging = (x: number, y: number, dropAction: dropActionType, hideSource = false) => this.docView?.startDragging(x, y, dropAction, hideSource);
-
-    public showContextMenu = (pageX: number, pageY: number) => this.docView?.onContextMenu(undefined, pageX, pageY);
-
+    playAnnotation = () => this._docViewInternal?.playAnnotation();
+    noOnClick = () => this._docViewInternal?.noOnClick();
+    makeIntoPortal = () => this._docViewInternal?.makeIntoPortal();
+    setToggleDetail = () => this._docViewInternal?.setToggleDetail();
+    onContextMenu = (e?: React.MouseEvent, pageX?: number, pageY?: number) => this._docViewInternal?.onContextMenu?.(e, pageX, pageY);
+    cleanupPointerEvents = () => this._docViewInternal?.cleanupPointerEvents();
+    public startDragging = (x: number, y: number, dropAction: dropActionType, hideSource = false) => this._docViewInternal?.startDragging(x, y, dropAction, hideSource);
+
+    public showContextMenu = (pageX: number, pageY: number) => this._docViewInternal?.onContextMenu(undefined, pageX, pageY);
+
+    public setTextHtmlOverlay = action((text: string | undefined, effect?: Doc) => {
+        this._htmlOverlayText = text;
+        this._htmlOverlayEffect = effect;
+    });
+    public setAnimateScaling = action((scale: number, time?: number) => {
+        if (this._docViewInternal) {
+            this._docViewInternal._animateScalingTo = scale;
+            this._docViewInternal._animateScaleTime = time;
+        }
+    });
     public setAnimEffect = (presEffect: Doc, timeInMs: number, afterTrans?: () => void) => {
         this.AnimEffectTimer && clearTimeout(this.AnimEffectTimer);
         this.Document[Animation] = presEffect;
@@ -1391,10 +1419,11 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> {
         DocServer.GetRefField(docId).then(docx =>
             LightboxView.Instance.SetLightboxDoc(
                 (docx as Doc) ?? // reuse existing pivot view of documents, or else create a new collection
-                Docs.Create.StackingDocument([], { title: linkAnchor.title + '-pivot', _width: 500, _height: 500, target: linkAnchor, updateContentsScript: ScriptField.MakeScript('updateLinkCollection(this, this.target)') }, docId)
+                Docs.Create.StackingDocument([], { title: linkAnchor.title + '-pivot', _width: 500, _height: 500, target: linkAnchor, onViewMounted: ScriptField.MakeScript('updateLinkCollection(this, this.target)') }, docId)
             )
         );
     }
+    toggleFollowLink = (zoom?: boolean, setTargetToggle?: boolean): void => this._docViewInternal?.toggleFollowLink(zoom, setTargetToggle);
 
     get Document() {
         return this._props.Document;
@@ -1403,19 +1432,19 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> {
         return this._props.renderDepth === 0;
     }
     get dataDoc() {
-        return this.docView?.dataDoc ?? this.Document;
+        return this._docViewInternal?.dataDoc ?? this.Document;
     }
     get ContentDiv() {
-        return this.docView?.ContentDiv;
+        return this._docViewInternal?.ContentDiv;
     }
     get ComponentView() {
-        return this.docView?._componentView;
+        return this._docViewInternal?._componentView;
     }
     get allLinks() {
-        return (this.docView?.allLinks || []).filter(link => !link.link_matchEmbeddings || link.link_anchor_1 === this.Document || link.link_anchor_2 === this.Document);
+        return (this._docViewInternal?.allLinks || []).filter(link => !link.link_matchEmbeddings || link.link_anchor_1 === this.Document || link.link_anchor_2 === this.Document);
     }
     get LayoutFieldKey() {
-        return this.docView?.LayoutFieldKey || 'layout';
+        return this._docViewInternal?.LayoutFieldKey || 'layout';
     }
     @computed get layout_fitWidth() {
         return this._props.layout_fitWidth?.(this.layoutDoc) ?? this.layoutDoc?.layout_fitWidth;
@@ -1439,8 +1468,17 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> {
     @computed get linkCountView() {
         return <DocumentLinksButton hideCount={this.hideLinkCount} View={this} scaling={this.screenToLocalScale} OnHover={true} Bottom={this.topMost} ShowCount={true} />;
     }
+    /**
+     * path of DocumentViews terminating in the DocumentView that contains this DocumentView
+     */
+    @computed get containerViewPath() {
+        return this._props.containerViewPath;
+    }
+    /**
+     * path of DocumentViews terminating in this DocumentView
+     */
     @computed get docViewPath(): DocumentView[] {
-        return this._props.docViewPath ? [...this._props.docViewPath(), this] : [this];
+        return this.containerViewPath ? [...this.containerViewPath(), this] : [this];
     }
     @computed get layoutDoc() {
         return Doc.Layout(this.Document, this._props.LayoutTemplate?.());
@@ -1502,19 +1540,19 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> {
         return this._props.CollectionFreeFormDocumentView?.();
     }
 
-    public toggleNativeDimensions = () => this.docView && this.Document.type !== DocumentType.INK && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.NativeDimScaling, this._props.PanelWidth(), this._props.PanelHeight());
+    public toggleNativeDimensions = () => this._docViewInternal && this.Document.type !== DocumentType.INK && Doc.toggleNativeDimensions(this.layoutDoc, this._docViewInternal.NativeDimScaling, this._props.PanelWidth(), this._props.PanelHeight());
     public getBounds = () => {
-        if (!this.docView?.ContentDiv || this._props.treeViewDoc || Doc.AreProtosEqual(this._props.Document, Doc.UserDoc())) {
+        if (!this._docViewInternal?.ContentDiv || this._props.treeViewDoc || Doc.AreProtosEqual(this.Document, Doc.UserDoc())) {
             return undefined;
         }
-        if (this.docView._componentView?.screenBounds?.()) {
-            return this.docView._componentView.screenBounds();
+        if (this._docViewInternal._componentView?.screenBounds?.()) {
+            return this._docViewInternal._componentView.screenBounds();
         }
-        const xf = this.docView.ScreenToLocalBoxXf().scale(this.nativeScaling).inverse();
+        const xf = this._docViewInternal._props.ScreenToLocalTransform().scale(this.nativeScaling).inverse();
         const [[left, top], [right, bottom]] = [xf.transformPoint(0, 0), xf.transformPoint(this.panelWidth, this.panelHeight)];
 
-        if (this.docView._props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
-            const docuBox = this.docView.ContentDiv.getElementsByClassName('linkAnchorBox-cont');
+        if (this._docViewInternal._props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
+            const docuBox = this._docViewInternal.ContentDiv.getElementsByClassName('linkAnchorBox-cont');
             if (docuBox.length) return { ...docuBox[0].getBoundingClientRect(), center: undefined };
         }
         return { left, top, right, bottom };
@@ -1522,11 +1560,11 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> {
 
     public iconify(finished?: () => void, animateTime?: number) {
         this.ComponentView?.updateIcon?.();
-        const animTime = this.docView?._animateScaleTime;
-        runInAction(() => this.docView && animateTime !== undefined && (this.docView._animateScaleTime = animateTime));
+        const animTime = this._docViewInternal?._animateScaleTime;
+        runInAction(() => this._docViewInternal && animateTime !== undefined && (this._docViewInternal._animateScaleTime = animateTime));
         const finalFinished = action(() => {
             finished?.();
-            this.docView && (this.docView._animateScaleTime = animTime);
+            this._docViewInternal && (this._docViewInternal._animateScaleTime = animTime);
         });
         const layout_fieldKey = Cast(this.Document.layout_fieldKey, 'string', null);
         if (layout_fieldKey !== 'layout_icon') {
@@ -1541,12 +1579,12 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> {
     }
     @undoBatch
     setCustomView = (custom: boolean, layout: string): void => {
-        Doc.setNativeView(this._props.Document);
-        custom && DocUtils.makeCustomViewClicked(this._props.Document, Docs.Create.StackingDocument, layout, undefined);
+        Doc.setNativeView(this.Document);
+        custom && DocUtils.makeCustomViewClicked(this.Document, Docs.Create.StackingDocument, layout, undefined);
     };
 
     switchViews = (custom: boolean, view: string, finished?: () => void, useExistingLayout = false) => {
-        runInAction(() => this.docView && (this.docView._animateScalingTo = 0.1)); // shrink doc
+        runInAction(() => this._docViewInternal && (this._docViewInternal._animateScalingTo = 0.1)); // shrink doc
         setTimeout(
             action(() => {
                 if (useExistingLayout && custom && this.Document['layout_' + view]) {
@@ -1554,16 +1592,16 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> {
                 } else {
                     this.setCustomView(custom, view);
                 }
-                this.docView && (this.docView._animateScalingTo = 1); // expand it
+                this._docViewInternal && (this._docViewInternal._animateScalingTo = 1); // expand it
                 setTimeout(
                     action(() => {
-                        this.docView && (this.docView._animateScalingTo = 0);
+                        this._docViewInternal && (this._docViewInternal._animateScalingTo = 0);
                         finished?.();
                     }),
-                    this.docView ? Math.max(0, this.docView.animateScaleTime - 10) : 0
+                    this._docViewInternal ? Math.max(0, this._docViewInternal.animateScaleTime - 10) : 0
                 );
             }),
-            this.docView ? Math.max(0, this.docView?.animateScaleTime - 10) : 0
+            this._docViewInternal ? Math.max(0, this._docViewInternal?.animateScaleTime - 10) : 0
         );
     };
 
@@ -1606,29 +1644,21 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> {
 
     componentDidMount() {
         runInAction(() => this.Document[DocViews].add(this));
-        this._disposers.updateContentsScript = reaction(() => ScriptCast(this.Document.updateContentsScript)?.script?.run({ this: this.Document }).result, emptyFunction);
-        this._disposers.height = reaction(
-            // increase max auto height if document has been resized to be greater than current max
-            () => NumCast(this.layoutDoc._height),
-            action(height => {
-                const docMax = NumCast(this.layoutDoc.layout_maxAutoHeight);
-                if (docMax && docMax < height) this.layoutDoc.layout_maxAutoHeight = height;
-            })
-        );
-        !BoolCast(this._props.Document.dontRegisterView, this._props.dontRegisterView) && DocumentManager.Instance.AddView(this);
+        this._disposers.onViewMounted = reaction(() => ScriptCast(this.Document.onViewMounted)?.script?.run({ this: this.Document }).result, emptyFunction);
+        !BoolCast(this.Document.dontRegisterView, this._props.dontRegisterView) && DocumentManager.Instance.AddView(this);
     }
 
     componentWillUnmount() {
-        this.Document[DocViews].delete(this);
+        runInAction(() => this.Document[DocViews].delete(this));
         Object.values(this._disposers).forEach(disposer => disposer?.());
-        !BoolCast(this._props.Document.dontRegisterView, this._props.dontRegisterView) && DocumentManager.Instance.RemoveView(this);
+        !BoolCast(this.Document.dontRegisterView, this._props.dontRegisterView) && DocumentManager.Instance.RemoveView(this);
     }
     // want the htmloverlay to be able to fade in but we also want it to be display 'none' until it is needed.
     // unfortunately, CSS can't transition animate any properties for something that is display 'none'.
     // so we need to first activate the div, then, after a render timeout, start the opacity transition.
     @observable enableHtmlOverlayTransitions: boolean = false;
     @computed get htmlOverlay() {
-        const effect = StrCast(this.htmlOverlayEffect?.presentation_effect, StrCast(this.htmlOverlayEffect?.followLinkAnimEffect));
+        const effect = StrCast(this._htmlOverlayEffect?.presentation_effect, StrCast(this._htmlOverlayEffect?.followLinkAnimEffect));
         return (
             <div
                 className="documentView-htmlOverlay"
@@ -1638,13 +1668,13 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> {
                         setTimeout(action(() => (this.enableHtmlOverlayTransitions = val)));
                     }
                 }}
-                style={{ display: !this.textHtmlOverlay ? 'none' : undefined }}>
+                style={{ display: !this._htmlOverlayText ? 'none' : undefined }}>
                 <div className="documentView-htmlOverlayInner" style={{ transition: `all 500ms`, opacity: this.enableHtmlOverlayTransitions ? 0.9 : 0 }}>
                     {DocumentViewInternal.AnimationEffect(
                         <div className="webBox-textHighlight">
-                            <ObserverJsxParser autoCloseVoidElements={true} key={42} onError={(e: any) => console.log('PARSE error', e)} renderInWrapper={false} jsx={StrCast(this.textHtmlOverlay)} />
+                            <ObserverJsxParser autoCloseVoidElements={true} key={42} onError={(e: any) => console.log('PARSE error', e)} renderInWrapper={false} jsx={StrCast(this._htmlOverlayText)} />
                         </div>,
-                        { ...(this.htmlOverlayEffect ?? {}), presentation_effect: effect ?? PresEffect.Zoom } as any as Doc,
+                        { ...(this._htmlOverlayEffect ?? {}), presentation_effect: effect ?? PresEffect.Zoom } as any as Doc,
                         this.Document
                     )}
                 </div>
@@ -1663,7 +1693,7 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> {
 
         return (
             <div className="contentFittingDocumentView" onPointerEnter={action(() => (this._isHovering = true))} onPointerLeave={action(() => (this._isHovering = false))}>
-                {!this._props.Document || !this._props.PanelWidth() ? null : (
+                {!this.Document || !this._props.PanelWidth() ? null : (
                     <div
                         className="contentFittingDocumentView-previewDoc"
                         ref={this.ContentRef}
@@ -1676,7 +1706,7 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> {
                         <DocumentViewInternal
                             {...this._props}
                             DocumentView={this.selfView}
-                            viewPath={this.docViewPathFunc}
+                            docViewPath={this.docViewPathFunc}
                             PanelWidth={this.PanelWidth}
                             PanelHeight={this.PanelHeight}
                             NativeWidth={this.NativeWidth}
@@ -1687,7 +1717,7 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> {
                             layout_fitWidth={this.layout_fitWidthFunc}
                             ScreenToLocalTransform={this.screenToContentsTransform}
                             focus={this._props.focus || emptyFunction}
-                            ref={action((r: DocumentViewInternal | null) => r && (this.docView = r))}
+                            ref={action((r: DocumentViewInternal | null) => r && (this._docViewInternal = r))}
                         />
                         {this.htmlOverlay}
                         {this.infoUI}
diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx
index ff92c701f..ebad257ac 100644
--- a/src/client/views/nodes/EquationBox.tsx
+++ b/src/client/views/nodes/EquationBox.tsx
@@ -27,7 +27,7 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     componentDidMount() {
         this._props.setContentView?.(this);
-        if (EquationBox.SelectOnLoad === this.Document[Id] && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this._props.docViewPath()))) {
+        if (EquationBox.SelectOnLoad === this.Document[Id] && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.docViewPath))) {
             this._props.select(false);
 
             this._ref.current!.mathField.focus();
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 008f10f26..b456807d7 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -6,23 +6,18 @@ import { Doc, Field, FieldResult, Opt } from '../../../fields/Doc';
 import { List } from '../../../fields/List';
 import { ScriptField } from '../../../fields/ScriptField';
 import { WebField } from '../../../fields/URLField';
-import { DocumentViewSharedProps } from './DocumentView';
+import { DocumentView, DocumentViewInternalSharedProps, DocumentViewSharedProps } from './DocumentView';
 
 //
 // these properties get assigned through the render() method of the DocumentView when it creates this node.
 // However, that only happens because the properties are "defined" in the markup for the field view.
 // See the LayoutString method on each field view :   ImageBox, FormattedTextBox, etc.
 //
-export interface FieldViewProps extends DocumentViewSharedProps {
+export interface FieldViewProps extends DocumentViewSharedProps, DocumentViewInternalSharedProps {
     // FieldView specific props that are not part of DocumentView props
     fieldKey: string;
 
-    select: (isCtrlPressed: boolean) => void;
-    isContentActive: (outsideReaction?: boolean) => boolean | undefined;
-    isDocumentActive?: () => boolean | undefined;
-    isSelected: () => boolean;
     setHeight?: (height: number) => void;
-    NativeDimScaling?: () => number; // scaling the DocumentView does to transform its contents into its panel & needed by ScreenToLocal NOTE: Must also be added to DocumentViewInternalsProps
     onBrowseClick?: () => ScriptField | undefined;
     onKey?: (e: React.KeyboardEvent, fieldProps: FieldViewProps) => boolean | undefined;
     pointerEvents?: () => Opt<string>;
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index a5853499f..4483d12ce 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -158,7 +158,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     @undoBatch
     setNativeSize = action(() => {
-        const scaling = (this._props.DocumentView?.().screenToViewTransform().Scale || 1) / NumCast(this.layoutDoc._freeform_scale, 1);
+        const scaling = (this.DocumentView?.().screenToViewTransform().Scale || 1) / NumCast(this.layoutDoc._freeform_scale, 1);
         const nscale = NumCast(this._props.PanelWidth()) / scaling;
         const nw = nscale / NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']);
         this.dataDoc[this.fieldKey + '_nativeHeight'] = NumCast(this.dataDoc[this.fieldKey + '_nativeHeight']) * nw;
@@ -368,7 +368,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     }
     screenToLocalTransform = () => this.ScreenToLocalBoxXf().translate(0, NumCast(this.layoutDoc._layout_scrollTop) * this.ScreenToLocalBoxXf().Scale);
     marqueeDown = (e: React.PointerEvent) => {
-        if (!e.altKey && e.button === 0 && NumCast(this.layoutDoc._freeform_scale, 1) <= NumCast(this.dataDoc.freeform_scaleMin, 1) && this._props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
+        if (!e.altKey && e.button === 0 && NumCast(this.layoutDoc._freeform_scale, 1) <= NumCast(this.dataDoc.freeform_scaleMin, 1) && this._props.isContentActive() && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
             setupMoveUpEvents(
                 this,
                 e,
@@ -444,7 +444,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                     {this.content}
                 </CollectionFreeFormView>
                 {this.annotationLayer}
-                {!this._mainCont.current || !this._annotationLayer.current ? null : (
+                {!this._mainCont.current || !this.DocumentView || !this._annotationLayer.current ? null : (
                     <MarqueeAnnotator
                         Document={this.Document}
                         ref={this._marqueeref}
@@ -452,7 +452,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                         annotationLayerScrollTop={0}
                         scaling={returnOne}
                         annotationLayerScaling={this._props.NativeDimScaling}
-                        docView={this._props.DocumentView!}
+                        docView={this.DocumentView}
                         addDocument={this.addDocument}
                         finishMarquee={this.finishMarquee}
                         savedAnnotations={this.savedAnnotations}
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 7bc9d3f85..b2aab2422 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -61,33 +61,11 @@ export class KeyValuePair extends ObservableReactComponent<KeyValuePairProps> {
     };
 
     render() {
-        const props: FieldViewProps = {
-            Document: this._props.doc,
-            childFilters: returnEmptyFilter,
-            childFiltersByRanges: returnEmptyFilter,
-            searchFilterDocs: returnEmptyDoclist,
-            styleProvider: DefaultStyleProvider,
-            docViewPath: returnEmptyDoclist,
-            fieldKey: this._props.keyName,
-            isSelected: returnFalse,
-            setHeight: returnFalse,
-            select: emptyFunction,
-            bringToFront: emptyFunction,
-            renderDepth: 1,
-            isContentActive: returnFalse,
-            whenChildContentsActiveChanged: emptyFunction,
-            ScreenToLocalTransform: Transform.Identity,
-            focus: emptyFunction,
-            PanelWidth: this._props.PanelWidth,
-            PanelHeight: this._props.PanelHeight,
-            addDocTab: returnFalse,
-            pinToPres: returnZero,
-        };
         // let fieldKey = Object.keys(props.Document).indexOf(props.fieldKey) !== -1 ? props.fieldKey : "(" + props.fieldKey + ")";
         let protoCount = 0;
-        let doc: Doc | undefined = props.Document;
+        let doc = this._props.doc;
         while (doc) {
-            if (Object.keys(doc).includes(props.fieldKey)) {
+            if (Object.keys(doc).includes(this._props.keyName)) {
                 break;
             }
             protoCount++;
@@ -106,17 +84,17 @@ export class KeyValuePair extends ObservableReactComponent<KeyValuePairProps> {
                             style={hover}
                             className="keyValuePair-td-key-delete"
                             onClick={undoBatch(() => {
-                                if (Object.keys(props.Document).indexOf(props.fieldKey) !== -1) {
-                                    delete props.Document[props.fieldKey];
-                                } else delete DocCast(props.Document.proto)?.[props.fieldKey];
+                                if (Object.keys(this._props.doc).indexOf(this._props.keyName) !== -1) {
+                                    delete this._props.doc[this._props.keyName];
+                                } else delete DocCast(this._props.doc.proto)?.[this._props.keyName];
                             })}>
                             X
                         </button>
                         <input className="keyValuePair-td-key-check" type="checkbox" style={hover} onChange={this.handleCheck} ref={this.checkbox} />
-                        <Tooltip title={Object.entries(new DocumentOptions()).find((pair: [string, FInfo]) => pair[0].replace(/^_/, '') === props.fieldKey)?.[1].description ?? ''}>
-                            <div className="keyValuePair-keyField" style={{ marginLeft: 20 * (props.fieldKey.match(/_/g)?.length || 0), color: keyStyle }}>
+                        <Tooltip title={Object.entries(new DocumentOptions()).find((pair: [string, FInfo]) => pair[0].replace(/^_/, '') === this._props.keyName)?.[1].description ?? ''}>
+                            <div className="keyValuePair-keyField" style={{ marginLeft: 20 * (this._props.keyName.match(/_/g)?.length || 0), color: keyStyle }}>
                                 {'('.repeat(parenCount)}
-                                {props.fieldKey}
+                                {this._props.keyName}
                                 {')'.repeat(parenCount)}
                             </div>
                         </Tooltip>
@@ -124,7 +102,32 @@ export class KeyValuePair extends ObservableReactComponent<KeyValuePairProps> {
                 </td>
                 <td className="keyValuePair-td-value" style={{ width: `${100 - this._props.keyWidth}%` }} onContextMenu={this.onContextMenu}>
                     <div className="keyValuePair-td-value-container">
-                        <EditableView contents={undefined} fieldContents={props} GetValue={() => Field.toKeyValueString(props.Document, props.fieldKey)} SetValue={(value: string) => KeyValueBox.SetField(props.Document, props.fieldKey, value)} />
+                        <EditableView
+                            contents={undefined}
+                            fieldContents={{
+                                Document: this._props.doc,
+                                childFilters: returnEmptyFilter,
+                                childFiltersByRanges: returnEmptyFilter,
+                                searchFilterDocs: returnEmptyDoclist,
+                                styleProvider: DefaultStyleProvider,
+                                fieldKey: this._props.keyName,
+                                isSelected: returnFalse,
+                                setHeight: returnFalse,
+                                select: emptyFunction,
+                                bringToFront: emptyFunction,
+                                renderDepth: 1,
+                                isContentActive: returnFalse,
+                                whenChildContentsActiveChanged: emptyFunction,
+                                ScreenToLocalTransform: Transform.Identity,
+                                focus: emptyFunction,
+                                PanelWidth: this._props.PanelWidth,
+                                PanelHeight: this._props.PanelHeight,
+                                addDocTab: returnFalse,
+                                pinToPres: returnZero,
+                            }}
+                            GetValue={() => Field.toKeyValueString(this._props.doc, this._props.keyName)}
+                            SetValue={(value: string) => KeyValueBox.SetField(this._props.doc, this._props.keyName, value)}
+                        />
                     </div>
                 </td>
             </tr>
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index b86ba72a0..864c1955b 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -7,13 +7,13 @@ import { TraceMobx } from '../../../fields/util';
 import { DragManager } from '../../util/DragManager';
 import { LinkFollower } from '../../util/LinkFollower';
 import { SelectionManager } from '../../util/SelectionManager';
-import { ViewBoxBaseComponent } from '../DocComponent';
+import { ViewBoxBaseComponent, ViewBoxBaseProps } from '../DocComponent';
 import { StyleProp } from '../StyleProvider';
 import { FieldView, FieldViewProps } from './FieldView';
 import './LinkAnchorBox.scss';
 import { LinkInfo } from './LinkDocPreview';
 const { default: { MEDIUM_GRAY }, } = require('../global/globalCssVariables.module.scss'); // prettier-ignore
-export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
+export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps & ViewBoxBaseProps>() {
     public static LayoutString(fieldKey: string) {
         return FieldView.LayoutString(LinkAnchorBox, fieldKey);
     }
@@ -33,12 +33,13 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
 
     @computed get linkSource() {
-        return this._props.docViewPath()[this._props.docViewPath().length - 2].Document; // this._props.styleProvider?.(this.dataDoc, this._props, StyleProp.LinkSource);
+        return this.containerViewPath?.().lastElement().Document; // this._props.styleProvider?.(this.dataDoc, this._props, StyleProp.LinkSource);
     }
 
     onPointerDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, (e, doubleTap) => {
-            if (doubleTap) LinkFollower.FollowLink(this.Document, this.linkSource, false);
+        const linkSource = this.linkSource;
+        linkSource && setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, (e, doubleTap) => {
+            if (doubleTap) LinkFollower.FollowLink(this.Document, linkSource, false);
             else this._props.select(false);
         });
     };
@@ -86,7 +87,8 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 className={`linkAnchorBox-cont${small ? '-small' : ''}`}
                 onPointerEnter={e =>
                     LinkInfo.SetLinkInfo({
-                        docProps: this._props,
+                        DocumentView: this.DocumentView,
+                        styleProvider: this._props.styleProvider,
                         linkSrc: this.linkSource,
                         linkDoc: this.Document,
                         showHeader: true,
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index 7f1d41547..4221f464d 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -29,12 +29,12 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @computed get anchor1() {
         const anchor1 = DocCast(this.dataDoc.link_anchor_1);
         const anchor_1 = anchor1?.layout_unrendered ? DocCast(anchor1.annotationOn) : anchor1;
-        return DocumentManager.Instance.getDocumentView(anchor_1, this._props.docViewPath()[this._props.docViewPath().length - 2]); // this._props.docViewPath().lastElement());
+        return DocumentManager.Instance.getDocumentView(anchor_1, this.containerViewPath?.().lastElement());
     }
     @computed get anchor2() {
         const anchor2 = DocCast(this.dataDoc.link_anchor_2);
         const anchor_2 = anchor2?.layout_unrendered ? DocCast(anchor2.annotationOn) : anchor2;
-        return DocumentManager.Instance.getDocumentView(anchor_2, this._props.docViewPath()[this._props.docViewPath().length - 2]); // this._props.docViewPath().lastElement());
+        return DocumentManager.Instance.getDocumentView(anchor_2, this.containerViewPath?.().lastElement());
     }
     screenBounds = () => {
         if (this.layoutDoc._layout_isSvg && this.anchor1 && this.anchor2 && this.anchor1.CollectionFreeFormView) {
@@ -66,7 +66,7 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     const a = (this.anchor1 ?? this.anchor2)!;
                     const b = (this.anchor2 ?? this.anchor1)!;
 
-                    const parxf = this._props.docViewPath()[this._props.docViewPath().length - 2].ComponentView as CollectionFreeFormView;
+                    const parxf = this.containerViewPath?.().lastElement().ComponentView as CollectionFreeFormView;
                     const this_xf = parxf?.screenToFreeformContentsXf ?? Transform.Identity; //this.ScreenToLocalTransform();
                     const a_invXf = a.screenToViewTransform().inverse();
                     const b_invXf = b.screenToViewTransform().inverse();
@@ -161,12 +161,11 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return (
             <div className={`linkBox-container${this._props.isContentActive() ? '-interactive' : ''}`} style={{ background: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor) }}>
                 <ComparisonBox
-                    {...this._props}
+                    {...this._props} //
                     fieldKey="link_anchor"
                     setHeight={emptyFunction}
                     dontRegisterView={true}
                     renderDepth={this._props.renderDepth + 1}
-                    isContentActive={this._props.isContentActive}
                     addDocument={returnFalse}
                     removeDocument={returnFalse}
                     moveDocument={returnFalse}
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index ea23ecbea..ed4878d9d 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -18,8 +18,9 @@ import { SearchUtil } from '../../util/SearchUtil';
 import { SettingsManager } from '../../util/SettingsManager';
 import { Transform } from '../../util/Transform';
 import { ObservableReactComponent } from '../ObservableReactComponent';
-import { DocumentView, DocumentViewSharedProps, OpenWhere } from './DocumentView';
+import { DocumentView, DocumentViewSharedProps, OpenWhere, StyleProviderFunc } from './DocumentView';
 import './LinkDocPreview.scss';
+import { FieldViewProps } from './FieldView';
 
 export class LinkInfo {
     private static _instance: Opt<LinkInfo>;
@@ -43,7 +44,8 @@ export class LinkInfo {
 interface LinkDocPreviewProps {
     linkDoc?: Doc;
     linkSrc?: Doc;
-    docProps: DocumentViewSharedProps;
+    DocumentView?: () => DocumentView;
+    styleProvider?: StyleProviderFunc;
     location: number[];
     hrefs?: string[];
     showHeader?: boolean;
@@ -152,7 +154,7 @@ export class LinkDocPreview extends ObservableReactComponent<LinkDocPreviewProps
             action(() => {
                 LinkManager.currentLink = this._linkDoc;
                 LinkManager.currentLinkAnchor = this._linkSrc;
-                this._props.docProps.DocumentView?.().select(false);
+                this._props.DocumentView?.().select(false);
                 if ((SettingsManager.Instance.propertiesWidth ?? 0) < 100) {
                     SettingsManager.Instance.propertiesWidth = 250;
                 }
@@ -269,8 +271,8 @@ export class LinkDocPreview extends ObservableReactComponent<LinkDocPreviewProps
                             }}
                             Document={this._targetDoc!}
                             moveDocument={returnFalse}
-                            styleProvider={this._props.docProps?.styleProvider}
-                            docViewPath={returnEmptyDoclist}
+                            styleProvider={this._props.styleProvider}
+                            containerViewPath={returnEmptyDoclist}
                             ScreenToLocalTransform={Transform.Identity}
                             isDocumentActive={returnFalse}
                             isContentActive={returnFalse}
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 3575b21e4..56fb157da 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -379,11 +379,11 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         });
 
         const targetCreator = (annotationOn: Doc | undefined) => {
-            const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, undefined, annotationOn, undefined, 'yellow');
+            const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, undefined, annotationOn, 'yellow');
             FormattedTextBox.SetSelectOnLoad(target);
             return target;
         };
-        const docView = this._props.DocumentView?.();
+        const docView = this.DocumentView?.();
         docView &&
             DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(docView, sourceAnchorCreator, targetCreator), e.pageX, e.pageY, {
                 dragComplete: e => {
diff --git a/src/client/views/nodes/MapBox/MapPushpinBox.tsx b/src/client/views/nodes/MapBox/MapPushpinBox.tsx
index 34e237007..fc5b4dd18 100644
--- a/src/client/views/nodes/MapBox/MapPushpinBox.tsx
+++ b/src/client/views/nodes/MapBox/MapPushpinBox.tsx
@@ -18,10 +18,10 @@ export class MapPushpinBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
 
     get mapBoxView() {
-        return this._props.DocumentView?.()?._props.docViewPath().lastElement()?.ComponentView as MapBox;
+        return this.DocumentView?.()?.containerViewPath?.().lastElement()?.ComponentView as MapBox;
     }
     get mapBox() {
-        return this._props.DocumentView?.()._props.docViewPath().lastElement()?.Document;
+        return this.DocumentView?.().containerViewPath?.().lastElement()?.Document;
     }
 
     render() {
diff --git a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
index ea8496c99..e079f7457 100644
--- a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
+++ b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
@@ -228,11 +228,11 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
         });
 
         const targetCreator = (annotationOn: Doc | undefined) => {
-            const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, undefined, annotationOn, undefined, 'yellow');
+            const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, undefined, annotationOn, 'yellow');
             FormattedTextBox.SetSelectOnLoad(target);
             return target;
         };
-        const docView = this._props.DocumentView?.();
+        const docView = this.DocumentView?.();
         docView &&
             DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(docView, sourceAnchorCreator, targetCreator), e.pageX, e.pageY, {
                 dragComplete: e => {
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 959d5d88d..71c1b6a4c 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -102,7 +102,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         Doc.GetProto(region).followLinkToggle = true;
         this.addDocument(region);
 
-        const docViewContent = this._props.docViewPath().lastElement().ContentDiv!;
+        const docViewContent = this.DocumentView?.().ContentDiv!;
         const newDiv = docViewContent.cloneNode(true) as HTMLDivElement;
         newDiv.style.width = NumCast(this.layoutDoc._width).toString();
         newDiv.style.height = NumCast(this.layoutDoc._height).toString();
@@ -163,7 +163,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     updateIcon = () => {
         // currently we render pdf icons as text labels
-        const docViewContent = this._props.docViewPath().lastElement().ContentDiv!;
+        const docViewContent = this.DocumentView?.().ContentDiv!;
         const filename = this.layoutDoc[Id] + '-icon' + new Date().getTime();
         this._pdfViewer?._mainCont.current &&
             CollectionFreeFormView.UpdateIcon(
@@ -304,7 +304,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     setPdfViewer = (pdfViewer: PDFViewer) => {
         this._pdfViewer = pdfViewer;
-        const docView = this._props.DocumentView?.();
+        const docView = this.DocumentView?.();
         if (this._initialScrollTarget && docView) {
             this.focus(this._initialScrollTarget, { instant: true });
             this._initialScrollTarget = undefined;
@@ -525,7 +525,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     removeDocument={this.removeDocument}
                 />
             ) : (
-                <div onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => SelectionManager.SelectView(this._props.DocumentView?.()!, false), true)}>
+                <div onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => SelectionManager.SelectView(this.DocumentView?.()!, false), true)}>
                     <ComponentTag
                         {...this._props}
                         setContentView={emptyFunction} // override setContentView to do nothing
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index fb42286af..1913ab032 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -21,7 +21,7 @@ import { CollectionFreeFormView } from '../collections/collectionFreeForm/Collec
 import { CollectionStackedTimeline, TrimScope } from '../collections/CollectionStackedTimeline';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
-import { ViewBoxAnnotatableComponent } from '../DocComponent';
+import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
 import { MarqueeAnnotator } from '../MarqueeAnnotator';
 import { AnchorMenu } from '../pdf/AnchorMenu';
 import { StyleProp } from '../StyleProvider';
@@ -44,7 +44,7 @@ import './VideoBox.scss';
  */
 
 @observer
-export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
+export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
     public static LayoutString(fieldKey: string) {
         return FieldView.LayoutString(VideoBox, fieldKey);
     }
@@ -608,7 +608,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     // removes from currently playing display
     @action
     removeCurrentlyPlaying = () => {
-        const docView = this._props.DocumentView?.();
+        const docView = this.DocumentView?.();
         if (CollectionStackedTimeline.CurrentlyPlaying && docView) {
             const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(docView);
             index !== -1 && CollectionStackedTimeline.CurrentlyPlaying.splice(index, 1);
@@ -617,7 +617,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     // adds doc to currently playing display
     @action
     addCurrentlyPlaying = () => {
-        const docView = this._props.DocumentView?.();
+        const docView = this.DocumentView?.();
         if (!CollectionStackedTimeline.CurrentlyPlaying) {
             CollectionStackedTimeline.CurrentlyPlaying = [];
         }
@@ -749,7 +749,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
 
     // starts marquee selection
     marqueeDown = (e: React.PointerEvent) => {
-        if (!e.altKey && e.button === 0 && NumCast(this.layoutDoc._freeform_scale, 1) === 1 && this._props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(Doc.ActiveTool)) {
+        if (!e.altKey && e.button === 0 && NumCast(this.layoutDoc._freeform_scale, 1) === 1 && this._props.isContentActive() && ![InkTool.Highlighter, InkTool.Pen].includes(Doc.ActiveTool)) {
             setupMoveUpEvents(
                 this,
                 e,
@@ -941,7 +941,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                 style={{
                     pointerEvents: this.layoutDoc._lockedPosition ? 'none' : undefined,
                     borderRadius,
-                    overflow: this._props.docViewPath?.().slice(-1)[0].layout_fitWidth ? 'auto' : undefined,
+                    overflow: this.DocumentView?.().layout_fitWidth ? 'auto' : undefined,
                 }}>
                 <div className="videoBox-viewer" onPointerDown={this.marqueeDown}>
                     <div
@@ -978,7 +978,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                         </CollectionFreeFormView>
                     </div>
                     {this.annotationLayer}
-                    {!this._mainCont.current || !this._annotationLayer.current ? null : (
+                    {!this._mainCont.current || !this.DocumentView || !this._annotationLayer.current ? null : (
                         <MarqueeAnnotator
                             ref={this._marqueeref}
                             Document={this.Document}
@@ -986,7 +986,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                             annotationLayerScrollTop={0}
                             scaling={returnOne}
                             annotationLayerScaling={this._props.NativeDimScaling}
-                            docView={this._props.DocumentView!}
+                            docView={this.DocumentView}
                             containerOffset={this.marqueeOffset}
                             addDocument={this.addDocWithTimecode}
                             finishMarquee={this.finishMarquee}
@@ -1004,7 +1004,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     }
 
     @computed get UIButtons() {
-        const bounds = this._props.docViewPath().lastElement().getBounds();
+        const bounds = this.DocumentView?.().getBounds();
         const width = (bounds?.right || 0) - (bounds?.left || 0);
         const curTime = NumCast(this.layoutDoc._layout_currentTimecode);
         return (
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index c3be2b390..86709e4d7 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -33,7 +33,7 @@ import { Annotation } from '../pdf/Annotation';
 import { GPTPopup } from '../pdf/GPTPopup/GPTPopup';
 import { SidebarAnnos } from '../SidebarAnnos';
 import { StyleProp } from '../StyleProvider';
-import { DocComponentView, DocFocusOptions, DocumentView, DocumentViewInternalProps, DocumentViewProps, OpenWhere } from './DocumentView';
+import { DocComponentView, DocFocusOptions, DocumentView, OpenWhere } from './DocumentView';
 import { FieldView, FieldViewProps } from './FieldView';
 import { LinkInfo } from './LinkDocPreview';
 import { PinProps, PresBox } from './trails';
@@ -250,7 +250,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @action
     createTextAnnotation = (sel: Selection, selRange: Range | undefined) => {
         if (this._mainCont.current && selRange) {
-            if (this.dataDoc[this._props.fieldKey] instanceof HtmlField) this._mainCont.current.style.transform = `rotate(${NumCast(this._props.DocumentView!().screenToContentsTransform().RotateDeg)}deg)`;
+            if (this.dataDoc[this._props.fieldKey] instanceof HtmlField) this._mainCont.current.style.transform = `rotate(${NumCast(this.DocumentView!().screenToContentsTransform().RotateDeg)}deg)`;
             const clientRects = selRange.getClientRects();
             for (let i = 0; i < clientRects.length; i++) {
                 const rect = clientRects.item(i);
@@ -274,7 +274,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
         // clear selection
         this._textAnnotationCreator = undefined;
-        if (sel.empty) sel.empty(); // Chrome
+        if (sel.empty)
+            sel.empty(); // Chrome
         else if (sel.removeAllRanges) sel.removeAllRanges(); // Firefox
         return this._savedAnnotations;
     };
@@ -297,8 +298,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     @action
     getView = (doc: Doc, options: DocFocusOptions) => {
-        if (Doc.AreProtosEqual(doc, this.Document)) return new Promise<Opt<DocumentView>>(res => res(this._props.DocumentView?.()));
-        if (this.Document.layout_fieldKey === 'layout_icon') this._props.DocumentView?.().iconify();
+        if (Doc.AreProtosEqual(doc, this.Document)) return new Promise<Opt<DocumentView>>(res => res(this.DocumentView?.()));
+        if (this.Document.layout_fieldKey === 'layout_icon') this.DocumentView?.().iconify();
         const webUrl = WebCast(doc.config_data)?.url;
         if (this._url && webUrl && webUrl.href !== this._url) this.setData(webUrl.href);
         if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) this.toggleSidebar(false);
@@ -343,7 +344,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     iframeUp = (e: PointerEvent) => {
         this._getAnchor = AnchorMenu.Instance?.GetAnchor; // need to save AnchorMenu's getAnchor since a subsequent selection on another doc will overwrite this value
         this._textAnnotationCreator = undefined;
-        this._props.docViewPath().lastElement()?.docView?.cleanupPointerEvents(); // pointerup events aren't generated on containing document view, so we have to invoke it here.
+        this.DocumentView?.()?.cleanupPointerEvents(); // pointerup events aren't generated on containing document view, so we have to invoke it here.
         if (this._iframe?.contentWindow && this._iframe.contentDocument && !this._iframe.contentWindow.getSelection()?.isCollapsed) {
             const mainContBounds = Utils.GetScreenTransform(this._mainCont.current!);
             const scale = (this._props.NativeDimScaling?.() || 1) * mainContBounds.scale;
@@ -364,7 +365,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         e.stopPropagation();
         const sel = window.getSelection();
         this._textAnnotationCreator = undefined;
-        if (sel?.empty) sel.empty(); // Chrome
+        if (sel?.empty)
+            sel.empty(); // Chrome
         else if (sel?.removeAllRanges) sel.removeAllRanges(); // Firefox
         // bcz: NEED TO unrotate e.clientX and e.clientY
         const word = getWordAtPoint(e.target, e.clientX, e.clientY);
@@ -556,7 +558,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             clearStyleSheetRules(WebBox.webStyleSheet);
             this._scrollTimer = undefined;
             const newScrollTop = scrollTop > iframeHeight ? iframeHeight : scrollTop;
-            if (!LinkInfo.Instance?.LinkInfo && this._outerRef.current && newScrollTop !== this.layoutDoc.thumbScrollTop && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this._props.docViewPath()))) {
+            if (!LinkInfo.Instance?.LinkInfo && this._outerRef.current && newScrollTop !== this.layoutDoc.thumbScrollTop && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.docViewPath))) {
                 this.layoutDoc.thumb = undefined;
                 this.layoutDoc.thumbScrollTop = undefined;
                 this.layoutDoc.thumbNativeWidth = undefined;
@@ -723,10 +725,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     onMarqueeDown = (e: React.PointerEvent) => {
         const sel = this._url ? this._iframe?.contentDocument?.getSelection() : window.document.getSelection();
         this._textAnnotationCreator = undefined;
-        if (sel?.empty) sel.empty(); // Chrome
+        if (sel?.empty)
+            sel.empty(); // Chrome
         else if (sel?.removeAllRanges) sel.removeAllRanges(); // Firefox
         this.marqueeing = [e.clientX, e.clientY];
-        if (!e.altKey && e.button === 0 && this._props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
+        if (!e.altKey && e.button === 0 && this._props.isContentActive() && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
             setupMoveUpEvents(
                 this,
                 e,
@@ -750,7 +753,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.marqueeing = undefined;
         this._textAnnotationCreator = undefined;
         const sel = this._url ? this._iframe?.contentDocument?.getSelection() : window.document.getSelection();
-        if (sel?.empty) sel.empty(); // Chrome
+        if (sel?.empty)
+            sel.empty(); // Chrome
         else if (sel?.removeAllRanges) sel.removeAllRanges(); // Firefox
         this._setPreviewCursor?.(x ?? 0, y ?? 0, false, !this._marqueeref.current?.isEmpty, this.Document);
         if (x !== undefined && y !== undefined) {
@@ -762,7 +766,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 setTimeout(() => {
                     // if menu comes up right away, the down event can still be active causing a menu item to be selected
                     this.specificContextMenu(undefined as any);
-                    this._props.docViewPath().lastElement().docView?.onContextMenu(undefined, x, y);
+                    this.DocumentView?.().onContextMenu(undefined, x, y);
                 });
             }
         }
@@ -908,7 +912,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     });
     @action
     onZoomWheel = (e: React.WheelEvent) => {
-        if (this._props.isContentActive(true)) {
+        if (this._props.isContentActive()) {
             e.stopPropagation();
         }
     };
@@ -1065,7 +1069,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
     transparentFilter = () => [...this._props.childFilters(), Utils.TransparentBackgroundFilter];
     opaqueFilter = () => [...this._props.childFilters(), Utils.noDragDocsFilter, ...(SnappingManager.CanEmbed ? [] : [Utils.OpaqueBackgroundFilter])];
-    childStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewInternalProps | FieldViewProps>, property: string): any => {
+    childStyleProvider = (doc: Doc | undefined, props: Opt<FieldViewProps>, property: string): any => {
         if (doc instanceof Doc && property === StyleProp.PointerEvents) {
             if (this.inlineTextAnnotations.includes(doc)) return 'none';
         }
@@ -1099,7 +1103,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     onContextMenu={this.specificContextMenu}>
                     {this.webpage}
                 </div>
-                {!this._mainCont.current || !this._annotationLayer.current ? null : (
+                {!this._mainCont.current || !this.DocumentView || !this._annotationLayer.current ? null : (
                     <div style={{ position: 'absolute', height: '100%', width: '100%', pointerEvents: this.marqueeing ? 'all' : 'none' }}>
                         <MarqueeAnnotator
                             ref={this._marqueeref}
@@ -1109,7 +1113,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                             annotationLayerScrollTop={0}
                             scaling={this._props.NativeDimScaling}
                             addDocument={this.addDocumentWrapper}
-                            docView={this._props.DocumentView!}
+                            docView={this.DocumentView}
                             finishMarquee={this.finishMarquee}
                             savedAnnotations={this.savedAnnotationsCreator}
                             selectionText={this.selectionText}
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 6a2cd359c..cc58c2071 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -6,7 +6,7 @@ import * as ReactDOM from 'react-dom/client';
 import { Doc } from '../../../../fields/Doc';
 import { Height, Width } from '../../../../fields/DocSymbols';
 import { NumCast } from '../../../../fields/Types';
-import { emptyFunction, returnFalse, Utils } from '../../../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnFalse, Utils } from '../../../../Utils';
 import { DocServer } from '../../../DocServer';
 import { Docs, DocUtils } from '../../../documents/Documents';
 import { Transform } from '../../../util/Transform';
@@ -207,7 +207,7 @@ export class DashDocViewInternal extends ObservableReactComponent<IDashDocViewIn
                     isDocumentActive={returnFalse}
                     isContentActive={this.isContentActive}
                     styleProvider={this._textBox._props.styleProvider}
-                    docViewPath={this._textBox._props.docViewPath}
+                    containerViewPath={this._textBox.docViewPathFunc ?? returnEmptyDoclist}
                     ScreenToLocalTransform={this.getDocTransform}
                     addDocTab={this._textBox._props.addDocTab}
                     pinToPres={returnFalse}
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 555b752f0..3f2ab0ec5 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -148,7 +148,7 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi
     }
 
     createPivotForField = (e: React.MouseEvent) => {
-        let container = this._props.tbox._props.DocumentView?.()._props.docViewPath().lastElement();
+        let container = this._props.tbox.containerViewPath?.().lastElement();
         if (container) {
             const embedding = Doc.MakeEmbedding(container.Document);
             embedding._type_collection = CollectionViewType.Time;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index ae26f170b..6a6cec094 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -42,7 +42,7 @@ import { CollectionStackingView } from '../../collections/CollectionStackingView
 import { CollectionTreeView } from '../../collections/CollectionTreeView';
 import { ContextMenu } from '../../ContextMenu';
 import { ContextMenuProps } from '../../ContextMenuItem';
-import { ViewBoxAnnotatableComponent } from '../../DocComponent';
+import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent';
 import { Colors } from '../../global/globalEnums';
 import { LightboxView } from '../../LightboxView';
 import { AnchorMenu } from '../../pdf/AnchorMenu';
@@ -71,7 +71,7 @@ import { SummaryView } from './SummaryView';
 
 export interface FormattedTextBoxProps {}
 @observer
-export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps & FormattedTextBoxProps>() {
+export class FormattedTextBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & FormattedTextBoxProps>() {
     public static LayoutString(fieldStr: string) {
         return FieldView.LayoutString(FormattedTextBox, fieldStr);
     }
@@ -123,7 +123,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     }
 
     @computed get noSidebar() {
-        return this._props.docViewPath().lastElement()?._props.hideDecorationTitle || this._props.noSidebar || this.Document._layout_noSidebar;
+        return this.DocumentView?.()._props.hideDecorationTitle || this._props.noSidebar || this.Document._layout_noSidebar;
     }
     @computed get layout_sidebarWidthPercent() {
         return this._showSidebar ? '20%' : StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%');
@@ -310,7 +310,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 return target;
             };
 
-            DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this._props.docViewPath().lastElement(), () => this.getAnchor(true), targetCreator), e.pageX, e.pageY);
+            DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.DocumentView?.()!, () => this.getAnchor(true), targetCreator), e.pageX, e.pageY);
         });
         const coordsB = this._editorView!.coordsAtPos(this._editorView!.state.selection.to);
         this._props.rootSelected?.() && AnchorMenu.Instance.jumpTo(coordsB.left, coordsB.bottom);
@@ -1410,7 +1410,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             (this._editorView as any).TextView = this;
         }
 
-        const selectOnLoad = Doc.AreProtosEqual(this._props.TemplateDataDocument ?? this.Document, FormattedTextBox.SelectOnLoad) && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this._props.docViewPath()));
+        const selectOnLoad = Doc.AreProtosEqual(this._props.TemplateDataDocument ?? this.Document, FormattedTextBox.SelectOnLoad) && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.docViewPath));
         if (this._editorView && selectOnLoad && !this._props.dontRegisterView && !this._props.dontSelectOnLoad && this.isActiveTab(this.ProseRef)) {
             const selLoadChar = FormattedTextBox.SelectOnLoadChar;
             FormattedTextBox.SelectOnLoad = undefined;
@@ -1531,7 +1531,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         }
         if (!state || !editor || !this.ProseRef?.children[0].className.includes('-focused')) return;
         if (!state.selection.empty && !(state.selection instanceof NodeSelection)) this.setupAnchorMenu();
-        else if (this._props.isContentActive(true)) {
+        else if (this._props.isContentActive()) {
             const pcords = editor.posAtCoords({ left: e.clientX, top: e.clientY });
             let xpos = pcords?.pos || 0;
             while (xpos > 0 && !state.doc.resolve(xpos).node()?.isTextblock) {
@@ -1764,7 +1764,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 return toNum(height) + Math.max(0, toNum(marginTop)) + Math.max(0, toNum(marginBottom));
             };
             const proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + toHgt(child), margins);
-            const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.layout_maxAutoHeight, proseHeight), proseHeight);
+            const scrollHeight = this.ProseRef && proseHeight;
             if (this._props.setHeight && scrollHeight && !this._props.dontRegisterView) {
                 // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
                 const setScrollHeight = () => (this.dataDoc[this.fieldKey + '_scrollHeight'] = scrollHeight);
@@ -1852,7 +1852,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     setHeight={this.setSidebarHeight}
                 />
             ) : (
-                <div onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => SelectionManager.SelectView(this._props.DocumentView?.()!, false), true)}>
+                <div onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => SelectionManager.SelectView(this.DocumentView?.()!, false), true)}>
                     <ComponentTag
                         {...this._props}
                         ref={this._sidebarTagRef as any}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index be8736525..ce17af6ca 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -134,7 +134,8 @@ export class FormattedTextBoxComment {
             //nbef &&
             naft &&
                 LinkInfo.SetLinkInfo({
-                    docProps: textBox.props,
+                    DocumentView: textBox.DocumentView,
+                    styleProvider: textBox._props.styleProvider,
                     linkSrc: textBox.Document,
                     linkDoc: linkDoc ? (DocServer.GetCachedRefField(linkDoc) as Doc) : undefined,
                     location: (pos => [pos.left, pos.top + 25])(view.coordsAtPos(state.selection.from - Math.max(0, nbef - 1))),
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 6fa64a765..645ac08e1 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -294,13 +294,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         listItemDoc.presentation_effect = this.activeItem.presBulletEffect;
                         listItemDoc.presentation_transition = 500;
                         targetView?.setAnimEffect(listItemDoc, 500);
-                        if (targetView?.docView && this.activeItem.presBulletExpand) {
-                            targetView.docView._animateScalingTo = 1.2;
-                            targetView.docView._animateScaleTime = 400;
-                            Doc.AddUnHighlightWatcher(() => {
-                                targetView.docView!._animateScaleTime = undefined;
-                                targetView!.docView!._animateScalingTo = 0;
-                            });
+                        if (targetView && this.activeItem.presBulletExpand) {
+                            targetView.setAnimateScaling(1.2, 400);
+                            Doc.AddUnHighlightWatcher(() => targetView?.setAnimateScaling(0, undefined));
                         }
                         listItemDoc.opacity = undefined;
                         this.activeItem.presentation_indexed = presIndexed + 1;
@@ -2622,7 +2618,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 ScreenToLocalTransform={this.getTransform}
                                 AddToMap={this.AddToMap}
                                 RemFromMap={this.RemFromMap}
-                                hierarchyIndex={emptyPath}
+                                hierarchyIndex={emptyPath as any as number[]}
                             />
                         ) : null}
                     </div>
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 4945d66c8..8cf01b9de 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -19,7 +19,7 @@ import { TreeView } from '../../collections/TreeView';
 import { ViewBoxBaseComponent } from '../../DocComponent';
 import { EditableView } from '../../EditableView';
 import { Colors } from '../../global/globalEnums';
-import { DocumentView, DocumentViewInternalProps, DocumentViewProps } from '../../nodes/DocumentView';
+import { DocumentView } from '../../nodes/DocumentView';
 import { FieldView, FieldViewProps } from '../../nodes/FieldView';
 import { StyleProp } from '../../StyleProvider';
 import { PresBox } from './PresBox';
@@ -50,7 +50,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     // the presentation view that renders this slide
     @computed get presBoxView() {
-        return this._props.DocumentView?.()?._props.docViewPath().lastElement()?.ComponentView as PresBox;
+        return this.containerViewPath?.().lastElement()?.ComponentView as PresBox;
     }
 
     // the presentation view document that renders this slide
@@ -97,7 +97,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     presExpandDocumentClick = () => (this.slideDoc.presentation_expandInlineButton = !this.slideDoc.presentation_expandInlineButton);
     embedHeight = () => this.collapsedHeight + this.expandViewHeight;
     embedWidth = () => this._props.PanelWidth() / 2;
-    styleProvider = (doc: Doc | undefined, props: Opt<DocumentViewInternalProps | FieldViewProps>, property: string): any => {
+    styleProvider = (doc: Doc | undefined, props: Opt<FieldViewProps>, property: string): any => {
         return property === StyleProp.Opacity ? 1 : this._props.styleProvider?.(doc, props, property);
     };
     /**
@@ -116,7 +116,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     hideLinkButton={true}
                     ScreenToLocalTransform={Transform.Identity}
                     renderDepth={this._props.renderDepth + 1}
-                    docViewPath={returnEmptyDoclist}
+                    containerViewPath={returnEmptyDoclist}
                     childFilters={this._props.childFilters}
                     childFiltersByRanges={this._props.childFiltersByRanges}
                     searchFilterDocs={this._props.searchFilterDocs}
@@ -196,7 +196,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const dragArray = this.presBoxView?._dragArray ?? [];
         const dragData = new DragManager.DocumentDragData(this.presBoxView?.sortArray() ?? []);
         if (!dragData.draggedDocuments.length) dragData.draggedDocuments.push(this.slideDoc);
-        dragData.treeViewDoc = this.presBox?._type_collection === CollectionViewType.Tree ? this.presBox : undefined; // this._props.DocumentView?.()?._props.treeViewDoc;
+        dragData.treeViewDoc = this.presBox?._type_collection === CollectionViewType.Tree ? this.presBox : undefined; // this.DocumentView?.()?._props.treeViewDoc;
         dragData.moveDocument = this._props.moveDocument;
         const dragItem: HTMLElement[] = [];
         const classesToRestore = new Map<HTMLElement, string>();
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 4a7e35c16..89e5944f2 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -17,7 +17,7 @@ import { SnappingManager } from '../../util/SnappingManager';
 import { MarqueeOptionsMenu } from '../collections/collectionFreeForm';
 import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
 import { MarqueeAnnotator } from '../MarqueeAnnotator';
-import { DocFocusOptions, DocumentViewInternalProps, DocumentViewProps } from '../nodes/DocumentView';
+import { DocFocusOptions, DocumentViewProps } from '../nodes/DocumentView';
 import { FieldViewProps } from '../nodes/FieldView';
 import { LinkInfo } from '../nodes/LinkDocPreview';
 import { ObservableReactComponent } from '../ObservableReactComponent';
@@ -30,7 +30,7 @@ const _global = (window /* browser */ || global) /* node */ as any;
 
 //pdfjsLib.GlobalWorkerOptions.workerSrc = `/assets/pdf.worker.js`;
 // The workerSrc property shall be specified.
-Pdfjs.GlobalWorkerOptions.workerSrc = 'https://unpkg.com/pdfjs-dist@4.0.269/build/pdf.worker.mjs';
+Pdfjs.GlobalWorkerOptions.workerSrc = 'https://unpkg.com/pdfjs-dist@4.0.379/build/pdf.worker.mjs';
 
 interface IViewerProps extends FieldViewProps {
     Document: Doc;
@@ -364,10 +364,10 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
         this._downX = e.clientX;
         this._downY = e.clientY;
         if ((this._props.Document._freeform_scale || 1) !== 1) return;
-        if ((e.button !== 0 || e.altKey) && this._props.isContentActive(true)) {
+        if ((e.button !== 0 || e.altKey) && this._props.isContentActive()) {
             this._setPreviewCursor?.(e.clientX, e.clientY, true, false, this._props.Document);
         }
-        if (!e.altKey && e.button === 0 && this._props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
+        if (!e.altKey && e.button === 0 && this._props.isContentActive() && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
             this._props.select(false);
             MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
             this._marqueeref.current?.onInitiateSelection([e.clientX, e.clientY]);
@@ -465,7 +465,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
 
     @action
     onZoomWheel = (e: React.WheelEvent) => {
-        if (this._props.isContentActive(true)) {
+        if (this._props.isContentActive()) {
             e.stopPropagation();
             if (e.ctrlKey) {
                 const curScale = Number(this._pdfViewer.currentScaleValue);
@@ -497,7 +497,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
     panelHeight = () => this._props.PanelHeight() / (this._props.NativeDimScaling?.() || 1);
     transparentFilter = () => [...this._props.childFilters(), Utils.TransparentBackgroundFilter];
     opaqueFilter = () => [...this._props.childFilters(), Utils.noDragDocsFilter, ...(SnappingManager.CanEmbed && this._props.isContentActive() ? [] : [Utils.OpaqueBackgroundFilter])];
-    childStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewInternalProps | FieldViewProps>, property: string): any => {
+    childStyleProvider = (doc: Doc | undefined, props: Opt<FieldViewProps>, property: string): any => {
         if (doc instanceof Doc && property === StyleProp.PointerEvents) {
             const docProps = testDocProps(props) ? props : undefined;
             if (this.inlineTextAnnotations.includes(doc) || this._props.isContentActive() === false) return 'none';
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index 575d5849e..188b2eba4 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -102,14 +102,12 @@ export class TopBar extends React.Component {
                     Document={selDoc}
                     fieldKey="data"
                     dropAction="embed"
-                    setHeight={returnFalse}
                     styleProvider={DefaultStyleProvider}
                     bringToFront={emptyFunction}
                     select={emptyFunction}
                     isContentActive={returnTrue}
                     isAnyChildContentActive={returnFalse}
                     isSelected={returnFalse}
-                    docViewPath={returnEmptyDoclist}
                     moveDocument={returnFalse}
                     addDocument={returnFalse}
                     addDocTab={DocumentViewInternal.addDocTabFunc}
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index a1ce55314..9d68ea731 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -396,7 +396,7 @@ export class MobileInterface extends React.Component {
                     isContentActive={emptyFunction}
                     focus={emptyFunction}
                     styleProvider={this.whitebackground}
-                    docViewPath={returnEmptyDoclist}
+                    containerViewPath={returnEmptyDoclist}
                     whenChildContentsActiveChanged={emptyFunction}
                     bringToFront={emptyFunction}
                     childFilters={returnEmptyFilter}
diff --git a/src/server/DashStats.ts b/src/server/DashStats.ts
index b6164832f..a9e6af67c 100644
--- a/src/server/DashStats.ts
+++ b/src/server/DashStats.ts
@@ -15,7 +15,8 @@ export namespace DashStats {
     export const SAMPLING_INTERVAL = 1000; // in milliseconds (ms) - Time interval to update the frontend.
     export const RATE_INTERVAL = 10; // in seconds (s) - Used to calculate rate
 
-    const statsCSVFilename = './src/server/stats/userLoginStats.csv';
+    const statsCSVDirectory = './src/server/stats/';
+    const statsCSVFilename = statsCSVDirectory + 'userLoginStats.csv';
     const columns = ['USERNAME', 'ACTION', 'TIME'];
 
     /**
@@ -153,6 +154,7 @@ export namespace DashStats {
                 TIME: currentDate.toISOString(),
             };
 
+            if (!fs.existsSync(statsCSVDirectory)) fs.mkdirSync(statsCSVDirectory);
             let statsFile = fs.createWriteStream(statsCSVFilename, { flags: 'a' });
             statsFile.write(convertToCSV(toWrite));
             statsFile.end();
-- 
cgit v1.2.3-70-g09d2

