From 7e4d793eaa7e5b6b564355a11fa02a5611645f20 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 21 Mar 2025 18:58:21 -0400
Subject: trying to improve how data / layout / root and templtae docs are
 accessed.

---
 src/client/views/DocumentDecorations.tsx | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 54e050f9f..92b4d6fbf 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -127,7 +127,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
         } else if (this._titleControlString.startsWith('$')) {
             if (this._accumulatedTitle.startsWith('-->#')) {
                 DocumentView.SelectedDocs().forEach(doc => {
-                    doc[DocData].onViewMounted = ScriptField.MakeScript(`updateTagsCollection(this)`);
+                    doc.$onViewMounted = ScriptField.MakeScript(`updateTagsCollection(this)`);
                 });
             }
             const titleFieldKey = this._titleControlString.substring(1);
@@ -621,7 +621,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
         this._inkDragDocs
             .map(oldbds => ({ oldbds, inkPts: Cast(oldbds.doc.data, InkField)?.inkData || [] }))
             .forEach(({ oldbds: { doc, x, y, width, height }, inkPts }) => {
-                doc[DocData].data = new InkField(inkPts.map(
+                doc.$data = new InkField(inkPts.map(
                     (ipt) => ({// (new x — oldx) + newWidth * (oldxpoint /oldWidth)
                         X: NumCast(doc.x) - x + (NumCast(doc.width) * ipt.X) / width,
                         Y: NumCast(doc.y) - y + (NumCast(doc.height) * ipt.Y) / height,
-- 
cgit v1.2.3-70-g09d2


From 705975eb43e7904c62e7e847478f6d0dac60d443 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sun, 23 Mar 2025 18:14:31 -0400
Subject: more _props.Document  to .Document refactoring.  type updates to
 prosemirrortransfer

---
 src/client/util/SharingManager.tsx                 |   2 +-
 src/client/views/DocComponent.tsx                  |  85 +++++++---
 src/client/views/DocumentDecorations.tsx           |   2 +-
 src/client/views/FieldsDropdown.tsx                |   4 +-
 src/client/views/FilterPanel.tsx                   |   4 +-
 src/client/views/LightboxView.tsx                  |   2 +-
 src/client/views/SidebarAnnos.tsx                  |  24 +--
 src/client/views/TemplateMenu.tsx                  |   1 -
 src/client/views/animationtimeline/Timeline.tsx    |  56 +++----
 .../collections/CollectionMasonryViewFieldRow.tsx  |   8 +-
 .../views/collections/CollectionNoteTakingView.tsx |  15 +-
 .../collections/CollectionNoteTakingViewColumn.tsx |  18 +--
 .../views/collections/CollectionPivotView.tsx      |   2 +-
 .../views/collections/CollectionStackingView.tsx   |  19 +--
 .../CollectionStackingViewFieldColumn.tsx          |  28 ++--
 src/client/views/collections/CollectionSubView.tsx |  10 +-
 .../views/collections/CollectionTimeView.tsx       |   2 +-
 .../views/collections/CollectionTreeView.tsx       |   4 +-
 src/client/views/collections/TabDocView.tsx        |  28 ++--
 .../CollectionFreeFormInfoUI.tsx                   |  14 +-
 .../CollectionFreeFormPannableContents.tsx         |   4 +-
 .../CollectionFreeFormRemoteCursors.tsx            |   2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  48 +++---
 .../collections/collectionFreeForm/MarqueeView.tsx |   1 +
 .../collectionSchema/SchemaCellField.tsx           |   4 +-
 .../collections/collectionSchema/SchemaRowBox.tsx  |   2 +-
 .../collectionSchema/SchemaTableCell.tsx           |  46 +++---
 .../views/nodes/CollectionFreeFormDocumentView.tsx |   2 +-
 src/client/views/nodes/DataVizBox/DataVizBox.tsx   |  30 ++--
 .../nodes/DataVizBox/components/Histogram.tsx      |  13 +-
 .../nodes/DataVizBox/components/LineChart.tsx      |   8 +-
 .../views/nodes/DataVizBox/components/PieChart.tsx |  16 +-
 .../views/nodes/DataVizBox/components/TableBox.tsx |  10 +-
 src/client/views/nodes/DocumentView.tsx            |   4 +-
 src/client/views/nodes/FieldView.tsx               |   4 +-
 src/client/views/nodes/ImageBox.tsx                |   2 +-
 src/client/views/nodes/KeyValueBox.tsx             |  10 +-
 src/client/views/nodes/MapBox/MapBox.tsx           |   2 +-
 .../views/nodes/MapboxMapBox/MapboxContainer.tsx   |   3 +-
 src/client/views/nodes/PDFBox.tsx                  |   3 +-
 src/client/views/nodes/ScreenshotBox.tsx           |   2 -
 src/client/views/nodes/WebBox.tsx                  |   2 +-
 .../views/nodes/formattedText/DashFieldView.tsx    |   2 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  10 +-
 .../formattedText/ProsemirrorExampleTransfer.ts    | 174 +++++++++++----------
 .../views/nodes/formattedText/SummaryView.tsx      |  46 +++---
 src/client/views/pdf/PDFViewer.tsx                 |  30 ++--
 src/client/views/search/SearchBox.tsx              |  16 +-
 src/client/views/smartdraw/StickerPalette.tsx      |  16 +-
 src/fields/RichTextUtils.ts                        |   3 +-
 src/fields/Types.ts                                |   5 -
 src/server/DashSession/DashSessionAgent.ts         |   4 +-
 52 files changed, 438 insertions(+), 414 deletions(-)

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

diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 3a248400b..962f51cd4 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -633,7 +633,7 @@ export class SharingManager extends React.Component<object> {
 
     private focusOn = (contents: string) => {
         const title = this.targetDoc ? StrCast(this.targetDoc.title) : '';
-        const docs = DocumentView.Selected().length > 1 ? DocumentView.Selected().map(docView => docView.props.Document) : [this.targetDoc];
+        const docs = DocumentView.Selected().length > 1 ? DocumentView.Selected().map(docView => docView.Document) : [this.targetDoc];
         return (
             <span
                 className="focus-span"
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index a311f6436..8f254ddcd 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -30,26 +30,40 @@ export function DocComponent<P extends DocComponentProps>() {
         }
 
         /**
-         * This is the document being rendered.  In the case of a compound template, it
-         * may not be the actual document rendered and it also may not be the 'real' root document.
-         * Rather, it specifies the shared properties of all layouts of the document (eg, x,y,)
+         * This is the doc that is being rendered. It will be either:
+         *   1) the same as Document if the root of a regular or compound Doc is rendered
+         *   2) the same as the layoutDoc if a component of a compound Doc is rendered.
+         * NOTE: it is very unlikely that you really want to use this method. Instead
+         *       consider: Document, layoutDoc, dataDoc
          */
-        get Document() {
-            return DocCast(this._props.Document.rootDocument, this._props.Document);
+        get _renderDoc() {
+            return this._props.Document;
         }
 
         /**
-         * This is the document being rendered.  It may be a template so it may or may no inherit from the data doc.
+         * This is the "root" Doc being rendered.  In the case of a compound template Doc,
+         * this is the outermost Doc that represents the entire compound Doc.  It is not
+         * necessarily the Doc being rendered in the current React component.
+         * This Doc inherits from the dataDoc, and may or may not inherit (or be) the layoutDoc.
+         */
+        get Document() {
+            return DocCast(this._renderDoc.rootDocument, this._renderDoc);
+        }
+        /**
+         * This is the document being rendered by the React component. In the
+         * case of a compound template, this will be the  expanded template Doc
+         * that represents the component of the compound Doc being rendered.
+         * This may or may not inherit from the data doc.
          */
         @computed get layoutDoc() {
-            return this._props.LayoutTemplateString ? this._props.Document : Doc.Layout(this._props.Document, this._props.LayoutTemplate?.());
+            return this._props.LayoutTemplateString ? this._renderDoc : Doc.Layout(this._renderDoc, this._props.LayoutTemplate?.());
         }
 
         /**
-         * This is the unique data repository for a dcoument that stores the intrinsic document data
+         * This is the unique data repository for a document that stores the intrinsic document data.
          */
         @computed get dataDoc() {
-            return this._props.Document[DocData];
+            return this._renderDoc[DocData];
         }
     }
     return Component;
@@ -75,25 +89,40 @@ export function ViewBoxBaseComponent<P extends FieldViewProps>() {
         }
 
         /**
-         * This is the document being rendered.  In the case of a compound template, it
-         * may not be the actual document rendered and it also may not be the 'real' root document.
-         * Rather, it specifies the shared properties of all layouts of the document (eg, x,y,)
+         * This is the doc that is being rendered. It will be either:
+         *   1) the same as Document if the root of a regular or compound Doc is rendered
+         *   2) the same as the layoutDoc if a component of a compound Doc is rendered.
+         * NOTE: it is very unlikely that you really want to use this method. Instead
+         *       consider: Document, layoutDoc, dataDoc
+         */
+        get _renderDoc() {
+            return this._props.Document;
+        }
+
+        /**
+         * This is the "root" Doc being rendered.  In the case of a compound template Doc,
+         * this is the outermost Doc that represents the entire compound Doc.  It is not
+         * necessarily the Doc being rendered in the current React component.
+         * This Doc inherits from the dataDoc, and may or may not inherit (or be) the layoutDoc.
          */
         get Document() {
-            return DocCast(this._props.Document.rootDocument, this._props.Document);
+            return DocCast(this._renderDoc.rootDocument, this._renderDoc);
         }
         /**
-         * This is the document being rendered.  It may be a template so it may or may no inherit from the data doc.
+         * This is the document being rendered by the React component. In the
+         * case of a compound template, this will be the  expanded template Doc
+         * that represents the component of the compound Doc being rendered.
+         * This may or may not inherit from the data doc.
          */
         @computed get layoutDoc() {
-            return Doc.Layout(this._props.Document);
+            return Doc.Layout(this._renderDoc);
         }
 
         /**
          * This is the unique data repository for a dcoument that stores the intrinsic document data
          */
         @computed get dataDoc() {
-            return this._props.Document.isTemplateForField || this._props.Document.isTemplateDoc ? (this._props.TemplateDataDocument ?? this._props.Document[DocData]) : this._props.Document[DocData];
+            return this._renderDoc.isTemplateForField || this._renderDoc.isTemplateDoc ? (this._props.TemplateDataDocument ?? this._renderDoc[DocData]) : this._renderDoc[DocData];
         }
 
         /**
@@ -133,25 +162,37 @@ export function ViewBoxAnnotatableComponent<P extends FieldViewProps>() {
         }
 
         /**
-         * This is the document being rendered.  In the case of a compound template, it
-         * may not be the actual document rendered and it also may not be the 'real' root document.
-         * Rather, it specifies the shared properties of all layouts of the document (eg, x,y,)
+         * This is the doc that is being rendered. It will be either:
+         *   1) the same as Document if the root of a regular or compound Doc is rendered
+         *   2) the same as the layoutDoc if a component of a compound Doc is rendered.
+         * NOTE: it would unlikely that you really want to use this instead of the
+         *       other Doc options (Document, layoutDoc, dataDoc)
+         */
+        get _renderDoc() {
+            return this._props.Document;
+        }
+
+        /**
+         * This is the "root" Doc being rendered.  In the case of a compound template Doc,
+         * this is the outermost Doc that represents the entire compound Doc.  It is not
+         * necessarily the Doc being rendered in the current React component.
+         * This Doc inherits from the dataDoc, and may or may not inherit (or be) the layoutDoc.
          */
         @computed get Document() {
-            return DocCast(this._props.Document.rootDocument, this._props.Document);
+            return DocCast(this._renderDoc.rootDocument, this._renderDoc);
         }
         /**
          * This is the document being rendered.  It may be a template so it may or may no inherit from the data doc.
          */
         @computed get layoutDoc() {
-            return Doc.Layout(this._props.Document);
+            return Doc.Layout(this._renderDoc);
         }
 
         /**
          * This is the unique data repository for a dcoument that stores the intrinsic document data
          */
         @computed get dataDoc() {
-            return this._props.Document.isTemplateForField || this._props.Document.isTemplateDoc ? (this._props.TemplateDataDocument ?? this._props.Document[DocData]) : this._props.Document[DocData];
+            return this._renderDoc.isTemplateForField || this._renderDoc.isTemplateDoc ? (this._props.TemplateDataDocument ?? this._renderDoc[DocData]) : this._props.Document[DocData];
         }
 
         /**
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 92b4d6fbf..19b987cb5 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -635,7 +635,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
         if (DocumentView.Selected().length === 1) {
             const selected = DocumentView.Selected()[0];
             if (this._titleControlString.startsWith('$')) {
-                return Field.toJavascriptString(selected.Document[this._titleControlString.substring(1)] as FieldType) || '-unset-';
+                return Field.toJavascriptString(selected._renderDoc[this._titleControlString.substring(1)] as FieldType) || '-unset-';
             }
             return this._accumulatedTitle;
         }
diff --git a/src/client/views/FieldsDropdown.tsx b/src/client/views/FieldsDropdown.tsx
index 407031b40..e7ab6a180 100644
--- a/src/client/views/FieldsDropdown.tsx
+++ b/src/client/views/FieldsDropdown.tsx
@@ -18,7 +18,7 @@ import './FilterPanel.scss';
 import { ObservableReactComponent } from './ObservableReactComponent';
 
 interface fieldsDropdownProps {
-    Document: Doc; // show fields for this Doc if set, otherwise for all docs in dashboard
+    Doc: Doc; // show fields for this Doc if set, otherwise for all docs in dashboard
     selectFunc: (value: string) => void;
     menuClose?: () => void;
     placeholder?: string | (() => string);
@@ -36,7 +36,7 @@ export class FieldsDropdown extends ObservableReactComponent<fieldsDropdownProps
 
     @computed get allDescendantDocs() {
         const allDocs = new Set<Doc>();
-        SearchUtil.foreachRecursiveDoc([this._props.Document], (depth, doc) => allDocs.add(doc));
+        SearchUtil.foreachRecursiveDoc([this._props.Doc], (depth, doc) => allDocs.add(doc));
         return Array.from(allDocs);
     }
 
diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
index 4fc8d7a68..c3b3f9d0c 100644
--- a/src/client/views/FilterPanel.tsx
+++ b/src/client/views/FilterPanel.tsx
@@ -379,7 +379,7 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
             <div className="filterBox-treeView">
                 <div className="filterBox-select">
                     <div style={{ width: '100%' }}>
-                        <FieldsDropdown Document={this.Document} selectFunc={this.facetClick} showPlaceholder placeholder="add a filter" addedFields={['acl_Guest', LinkedTo, 'Star', 'Heart', 'Bolt', 'Cloud']} />
+                        <FieldsDropdown Doc={this.Document} selectFunc={this.facetClick} showPlaceholder placeholder="add a filter" addedFields={['acl_Guest', LinkedTo, 'Star', 'Heart', 'Bolt', 'Cloud']} />
                     </div>
                     {/* THE FOLLOWING CODE SHOULD BE DEVELOPER FOR BOOLEAN EXPRESSION (AND / OR) */}
                     {/* <div className="filterBox-select-bool"> 
@@ -443,7 +443,7 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
                 <div>
                     <div className="filterBox-select">
                         <div style={{ width: '100%' }}>
-                            <FieldsDropdown Document={this.Document} selectFunc={this._props.addHotKey} showPlaceholder placeholder="add a hotkey" addedFields={['acl_Guest', LinkedTo]} />
+                            <FieldsDropdown Doc={this.Document} selectFunc={this._props.addHotKey} showPlaceholder placeholder="add a hotkey" addedFields={['acl_Guest', LinkedTo]} />
                         </div>
                     </div>
                 </div>
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index 5698da785..de2c7cd09 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -317,7 +317,7 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> {
                             </GestureOverlay>
                         </div>
 
-                        {this._showPalette && <StickerPalette ref={r => (this._annoPaletteView = r)} Document={DocCast(Doc.UserDoc().myLightboxDrawings)} />}
+                        {this._showPalette && <StickerPalette ref={r => (this._annoPaletteView = r)} Doc={DocCast(Doc.UserDoc().myLightboxDrawings)} />}
                         {this.renderNavBtn(0, undefined, this._props.PanelHeight / 2 - 12.5, 'chevron-left', this._doc && this._history.length ? true : false, this.previous)}
                         {this.renderNavBtn(
                             this._props.PanelWidth - Math.min(this._props.PanelWidth / 4, this._props.maxBorder[0]),
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index b3f3a4478..3c0611f03 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -22,7 +22,7 @@ import { FieldViewProps } from './nodes/FieldView';
 
 interface ExtraProps {
     fieldKey: string;
-    Document: Doc;
+    Doc: Doc;
     layoutDoc: Doc;
     dataDoc: Doc;
     // usePanelWidth: boolean;
@@ -45,7 +45,7 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
     _stackRef = React.createRef<CollectionStackingView>();
     @computed get allMetadata() {
         const keys = new Map<string, FieldResult<FieldType>>();
-        DocListCast(this._props.Document[this.sidebarKey]).forEach(doc =>
+        DocListCast(this._props.Doc[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]))
@@ -54,7 +54,7 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
     }
     @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.Doc[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()))
@@ -62,7 +62,7 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
     }
     @computed get allUsers() {
         const keys = new Set<string>();
-        DocListCast(this._props.Document[this.sidebarKey]).forEach(doc => keys.add(StrCast(doc.author)));
+        DocListCast(this._props.Doc[this.sidebarKey]).forEach(doc => keys.add(StrCast(doc.author)));
         return Array.from(keys.keys()).sort();
     }
 
@@ -72,7 +72,7 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
             .join(' ');
         const target = Docs.Create.TextDocument(startup, {
             title: '-note-',
-            annotationOn: this._props.Document,
+            annotationOn: this._props.Doc,
             _width: 200,
             _height: 50,
             _layout_fitWidth: true,
@@ -87,7 +87,7 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
             .filter(data => data.split(':')[0])
             .filter(data => !filterExlusions?.includes(data.split(':')[0]))
             .map(data => {
-                const key = '$'+data.split(':')[0];
+                const key = '$' + data.split(':')[0];
                 const val = Field.Copy(this.allMetadata.get(key));
                 target[key] = val;
                 return {
@@ -148,7 +148,7 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
         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.Doc[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>();
@@ -189,7 +189,7 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
         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.Doc, 'tags', tag, 'check', true, undefined, e.shiftKey)}>
                     {tag}
                 </div>
             );
@@ -197,7 +197,7 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
         const renderMeta = (tag: string) => {
             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.Doc, tag, Doc.FilterAny, 'exists', true, undefined, e.shiftKey)}>
                     {tag}
                 </div>
             );
@@ -205,7 +205,7 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
         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.Doc, 'author', user, 'check', true, undefined, e.shiftKey)}>
                     {user}
                 </div>
             );
@@ -216,9 +216,9 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
                 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,
+                    top: this._props.Doc.type !== DocumentType.RTF && StrCast(this._props.Doc._layout_showTitle) === 'title' ? 15 : 0,
                     right: 0,
-                    background: this._props.styleProvider?.(this._props.Document, this._props, StyleProp.WidgetColor) as string,
+                    background: this._props.styleProvider?.(this._props.Doc, this._props, StyleProp.WidgetColor) as string,
                     width: `100%`,
                     height: '100%',
                 }}>
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 680c8ed0e..1266a11c1 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -80,7 +80,6 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
         const addedTypes = DocListCast(Cast(Doc.UserDoc().template_clickFuncs, Doc, null)?.data);
         const templateMenu: Array<JSX.Element> = [];
         templateMenu.push(<OtherToggle key="default" name={firstDoc.layout instanceof Doc ? StrCast(firstDoc.layout.title) : 'Default'} checked={templateName === 'layout'} toggle={this.toggleDefault} />);
-        // eslint-disable-next-line no-return-assign
         addedTypes.concat(noteTypes).map(template => (template.treeView_Checked = this.templateIsUsed(firstDoc, template)));
         this._addedKeys &&
             Array.from(this._addedKeys)
diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx
index 15683ebf2..cd2c7df1b 100644
--- a/src/client/views/animationtimeline/Timeline.tsx
+++ b/src/client/views/animationtimeline/Timeline.tsx
@@ -46,7 +46,7 @@ import { Id } from '../../../fields/FieldSymbols';
  */
 
 @observer
-export class Timeline extends ObservableReactComponent<FieldViewProps> {
+export class Timeline extends ObservableReactComponent<FieldViewProps & { Doc: Doc }> {
     // readonly constants
     private readonly DEFAULT_TICK_SPACING: number = 50;
     private readonly MAX_TITLE_HEIGHT = 75;
@@ -57,7 +57,7 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
     private DEFAULT_CONTAINER_HEIGHT: number = 330;
     private MIN_CONTAINER_HEIGHT: number = 205;
 
-    constructor(props: FieldViewProps) {
+    constructor(props: FieldViewProps & { Doc: Doc }) {
         super(props);
         makeObservable(this);
     }
@@ -90,11 +90,11 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
      */
     @computed
     private get children(): Doc[] {
-        const annotatedDoc = [DocumentType.IMG, DocumentType.VID, DocumentType.PDF, DocumentType.MAP].includes(StrCast(this._props.Document.type) as unknown as DocumentType);
+        const annotatedDoc = [DocumentType.IMG, DocumentType.VID, DocumentType.PDF, DocumentType.MAP].includes(StrCast(this._props.Doc.type) as unknown as DocumentType);
         if (annotatedDoc) {
-            return DocListCast(this._props.Document[Doc.LayoutFieldKey(this._props.Document) + '_annotations']);
+            return DocListCast(this._props.Doc[Doc.LayoutFieldKey(this._props.Doc) + '_annotations']);
         }
-        return DocListCast(this._props.Document[this._props.fieldKey]);
+        return DocListCast(this._props.Doc[this._props.fieldKey]);
     }
 
     /// //////lifecycle functions////////////
@@ -104,21 +104,21 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
         this._titleHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT; // check if relHeight is less than Maxheight. Else, just set relheight to max
         this.MIN_CONTAINER_HEIGHT = this._titleHeight + 130; // offset
         this.DEFAULT_CONTAINER_HEIGHT = this._titleHeight * 2 + 130; // twice the titleheight + offset
-        if (!this._props.Document.AnimationLength) {
+        if (!this._props.Doc.AnimationLength) {
             // if animation length did not exist
-            this._props.Document.AnimationLength = this._time; // set it to default time
+            this._props.Doc.AnimationLength = this._time; // set it to default time
         } else {
-            this._time = NumCast(this._props.Document.AnimationLength); // else, set time to animationlength stored from before
+            this._time = NumCast(this._props.Doc.AnimationLength); // else, set time to animationlength stored from before
         }
         this._totalLength = this._tickSpacing * (this._time / this._tickIncrement); // the entire length of the timeline div (actual div part itself)
         this._visibleLength = this._infoContainer.current!.getBoundingClientRect().width; // the visible length of the timeline (the length that you current see)
         this._visibleStart = this._infoContainer.current!.scrollLeft; // where the div starts
-        this._props.Document.isATOn = !this._props.Document.isATOn; // turns the boolean on, saying AT (animation timeline) is on
+        this._props.Doc.isATOn = !this._props.Doc.isATOn; // turns the boolean on, saying AT (animation timeline) is on
         this.toggleHandle();
     }
 
     componentWillUnmount() {
-        this._props.Document.AnimationLength = this._time; // save animation length
+        this._props.Doc.AnimationLength = this._time; // save animation length
     }
     /// //////////////////////////////////////////////
 
@@ -224,7 +224,7 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
      */
     @action
     onPanDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, this.onPanMove, emptyFunction, e => this.changeCurrentBarX(this._trackbox.current!.scrollLeft + e.clientX - this._trackbox.current!.getBoundingClientRect().left));
+        setupMoveUpEvents(this, e, this.onPanMove, emptyFunction, movEv => this.changeCurrentBarX(this._trackbox.current!.scrollLeft + movEv.clientX - this._trackbox.current!.getBoundingClientRect().left));
     };
 
     /**
@@ -241,7 +241,7 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
             this._visibleStart -= e.movementX;
             this._totalLength -= e.movementX;
             this._time -= RegionHelpers.convertPixelTime(e.movementX, 'mili', 'time', this._tickSpacing, this._tickIncrement);
-            this._props.Document.AnimationLength = this._time;
+            this._props.Doc.AnimationLength = this._time;
         }
         return false;
     };
@@ -259,8 +259,8 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
         setupMoveUpEvents(
             this,
             e,
-            action(e => {
-                const offset = e.clientY - this._timelineContainer.current!.getBoundingClientRect().bottom;
+            action(movEv => {
+                const offset = movEv.clientY - this._timelineContainer.current!.getBoundingClientRect().bottom;
                 this._containerHeight = clamp(this.MIN_CONTAINER_HEIGHT, this._containerHeight + offset, this.MAX_CONTAINER_HEIGHT);
                 return false;
             }),
@@ -358,7 +358,7 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
         const size = 40 * scale; // 50 is default
         const iconSize = 25;
         const width: number = this._props.PanelWidth();
-        const modeType = this._props.Document.isATOn ? 'Author' : 'Play';
+        const modeType = this._props.Doc.isATOn ? 'Author' : 'Play';
 
         // decides if information should be omitted because the timeline is very small
         // if its less than 950 pixels then it's going to be overlapping
@@ -397,7 +397,7 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
                             tickIncrement={this._tickIncrement}
                             time={this._time}
                             parent={this}
-                            isAuthoring={BoolCast(this._props.Document.isATOn)}
+                            isAuthoring={BoolCast(this._props.Doc.isATOn)}
                             currentBarX={this._currentBarX}
                             totalLength={this._totalLength}
                             visibleLength={this._visibleLength}
@@ -418,10 +418,10 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
                     </div>
                     <div className="time-box overview-tool" style={{ display: 'flex' }}>
                         {this.timeIndicator(lengthString, totalTime)}
-                        <div className="resetView-tool" title="Return to Default View" onClick={() => this.resetView(this._props.Document)}>
+                        <div className="resetView-tool" title="Return to Default View" onClick={() => this.resetView(this._props.Doc)}>
                             <FontAwesomeIcon icon="compress-arrows-alt" size="lg" />
                         </div>
-                        <div className="resetView-tool" style={{ display: this._props.Document.isATOn ? 'flex' : 'none' }} title="Set Default View" onClick={() => this.setView(this._props.Document)}>
+                        <div className="resetView-tool" style={{ display: this._props.Doc.isATOn ? 'flex' : 'none' }} title="Set Default View" onClick={() => this.setView(this._props.Doc)}>
                             <FontAwesomeIcon icon="expand-arrows-alt" size="lg" />
                         </div>
                     </div>
@@ -431,17 +431,17 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
     };
 
     timeIndicator(lengthString: string, totalTime: number) {
-        if (this._props.Document.isATOn) {
-            return <div key="time-text" className="animation-text" style={{ visibility: this._props.Document.isATOn ? 'visible' : 'hidden', display: this._props.Document.isATOn ? 'flex' : 'none' }}>{`Total: ${this.toReadTime(totalTime)}`}</div>;
+        if (this._props.Doc.isATOn) {
+            return <div key="time-text" className="animation-text" style={{ visibility: this._props.Doc.isATOn ? 'visible' : 'hidden', display: this._props.Doc.isATOn ? 'flex' : 'none' }}>{`Total: ${this.toReadTime(totalTime)}`}</div>;
         } else {
             const ctime = `Current: ${this.getCurrentTime()}`;
             const ttime = `Total: ${this.toReadTime(this._time)}`;
             return (
                 <div style={{ flexDirection: 'column' }}>
-                    <div className="animation-text" style={{ fontSize: '10px', width: '100%', display: !this._props.Document.isATOn ? 'block' : 'none' }}>
+                    <div className="animation-text" style={{ fontSize: '10px', width: '100%', display: !this._props.Doc.isATOn ? 'block' : 'none' }}>
                         {ctime}
                     </div>
-                    <div className="animation-text" style={{ fontSize: '10px', width: '100%', display: !this._props.Document.isATOn ? 'block' : 'none' }}>
+                    <div className="animation-text" style={{ fontSize: '10px', width: '100%', display: !this._props.Doc.isATOn ? 'block' : 'none' }}>
                         {ttime}
                     </div>
                 </div>
@@ -467,8 +467,8 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
         const roundToggleContainer = this._roundToggleContainerRef.current!;
         const timelineContainer = this._timelineContainer.current!;
 
-        this._props.Document.isATOn = !this._props.Document.isATOn;
-        if (!BoolCast(this._props.Document.isATOn)) {
+        this._props.Doc.isATOn = !this._props.Doc.isATOn;
+        if (!BoolCast(this._props.Doc.isATOn)) {
             // turning on playmode...
             roundToggle.style.transform = 'translate(0px, 0px)';
             roundToggle.style.animationName = 'turnoff';
@@ -543,7 +543,7 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
         // change visible and total width
         return (
             <div style={{ visibility: 'visible' }}>
-                <div key="timeline_wrapper" style={{ visibility: this._props.Document.isATOn ? 'visible' : 'hidden', left: '0px', top: '0px', position: 'absolute', width: '100%', transform: 'translate(0px, 0px)' }}>
+                <div key="timeline_wrapper" style={{ visibility: this._props.Doc.isATOn ? 'visible' : 'hidden', left: '0px', top: '0px', position: 'absolute', width: '100%', transform: 'translate(0px, 0px)' }}>
                     <div key="timeline_container" className="timeline-container" ref={this._timelineContainer} style={{ height: `${this._containerHeight}px`, top: `0px` }}>
                         <div key="timeline_info" className="info-container" onPointerDown={this.onPanDown} ref={this._infoContainer} onWheel={this.onWheelZoom}>
                             {this.drawTicks()}
@@ -551,7 +551,7 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
                                 <div key="timeline_scrubberhead" className="scrubberhead" onPointerDown={this.onScrubberDown} />
                             </div>
                             <div key="timeline_trackbox" className="trackbox" ref={this._trackbox} style={{ width: `${this._totalLength}px` }}>
-                                {[...this.children, this._props.Document].map(doc => (
+                                {[...this.children, this._props.Doc].map(doc => (
                                     <Track
                                         key={doc[Id]}
                                         ref={ref => this.mapOfTracks.push(ref)}
@@ -563,7 +563,7 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
                                         time={this._time}
                                         tickSpacing={this._tickSpacing}
                                         tickIncrement={this._tickIncrement}
-                                        collection={this._props.Document}
+                                        collection={this._props.Doc}
                                         timelineVisible={true}
                                     />
                                 ))}
@@ -571,7 +571,7 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
                         </div>
                         <div className="currentTime">Current: {this.getCurrentTime()}</div>
                         <div key="timeline_title" className="title-container" ref={this._titleContainer}>
-                            {[...this.children, this._props.Document].map(doc => (
+                            {[...this.children, this._props.Doc].map(doc => (
                                 <div key={doc[Id]} style={{ height: `${this._titleHeight}px` }} className="datapane" onPointerOver={() => Doc.BrushDoc(doc)} onPointerOut={() => Doc.UnBrushDoc(doc)}>
                                     <p>{StrCast(doc.title)}</p>
                                 </div>
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index 4fe73895e..89ccf5a0f 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -22,7 +22,7 @@ import './CollectionStackingView.scss';
 interface CMVFieldRowProps {
     rows: () => number;
     headings: () => object[];
-    Document: Doc;
+    Doc: Doc;
     chromeHidden?: boolean;
     heading: string;
     headingObject: SchemaHeaderField | undefined;
@@ -73,7 +73,7 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
 
     createRowDropRef = (ele: HTMLDivElement | null) => {
         this._dropDisposer?.();
-        if (ele) this._dropDisposer = DragManager.MakeDropTarget(ele, this.rowDrop.bind(this), this._props.Document);
+        if (ele) this._dropDisposer = DragManager.MakeDropTarget(ele, this.rowDrop.bind(this), this._props.Doc);
         else if (this._ele) this.props.refList.splice(this.props.refList.indexOf(this._ele), 1);
         this._ele = ele;
     };
@@ -189,7 +189,7 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
     };
 
     headerMove = (e: PointerEvent) => {
-        const embedding = Doc.MakeEmbedding(this._props.Document);
+        const embedding = Doc.MakeEmbedding(this._props.Doc);
         const key = this._props.pivotField;
         let value = this.getValue(this.heading);
         value = typeof value === 'string' ? `"${value}"` : value;
@@ -289,7 +289,7 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
         const key = this._props.pivotField;
         const evContents = this.heading ? this.heading : this._props.type && this._props.type === 'number' ? '0' : `NO ${key.toUpperCase()} VALUE`;
         const editableHeaderView = <EditableView GetValue={() => evContents} SetValue={this.headingChanged} contents={evContents} oneLine />;
-        return this._props.Document.miniHeaders ? (
+        return this._props.Doc.miniHeaders ? (
             <div className="collectionStackingView-miniHeader">{editableHeaderView}</div>
         ) : !this._props.headingObject ? null : (
             <div className="collectionStackingView-sectionHeader" ref={this._headerRef}>
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index 4650727eb..2dabd3269 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -32,6 +32,7 @@ import { CollectionNoteTakingViewColumn } from './CollectionNoteTakingViewColumn
 import { CollectionNoteTakingViewDivider } from './CollectionNoteTakingViewDivider';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
 import { Property } from 'csstype';
+import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 
 /**
  * CollectionNoteTakingView is a column-based view for displaying documents. In this view, the user can (1)
@@ -437,10 +438,10 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     };
 
     @undoBatch
-    onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
-        if ((e.ctrlKey || fieldProps.Document._createDocOnCR) && ['Enter'].includes(e.key)) {
+    onKeyDown = (e: React.KeyboardEvent, textBox: FormattedTextBox) => {
+        if ((e.ctrlKey || textBox.Document._createDocOnCR) && ['Enter'].includes(e.key)) {
             e.stopPropagation?.();
-            const newDoc = Doc.MakeCopy(fieldProps.Document, true);
+            const newDoc = Doc.MakeCopy(textBox.Document, true);
             newDoc.$text = undefined;
             DocumentView.SetSelectOnLoad(newDoc);
             return this.addDocument?.(newDoc);
@@ -543,8 +544,8 @@ export class CollectionNoteTakingView extends CollectionSubView() {
             addDocument={this.addDocument}
             chromeHidden={this.chromeHidden}
             colHeaderData={this.colHeaderData}
-            Document={this.Document}
-            TemplateDataDocument={this._props.TemplateDataDocument}
+            Doc={this.Document}
+            TemplateDataDoc={this._props.TemplateDataDocument}
             resizeColumns={this.resizeColumns}
             renderChildren={this.children}
             numGroupColumns={this.numGroupColumns}
@@ -567,7 +568,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
 
     @undoBatch
     remColumn = (value: SchemaHeaderField) => {
-        const colHdrData = Array.from(Cast(this._props.Document[this._props.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null));
+        const colHdrData = Array.from(Cast(this.Document[this._props.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null));
         if (value) {
             const index = colHdrData.indexOf(value);
             index !== -1 && colHdrData.splice(index, 1);
@@ -701,7 +702,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                 {this.renderedSections}
                 <div className="collectionNotetaking-pivotField" style={{ right: 0, top: 0, position: 'absolute' }}>
                     <FieldsDropdown
-                        Document={this.Document}
+                        Doc={this.Document}
                         selectFunc={undoable(fieldKey => {
                             this.layoutDoc._pivotField = fieldKey;
                             this.removeEmptyColumns();
diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index df9b5a1eb..f283b0abe 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -21,8 +21,8 @@ import './CollectionNoteTakingView.scss';
 import { DocumentView } from '../nodes/DocumentView';
 
 interface CSVFieldColumnProps {
-    Document: Doc;
-    TemplateDataDocument: Opt<Doc>;
+    Doc: Doc;
+    TemplateDataDoc: Opt<Doc>;
     backgroundColor?: () => string | undefined;
     docList: Doc[];
     heading: string;
@@ -65,7 +65,7 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
 
     // columnWidth returns the width of a column in absolute pixels
     @computed get columnWidth() {
-        if (this._props.Document._notetaking_columns_autoSize) return this._props.availableWidth / (this._props.colHeaderData?.length || 1);
+        if (this._props.Doc._notetaking_columns_autoSize) return this._props.availableWidth / (this._props.colHeaderData?.length || 1);
         if (!this._props.colHeaderData || !this._props.headingObject || this._props.colHeaderData.length === 1) return `${(this._props.availableWidth / this._props.PanelWidth()) * 100}%`;
         const i = this._props.colHeaderData.findIndex(hd => hd.heading === this._props.headingObject?.heading && hd.color === this._props.headingObject.color);
         return ((this._props.colHeaderData[i].width * this._props.availableWidth) / this._props.PanelWidth()) * 100 + '%';
@@ -81,7 +81,7 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
 
     createColumnDropRef = (ele: HTMLDivElement | null) => {
         this.dropDisposer?.();
-        if (ele) this.dropDisposer = DragManager.MakeDropTarget(ele, this.columnDrop.bind(this), this._props.Document);
+        if (ele) this.dropDisposer = DragManager.MakeDropTarget(ele, this.columnDrop.bind(this), this._props.Doc);
         else if (this._ele) this.props.refList.slice(this.props.refList.indexOf(this._ele), 1);
         this._ele = ele;
     };
@@ -155,7 +155,7 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
     // all docs are added to the column directly to the left.
     @undoBatch
     deleteColumn = () => {
-        const colHdrData = Array.from(Cast(this._props.Document[this._props.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null));
+        const colHdrData = Array.from(Cast(this._props.Doc[this._props.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null));
         if (this._props.headingObject) {
             //          this._props.docList.forEach(d => (d['$'+this._props.pivotField] = undefined));
             colHdrData.splice(colHdrData.indexOf(this._props.headingObject), 1);
@@ -184,11 +184,11 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
         );
 
         ContextMenu.Instance.setDefaultItem('::', (name: string): void => {
-            Doc.GetProto(this._props.Document)[name] = '';
+            Doc.GetProto(this._props.Doc)[name] = '';
             const created = Docs.Create.TextDocument('', { title: name, _width: 250, _layout_autoHeight: true });
             if (created) {
-                if (this._props.Document.isTemplateDoc) {
-                    Doc.MakeMetadataFieldTemplate(created, this._props.Document);
+                if (this._props.Doc.isTemplateDoc) {
+                    Doc.MakeMetadataFieldTemplate(created, this._props.Doc);
                 }
                 this._props.addDocument?.(created);
             }
@@ -267,7 +267,7 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
                 style={{
                     width: this.columnWidth,
                     background: this._hover && SnappingManager.IsDragging ? '#b4b4b4' : 'inherit',
-                    marginLeft: this._props.headings().findIndex(h => h[0] === this._props.headingObject) === 0 ? NumCast(this._props.Document.xMargin) : 0,
+                    marginLeft: this._props.headings().findIndex(h => h[0] === this._props.headingObject) === 0 ? NumCast(this._props.Doc.xMargin) : 0,
                 }}>
                 <div className="collectionNoteTakingViewFieldColumn" key={this._heading} ref={this.createColumnDropRef}>
                     {this.innards}
diff --git a/src/client/views/collections/CollectionPivotView.tsx b/src/client/views/collections/CollectionPivotView.tsx
index 2600c0f57..4736070c3 100644
--- a/src/client/views/collections/CollectionPivotView.tsx
+++ b/src/client/views/collections/CollectionPivotView.tsx
@@ -103,7 +103,7 @@ export class CollectionPivotView extends CollectionSubView() {
                 {this.contents}
                 <div style={{ right: 0, top: 0, position: 'absolute' }}>
                     <FieldsDropdown
-                        Document={this.Document}
+                        Doc={this.Document}
                         selectFunc={fieldKey => {
                             this.layoutDoc._pivotField = fieldKey;
                         }}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 9972fe03d..883b0bbe3 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -34,6 +34,7 @@ import './CollectionStackingView.scss';
 import { CollectionStackingViewFieldColumn } from './CollectionStackingViewFieldColumn';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
 import { computedFn } from 'mobx-utils';
+import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 
 export type collectionStackingViewProps = {
     sortFunc?: (a: Doc, b: Doc) => number;
@@ -309,15 +310,15 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         return this._props.styleProvider?.(doc, props, property);
     };
     @undoBatch
-    onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
+    onKeyDown = (e: React.KeyboardEvent, textBox: FormattedTextBox) => {
         if (['Enter'].includes(e.key) && e.ctrlKey) {
             e.stopPropagation?.();
-            const layoutFieldKey = StrCast(fieldProps.fieldKey);
-            const newDoc = Doc.MakeCopy(fieldProps.Document, true);
-            const dataField = fieldProps.Document[Doc.LayoutFieldKey(newDoc)];
+            const layoutFieldKey = StrCast(textBox.fieldKey);
+            const newDoc = Doc.MakeCopy(textBox.Document, true);
+            const dataField = textBox.Document[Doc.LayoutFieldKey(newDoc)];
             newDoc['$' + Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined;
-            if (layoutFieldKey !== 'layout' && fieldProps.Document[layoutFieldKey] instanceof Doc) {
-                newDoc[layoutFieldKey] = fieldProps.Document[layoutFieldKey];
+            if (layoutFieldKey !== 'layout' && textBox.Document[layoutFieldKey] instanceof Doc) {
+                newDoc[layoutFieldKey] = textBox.Document[layoutFieldKey];
             }
             newDoc.$text = undefined;
             DocumentView.SetSelectOnLoad(newDoc);
@@ -574,8 +575,8 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                 addDocument={this.addDocument}
                 chromeHidden={this.chromeHidden}
                 colHeaderData={this.colHeaderData}
-                Document={this.Document}
-                TemplateDataDocument={this._props.TemplateDataDocument}
+                Doc={this.Document}
+                TemplateDataDoc={this._props.TemplateDataDocument}
                 renderChildren={this.children}
                 columnWidth={this.columnWidth}
                 numGroupColumns={this.numGroupColumns}
@@ -610,7 +611,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                 <div style={{ top: this.yMargin }}>
                     <CollectionMasonryViewFieldRow
                         showHandle={first}
-                        Document={this.Document}
+                        Doc={this.Document}
                         chromeHidden={this.chromeHidden}
                         pivotField={this.pivotField}
                         refList={this._refList}
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 6f32dd2e0..3b9d167c6 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -29,8 +29,8 @@ import './CollectionStackingView.scss';
 
 // So this is how we are storing a column
 interface CSVFieldColumnProps {
-    Document: Doc;
-    TemplateDataDocument: Opt<Doc>;
+    Doc: Doc;
+    TemplateDataDoc: Opt<Doc>;
     docList: Doc[];
     heading: string;
     pivotField: string;
@@ -90,7 +90,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
     // is that the only way to have drop targets?
     createColumnDropRef = (ele: HTMLDivElement | null) => {
         this.dropDisposer?.();
-        if (ele) this.dropDisposer = DragManager.MakeDropTarget(ele, this.columnDrop.bind(this), this._props.Document, this.onInternalPreDrop.bind(this));
+        if (ele) this.dropDisposer = DragManager.MakeDropTarget(ele, this.columnDrop.bind(this), this._props.Doc, this.onInternalPreDrop.bind(this));
         else if (this._ele) this.props.refList.splice(this.props.refList.indexOf(this._ele), 1);
         this._ele = ele;
     };
@@ -183,7 +183,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
     // TODO: I think this is where I'm supposed to edit stuff
     startDrag = (e: PointerEvent) => {
         // is MakeEmbedding a way to make a copy of a doc without rendering it?
-        const embedding = Doc.MakeEmbedding(this._props.Document);
+        const embedding = Doc.MakeEmbedding(this._props.Doc);
         embedding._width = this._props.columnWidth / (this._props.colHeaderData?.length || 1);
         embedding._pivotField = undefined;
         let value = this.getValue(this._heading);
@@ -230,7 +230,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
         ContextMenu.Instance.clearItems();
         const layoutItems: ContextMenuProps[] = [];
         const docItems: ContextMenuProps[] = [];
-        const dataDoc = this._props.TemplateDataDocument || this._props.Document;
+        const dataDoc = this._props.TemplateDataDoc || this._props.Doc;
         const width = this._ele ? DivWidth(this._ele) : 0;
         const height = this._ele ? DivHeight(this._ele) : 0;
         DocUtils.addDocumentCreatorMenuItems(
@@ -250,10 +250,10 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
                 docItems.push({
                     description: ':' + fieldKey,
                     event: () => {
-                        const created = DocumentFromField(dataDoc, fieldKey, Doc.GetProto(this._props.Document));
+                        const created = DocumentFromField(dataDoc, fieldKey, Doc.GetProto(this._props.Doc));
                         if (created) {
-                            if (this._props.Document.isTemplateDoc) {
-                                Doc.MakeMetadataFieldTemplate(created, this._props.Document);
+                            if (this._props.Doc.isTemplateDoc) {
+                                Doc.MakeMetadataFieldTemplate(created, this._props.Doc);
                             }
                             return this._props.addDocument?.(created);
                         }
@@ -270,7 +270,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
                     event: () => {
                         const created = Docs.Create.CarouselDocument([], { _width: 400, _height: 200, title: fieldKey });
                         if (created) {
-                            const container = this._props.Document.resolvedDataDoc ? Doc.GetProto(this._props.Document) : this._props.Document;
+                            const container = this._props.Doc.resolvedDataDoc ? Doc.GetProto(this._props.Doc) : this._props.Doc;
                             if (container.isTemplateDoc) {
                                 Doc.MakeMetadataFieldTemplate(created, container);
                                 return Doc.AddDocToList(container, Doc.LayoutFieldKey(container), created);
@@ -285,11 +285,11 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
         !Doc.noviceMode && ContextMenu.Instance.addItem({ description: 'Doc Fields ...', subitems: docItems, icon: 'eye' });
         !Doc.noviceMode && ContextMenu.Instance.addItem({ description: 'Containers ...', subitems: layoutItems, icon: 'eye' });
         ContextMenu.Instance.setDefaultItem('::', (name: string): void => {
-            Doc.GetProto(this._props.Document)[name] = '';
+            Doc.GetProto(this._props.Doc)[name] = '';
             const created = Docs.Create.TextDocument('', { title: name, _width: 250, _layout_autoHeight: true });
             if (created) {
-                if (this._props.Document.isTemplateDoc) {
-                    Doc.MakeMetadataFieldTemplate(created, this._props.Document);
+                if (this._props.Doc.isTemplateDoc) {
+                    Doc.MakeMetadataFieldTemplate(created, this._props.Doc);
                 }
                 this._props.addDocument?.(created);
             }
@@ -350,10 +350,10 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
             </div>
         ) : null;
         const templatecols = `${this._props.columnWidth / this._props.numGroupColumns}px `;
-        const { type } = this._props.Document;
+        const { type } = this._props.Doc;
         return (
             <>
-                {this._props.Document._columnsHideIfEmpty ? null : headingView}
+                {this._props.Doc._columnsHideIfEmpty ? null : headingView}
                 {this.collapsed ? null : (
                     <div
                         style={{
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 29caead5b..a43cf0755 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -9,7 +9,7 @@ import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { listSpec } from '../../../fields/Schema';
 import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, DateCast, DocCast, NumCast, ScriptCast, StrCast, toList } from '../../../fields/Types';
+import { BoolCast, Cast, DateCast, NumCast, ScriptCast, StrCast, toList } from '../../../fields/Types';
 import { WebField } from '../../../fields/URLField';
 import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
 import { GestureUtils } from '../../../pen-gestures/GestureUtils';
@@ -85,9 +85,6 @@ export function CollectionSubView<X>() {
         constructor(props: X & SubCollectionViewProps) {
             super(props);
             makeObservable(this);
-            console.log(`propsTitle: ${this._props.Document.title} DocTitle: ${this.Document.title} LayoutTitle:${this.layoutDoc.title} DataTitle:${this.dataDoc.title}`);
-            console.log(`tempTitle: ${this._props.TemplateDataDocument?.title} LayouTResolve: ${DocCast(this.layoutDoc.resolvedDataDoc)?.title} propDocResolve: ${DocCast(this._props.Document.resolvedDataDoc)?.title}`);
-            console.log('Children:', this.childDocs, this.childLayoutPairs);
         }
 
         @observable _focusFilters: Opt<string[]> = undefined; // childFilters that are overridden when previewing a link to an anchor which has childFilters set on it
@@ -111,7 +108,6 @@ export function CollectionSubView<X>() {
         }
 
         get dataDoc() {
-            console.log(this._props.Document.title + ' isTemplate: ' + this.layoutDoc.isTemplateForField);
             return this._props.TemplateDataDocument instanceof Doc && this.layoutDoc.isTemplateForField //
                 ? this._props.TemplateDataDocument[DocData]
                 : this.layoutDoc.resolvedDataDoc
@@ -135,7 +131,7 @@ export function CollectionSubView<X>() {
 
         hasChildDocs = () => this.childLayoutPairs.map(pair => pair.layout);
         @computed get childLayoutPairs(): { layout: Doc; data: Doc }[] {
-            const { Document, TemplateDataDocument } = this._props;
+            const { Document: Document, TemplateDataDocument } = this._props;
             const validPairs = this.childDocs
                 .map(doc => Doc.GetLayoutDataDocPair(Document, !this._props.isAnnotationOverlay ? TemplateDataDocument : undefined, doc))
                 .filter(
@@ -303,7 +299,7 @@ export function CollectionSubView<X>() {
             const dragData = de.complete.docDragData;
             if (dragData) {
                 const sourceDragAction = dragData.dropAction;
-                const sameCollection = !dragData.draggedDocuments.some(d => d.embedContainer !== this._props.Document);
+                const sameCollection = !dragData.draggedDocuments.some(d => d.embedContainer !== this._renderDoc);
                 dragData.dropAction = !sameCollection // if doc from another tree
                     ? sourceDragAction || targetDropAction // then use the source's dragAction otherwise the target's
                     : sourceDragAction === dropActionType.inPlace // if source drag is inPlace
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 98bd06221..fd562e64f 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -121,7 +121,7 @@ export class CollectionTimeView extends CollectionSubView() {
                 {this.contents}
                 <div style={{ right: 0, top: 0, position: 'absolute' }}>
                     <FieldsDropdown
-                        Document={this.Document}
+                        Doc={this.Document}
                         selectFunc={fieldKey => {
                             this.layoutDoc._pivotField = fieldKey;
                         }}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index e93724dd4..b7f49ac20 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -241,7 +241,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
         );
     }
 
-    onKey = (e: React.KeyboardEvent /* , fieldProps: FieldViewProps */) => {
+    onKey = (e: React.KeyboardEvent /* , textBox: FormattedTextBox */) => {
         if (this.outlineMode && e.key === 'Enter') {
             e.stopPropagation();
             this.makeTextCollection(this.treeChildren);
@@ -252,7 +252,6 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     get documentTitle() {
         return (
             <FormattedTextBox
-                // eslint-disable-next-line react/jsx-props-no-spreading
                 {...this._props}
                 fieldKey="text"
                 renderDepth={this._props.renderDepth + 1}
@@ -471,7 +470,6 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
             <div style={{ transform: `scale(${scale})`, transformOrigin: 'top left', width: `${100 / scale}%`, height: `${100 / scale}%` }}>
                 {!(this.Document instanceof Doc) || !this.treeChildren ? null : this.Document.treeView_HasOverlay ? (
                     <CollectionFreeFormView
-                        // eslint-disable-next-line react/jsx-props-no-spreading
                         {...this._props}
                         setContentViewBox={emptyFunction}
                         NativeWidth={returnZero}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 2d2c0fce5..620be2726 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -43,7 +43,7 @@ import './TabDocView.scss';
 import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
 
 interface TabMinimapViewProps {
-    document: Doc;
+    doc: Doc;
     tabView: () => DocumentView | undefined;
     addDocTab: (doc: Doc | Doc[], where: OpenWhere) => boolean;
     PanelWidth: () => number;
@@ -100,15 +100,15 @@ export class TabMinimapView extends ObservableReactComponent<TabMinimapViewProps
         return bounds === undefined ? bounds : { l: bounds.x + width / 2 - dim / 2, t: bounds.y + height / 2 - dim / 2, cx, cy, dim };
     }
     @computed get xPadding() {
-        return !this.renderBounds ? 0 : Math.max(0, this._props.PanelWidth() / NumCast(this._props.document._freeform_scale, 1) - 2 * (this.renderBounds.cx - this.renderBounds.l));
+        return !this.renderBounds ? 0 : Math.max(0, this._props.PanelWidth() / NumCast(this._props.doc._freeform_scale, 1) - 2 * (this.renderBounds.cx - this.renderBounds.l));
     }
     @computed get yPadding() {
-        return !this.renderBounds ? 0 : Math.max(0, this._props.PanelHeight() / NumCast(this._props.document._freeform_scale, 1) - 2 * (this.renderBounds.cy - this.renderBounds.l));
+        return !this.renderBounds ? 0 : Math.max(0, this._props.PanelHeight() / NumCast(this._props.doc._freeform_scale, 1) - 2 * (this.renderBounds.cy - this.renderBounds.l));
     }
-    childLayoutTemplate = () => Cast(this._props.document.childLayoutTemplate, Doc, null);
-    returnMiniSize = () => NumCast(this._props.document._miniMapSize, 150);
+    childLayoutTemplate = () => Cast(this._props.doc.childLayoutTemplate, Doc, null);
+    returnMiniSize = () => NumCast(this._props.doc._miniMapSize, 150);
     miniDown = (e: React.PointerEvent) => {
-        const doc = this._props.document;
+        const doc = this._props.doc;
         const miniSize = this.returnMiniSize();
         doc &&
             setupMoveUpEvents(
@@ -127,15 +127,15 @@ export class TabMinimapView extends ObservableReactComponent<TabMinimapViewProps
     popup = () => {
         const { renderBounds } = this;
         if (!renderBounds) return <div />;
-        const miniWidth = () => (this._props.PanelWidth() / NumCast(this._props.document._freeform_scale, 1) / renderBounds.dim) * 100;
-        const miniHeight = () => (this._props.PanelHeight() / NumCast(this._props.document._freeform_scale, 1) / renderBounds.dim) * 100;
-        const miniLeft = () => 50 + ((NumCast(this._props.document._freeform_panX) - renderBounds.cx) / renderBounds.dim) * 100 - miniWidth() / 2;
-        const miniTop = () => 50 + ((NumCast(this._props.document._freeform_panY) - renderBounds.cy) / renderBounds.dim) * 100 - miniHeight() / 2;
+        const miniWidth = () => (this._props.PanelWidth() / NumCast(this._props.doc._freeform_scale, 1) / renderBounds.dim) * 100;
+        const miniHeight = () => (this._props.PanelHeight() / NumCast(this._props.doc._freeform_scale, 1) / renderBounds.dim) * 100;
+        const miniLeft = () => 50 + ((NumCast(this._props.doc._freeform_panX) - renderBounds.cx) / renderBounds.dim) * 100 - miniWidth() / 2;
+        const miniTop = () => 50 + ((NumCast(this._props.doc._freeform_panY) - renderBounds.cy) / renderBounds.dim) * 100 - miniHeight() / 2;
         const miniSize = this.returnMiniSize();
         return (
             <div className="miniMap" style={{ width: miniSize, height: miniSize, background: this._props.background() }}>
                 <CollectionFreeFormView
-                    Document={this._props.document}
+                    Document={this._props.doc}
                     docViewPath={returnEmptyDocViewList}
                     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 // don't render overlay Docs since they won't scale
@@ -144,7 +144,7 @@ export class TabMinimapView extends ObservableReactComponent<TabMinimapViewProps
                     select={emptyFunction}
                     isSelected={returnFalse}
                     dontRegisterView
-                    fieldKey={Doc.LayoutFieldKey(this._props.document)}
+                    fieldKey={Doc.LayoutFieldKey(this._props.doc)}
                     addDocument={returnFalse}
                     moveDocument={returnFalse}
                     removeDocument={returnFalse}
@@ -172,7 +172,7 @@ export class TabMinimapView extends ObservableReactComponent<TabMinimapViewProps
         );
     };
     render() {
-        return this._props.document.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this._props.document)) || this._props.document?._type_collection !== CollectionViewType.Freeform ? null : (
+        return this._props.doc.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this._props.doc)) || this._props.doc?._type_collection !== CollectionViewType.Freeform ? null : (
             <div className="miniMap-hidden">
                 <Popup icon={<FontAwesomeIcon icon="globe-asia" size="lg" />} color={SnappingManager.userVariantColor} type={Type.TERT} onPointerDown={e => e.stopPropagation()} placement="top-end" popup={this.popup} />
             </div>
@@ -631,7 +631,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
                 }}>
                 {!this._activated || !this._document ? null : this.renderDocView(this._document)}
                 {this.disableMinimap() || !this._document ? null : (
-                    <TabMinimapView key="minimap" addDocTab={this.addDocTab} PanelHeight={this.PanelHeight} PanelWidth={this.PanelWidth} background={this.miniMapColor} document={this._document} tabView={this.tabView} />
+                    <TabMinimapView key="minimap" addDocTab={this.addDocTab} PanelHeight={this.PanelHeight} PanelWidth={this.PanelWidth} background={this.miniMapColor} doc={this._document} tabView={this.tabView} />
                 )}
             </div>
         );
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
index 35c6d30fe..89d2bf2c3 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
@@ -12,8 +12,8 @@ import { CollectionFreeFormView } from './CollectionFreeFormView';
 import './CollectionFreeFormView.scss';
 
 export interface CollectionFreeFormInfoUIProps {
-    Document: Doc;
-    LayoutDoc: Doc;
+    Doc: Doc;
+    layoutDoc: Doc;
     childDocs: () => Doc[];
     close: () => void;
 }
@@ -23,7 +23,7 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio
     public static Init() {
         CollectionFreeFormView.SetInfoUICreator((doc: Doc, layout: Doc, childDocs: () => Doc[], close: () => void) => (
             //
-            <CollectionFreeFormInfoUI Document={doc} LayoutDoc={layout} childDocs={childDocs} close={close} />
+            <CollectionFreeFormInfoUI Doc={doc} layoutDoc={layout} childDocs={childDocs} close={close} />
         ));
     }
     _firstDocPos = { x: 0, y: 0 };
@@ -40,7 +40,7 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio
     set currState(val) { runInAction(() => {this._currState = val;}); } // prettier-ignore
 
     componentWillUnmount(): void {
-        this._props.Document.$backgroundColor = this._originalbackground;
+        this._props.Doc.$backgroundColor = this._originalbackground;
     }
 
     setCurrState = (state: infoState) => {
@@ -51,10 +51,10 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio
     };
 
     setupStates = () => {
-        this._originalbackground = StrCast(this._props.Document.$backgroundColor);
+        this._originalbackground = StrCast(this._props.Doc.$backgroundColor);
         //  state entry functions
-        // const setBackground = (colour: string) => () => {this._props.Document.$backgroundColor = colour;} // prettier-ignore
-        // const setOpacity = (opacity: number) => () => {this._props.LayoutDoc.opacity = opacity;} // prettier-ignore
+        // const setBackground = (colour: string) => () => {this._props.Doc.$backgroundColor = colour;} // prettier-ignore
+        // const setOpacity = (opacity: number) => () => {this._props.layoutDoc.opacity = opacity;} // prettier-ignore
         //  arc transition trigger conditions
         const firstDoc = () => (this._props.childDocs().length ? this._props.childDocs()[0] : undefined);
         const numDocs = () => this._props.childDocs().length;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
index bc9dd022c..2683d9439 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
@@ -7,7 +7,7 @@ import { ObservableReactComponent } from '../../ObservableReactComponent';
 import './CollectionFreeFormView.scss';
 
 export interface CollectionFreeFormPannableContentsProps {
-    Document: Doc;
+    Doc: Doc;
     viewDefDivClick?: ScriptField;
     children?: React.ReactNode | undefined;
     transition: () => string;
@@ -33,7 +33,7 @@ export class CollectionFreeFormPannableContents extends ObservableReactComponent
         makeObservable(this);
     }
     @computed get presPaths() {
-        return this._props.showPresPaths() ? CollectionFreeFormPannableContents._overlayPlugin?.(this._props.Document) : null;
+        return this._props.showPresPaths() ? CollectionFreeFormPannableContents._overlayPlugin?.(this._props.Doc) : null;
     }
     // rectangle highlight used when following trail/link to a region of a collection that isn't a document
     showViewport = (viewport: { panX: number; panY: number; width: number; height: number } | undefined) =>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
index f64c6715b..86310dca3 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
@@ -13,7 +13,7 @@ import './CollectionFreeFormView.scss';
 @observer
 export class CollectionFreeFormRemoteCursors extends React.Component<CollectionViewProps> {
     @computed protected get cursors(): CursorField[] {
-        const { Document } = this.props;
+        const { Document: Document } = this.props;
         const cursors = Cast(Document.cursors, listSpec(CursorField));
         if (!cursors) {
             return [];
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 6030e146c..25cec9c0d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,10 +1,13 @@
-import { Bezier } from 'bezier-js';
 import { Button, Colors, Type } from '@dash/components';
+import { Slider } from '@mui/material';
+import { Bezier } from 'bezier-js';
 import { Property } from 'csstype';
 import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import { computedFn } from 'mobx-utils';
 import * as React from 'react';
+import { AiOutlineSend } from 'react-icons/ai';
+import ReactLoading from 'react-loading';
 import { ClientUtils, DashColor, lightOrDark, OmitKeys, returnFalse, returnZero, setupMoveUpEvents, UpdateIcon } from '../../../../ClientUtils';
 import { DateField } from '../../../../fields/DateField';
 import { Doc, DocListCast, Field, FieldType, Opt, StrListCast } from '../../../../fields/Doc';
@@ -28,6 +31,7 @@ import { DragManager } from '../../../util/DragManager';
 import { dropActionType } from '../../../util/DropActionTypes';
 import { CompileScript } from '../../../util/Scripting';
 import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
+import { SettingsManager } from '../../../util/SettingsManager';
 import { freeformScrollMode, SnappingManager } from '../../../util/SnappingManager';
 import { Transform } from '../../../util/Transform';
 import { undoable, UndoManager } from '../../../util/UndoManager';
@@ -37,26 +41,26 @@ import { InkingStroke } from '../../InkingStroke';
 import { CollectionFreeFormDocumentView } from '../../nodes/CollectionFreeFormDocumentView';
 import { SchemaCSVPopUp } from '../../nodes/DataVizBox/SchemaCSVPopUp';
 import {
+    ActiveEraserWidth,
     ActiveInkArrowEnd,
     ActiveInkArrowStart,
-    ActiveInkDash,
-    ActiveEraserWidth,
-    ActiveInkFillColor,
     ActiveInkBezierApprox,
     ActiveInkColor,
+    ActiveInkDash,
+    ActiveInkFillColor,
     ActiveInkWidth,
     ActiveIsInkMask,
     DocumentView,
     SetActiveInkColor,
     SetActiveInkWidth,
 } from '../../nodes/DocumentView';
-import { FieldViewProps } from '../../nodes/FieldView';
 import { FocusViewOptions } from '../../nodes/FocusViewOptions';
 import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
 import { OpenWhere } from '../../nodes/OpenWhere';
 import { PinDocView, PinProps } from '../../PinFuncs';
-import { StickerPalette } from '../../smartdraw/StickerPalette';
+import { DrawingFillHandler } from '../../smartdraw/DrawingFillHandler';
 import { DrawingOptions, SmartDrawHandler } from '../../smartdraw/SmartDrawHandler';
+import { StickerPalette } from '../../smartdraw/StickerPalette';
 import { StyleProp } from '../../StyleProp';
 import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView';
 import { TreeViewType } from '../CollectionTreeViewType';
@@ -67,11 +71,6 @@ import { CollectionFreeFormPannableContents } from './CollectionFreeFormPannable
 import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCursors';
 import './CollectionFreeFormView.scss';
 import { MarqueeView } from './MarqueeView';
-import ReactLoading from 'react-loading';
-import { SettingsManager } from '../../../util/SettingsManager';
-import { Slider } from '@mui/material';
-import { AiOutlineSend } from 'react-icons/ai';
-import { DrawingFillHandler } from '../../smartdraw/DrawingFillHandler';
 
 @observer
 class CollectionFreeFormOverlayView extends React.Component<{ elements: () => ViewDefResult[] }> {
@@ -180,10 +179,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         return (this._props.fitContentsToBox?.() || this.Document._freeform_fitContentsToBox) && !this.isAnnotationOverlay;
     }
     @computed get nativeWidth() {
-        return this._props.NativeWidth?.() || Doc.NativeWidth(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null));
+        return this._props.NativeWidth?.() || Doc.NativeWidth(this.Document);
     }
     @computed get nativeHeight() {
-        return this._props.NativeHeight?.() || Doc.NativeHeight(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null));
+        return this._props.NativeHeight?.() || Doc.NativeHeight(this.Document);
     }
     @computed get centeringShiftX(): number {
         return this._props.isAnnotationOverlay || this._props.originTopLeft ? 0 : this._props.PanelWidth() / 2 / this.nativeDimScaling; // shift so pan position is at center of window for non-overlay collections
@@ -285,8 +284,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     };
     // freeform_panx, freeform_pany, freeform_scale all attempt to get values first from the layout controller, then from the layout/dataDoc (or template layout doc), and finally from the resolved template data document.
     // this search order, for example, allows icons of cropped images to find the panx/pany/zoom on the cropped image's data doc instead of the usual layout doc because the zoom/panX/panY define the cropped image
-    panX = () => this.fitContentBounds?.cx ?? NumCast(this.Document[this.panXFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.freeform_panX, 1));
-    panY = () => this.fitContentBounds?.cy ?? NumCast(this.Document[this.panYFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.freeform_panY, 1));
+    panX = () => this.fitContentBounds?.cx ?? NumCast(this.Document[this.panXFieldKey], NumCast(this.Document.freeform_panX, 1));
+    panY = () => this.fitContentBounds?.cy ?? NumCast(this.Document[this.panYFieldKey], NumCast(this.Document.freeform_panY, 1));
     zoomScaling = () => this.fitContentBounds?.scale ?? NumCast(Doc.Layout(this.Document)[this.scaleFieldKey], 1); // , NumCast(DocCast(this.Document.resolvedDataDoc)?.[this.scaleFieldKey], 1));
     PanZoomCenterXf = () => (this._props.isAnnotationOverlay && this.zoomScaling() === 1 ? `` : `translate(${this.centeringShiftX}px, ${this.centeringShiftY}px) scale(${this.zoomScaling()}) translate(${-this.panX()}px, ${-this.panY()}px)`);
     ScreenToContentsXf = () => this.screenToFreeformContentsXf.copy();
@@ -1489,20 +1488,20 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
      * @param below whether to place the new text Doc below or to the right of the one being typed into.
      * @returns whether the new text doc was created and added successfully
      */
-    createTextDocCopy = undoable((fieldProps: FieldViewProps, below: boolean) => {
-        const textDoc = DocCast(fieldProps.Document.rootDocument, fieldProps.Document);
+    createTextDocCopy = undoable((textBox: FormattedTextBox, below: boolean) => {
+        const textDoc = DocCast(textBox.Document.rootDocument, textBox.Document);
         const newDoc = Doc.MakeCopy(textDoc, true);
-        newDoc['$' + Doc.LayoutFieldKey(newDoc, fieldProps.LayoutTemplateString)] = undefined; // the copy should not copy the text contents of it source, just the render style
+        newDoc['$' + Doc.LayoutFieldKey(newDoc, textBox._props.LayoutTemplateString)] = undefined; // the copy should not copy the text contents of it source, just the render style
         newDoc.x = NumCast(textDoc.x) + (below ? 0 : NumCast(textDoc._width) + 10);
         newDoc.y = NumCast(textDoc.y) + (below ? NumCast(textDoc._height) + 10 : 0);
         DocumentView.SetSelectOnLoad(newDoc);
         return this.addDocument?.(newDoc);
     }, 'copied text note');
 
-    onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
-        if ((e.metaKey || e.ctrlKey || e.altKey || fieldProps.Document._createDocOnCR) && ['Tab', 'Enter'].includes(e.key)) {
+    onKeyDown = (e: React.KeyboardEvent, textBox: FormattedTextBox) => {
+        if ((e.metaKey || e.ctrlKey || e.altKey || textBox.Document._createDocOnCR) && ['Tab', 'Enter'].includes(e.key)) {
             e.stopPropagation?.();
-            return this.createTextDocCopy(fieldProps, !e.altKey && e.key !== 'Tab');
+            return this.createTextDocCopy(textBox, !e.altKey && e.key !== 'Tab');
         }
         return undefined;
     };
@@ -1685,7 +1684,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
      * since rendering a large collection of documents can be slow, at startup, docs are rendered in batches.
      * each doc's render() method will call the cutoff provider which will let the doc know if it should render itself yet, or wait
      */
-    renderCutoffProvider = computedFn((doc: Doc) => (this._props.Document.isTemplateDoc || this._props.Document.isTemplateForField ? false : !this._renderCutoffData.get(doc[Id] + '')));
+    renderCutoffProvider = computedFn((doc: Doc) => (this.Document.isTemplateDoc || this.Document.isTemplateForField ? false : !this._renderCutoffData.get(doc[Id] + '')));
 
     doEngineLayout(
         poolData: Map<string, PoolData>,
@@ -2115,7 +2114,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         this.incrementalRender(); // needs to happen synchronously or freshly typed text documents will flash and miss their first characters
         return (
             <CollectionFreeFormPannableContents
-                Document={this.Document}
+                Doc={this.Document}
                 brushedView={this.brushedView}
                 isAnnotationOverlay={this.isAnnotationOverlay}
                 transform={this.PanZoomCenterXf}
@@ -2133,6 +2132,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             <MarqueeView
                 {...this._props}
                 ref={this._marqueeViewRef}
+                Doc={this.Document}
                 ungroup={this.Document.isGroup ? this.promoteCollection : undefined}
                 nudge={this.isAnnotationOverlay || this._props.renderDepth > 0 ? undefined : this.nudge}
                 addDocTab={this.addDocTab}
@@ -2149,7 +2149,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 isAnnotationOverlay={this.isAnnotationOverlay}>
                 {this.layoutDoc._freeform_backgroundGrid ? this.backgroundGrid : null}
                 {this.pannableContents}
-                {this._showAnimTimeline ? <Timeline ref={this._timelineRef} {...this._props} /> : null}
+                {this._showAnimTimeline ? <Timeline ref={this._timelineRef} {...this._props} Doc={this._props.Document} /> : null}
             </MarqueeView>
         );
     }
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index e3f4c6605..eaa8826ed 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -32,6 +32,7 @@ import { MarqueeOptionsMenu } from './MarqueeOptionsMenu';
 import './MarqueeView.scss';
 
 interface MarqueeViewProps {
+    Doc: Doc;
     getContainerTransform: () => Transform;
     getTransform: () => Transform;
     activeDocuments: () => Doc[];
diff --git a/src/client/views/collections/collectionSchema/SchemaCellField.tsx b/src/client/views/collections/collectionSchema/SchemaCellField.tsx
index e89822b4c..daffdf1f5 100644
--- a/src/client/views/collections/collectionSchema/SchemaCellField.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaCellField.tsx
@@ -21,11 +21,11 @@ import DOMPurify from 'dompurify';
  */
 
 export interface SchemaCellFieldProps {
+    Doc: Doc;
     contents: FieldType | undefined;
     fieldContents?: FieldViewProps;
     editing?: boolean;
     oneLine?: boolean;
-    Document: Doc;
     fieldKey: string;
     // eslint-disable-next-line no-use-before-define
     refSelectModeInfo: { enabled: boolean; currEditing: SchemaCellField | undefined };
@@ -55,7 +55,7 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
         }); //must be moved to end of batch or else other docs aren't loaded, so render as d-1 in function
     }
 
-    get docIndex(){return DocumentView.getDocViewIndex(this._props.Document);} // prettier-ignore
+    get docIndex(){return DocumentView.getDocViewIndex(this._props.Doc);} // prettier-ignore
 
     get selfRefPattern() {
         return `d${this.docIndex}.${this._props.fieldKey}`;
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index da203abfa..c9853fab0 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -171,7 +171,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
                             isolatedSelection={this.isolatedSelection}
                             key={key}
                             rowSelected={this._props.isSelected}
-                            Document={this.Document}
+                            Doc={this.Document}
                             col={index}
                             fieldKey={key}
                             allowCRs={false} // to enter text with new lines, must use \n
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 1b4f200a3..e6fe46638 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -40,7 +40,7 @@ import { SchemaCellField } from './SchemaCellField';
  */
 
 export interface SchemaTableCellProps {
-    Document: Doc;
+    Doc: Doc;
     col: number;
     deselectCell: () => void;
     selectCell: (doc: Doc, col: number, shift: boolean, ctrl: boolean) => void;
@@ -71,7 +71,7 @@ export interface SchemaTableCellProps {
 }
 
 function selectedCell(props: SchemaTableCellProps) {
-    return props.isRowActive() && props.selectedCol() === props.col && props.selectedCells()?.filter(d => d === props.Document)?.length;
+    return props.isRowActive() && props.selectedCol() === props.col && props.selectedCells()?.filter(d => d === props.Doc)?.length;
 }
 
 @observer
@@ -84,11 +84,11 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
         makeObservable(this);
     }
 
-    get docIndex(){return DocumentView.getDocViewIndex(this._props.Document);} // prettier-ignore
+    get docIndex(){return DocumentView.getDocViewIndex(this._props.Doc);} // prettier-ignore
 
     get isDefault(){return SchemaColumnHeader.isDefaultField(this._props.fieldKey);} // prettier-ignore
 
-    get lockedInteraction(){return (this.isDefault || this._props.Document._lockedSchemaEditing);} // prettier-ignore
+    get lockedInteraction(){return (this.isDefault || this._props.Doc._lockedSchemaEditing);} // prettier-ignore
 
     get backgroundColor() {
         if (this.lockedInteraction) {
@@ -102,7 +102,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
         return true;
     };
     public static renderProps(props: SchemaTableCellProps) {
-        const { Document, fieldKey, /* getFinfo,*/ columnWidth, isRowActive } = props;
+        const { Doc: Document, fieldKey, /* getFinfo,*/ columnWidth, isRowActive } = props;
         let protoCount = 0;
         let doc: Doc | undefined = Document;
         while (doc) {
@@ -196,7 +196,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
                 <SchemaCellField
                     fieldKey={this._props.fieldKey}
                     refSelectModeInfo={this._props.refSelectModeInfo}
-                    Document={this._props.Document}
+                    Doc={this._props.Doc}
                     highlightCells={(text: string) => this._props.highlightCells(this.adjustSelfReference(text))}
                     getCells={(text: string) => this._props.eqHighlightFunc(this.adjustSelfReference(text))}
                     ref={r => selectedCell(this._props) && this._props.autoFocus && r?.setIsFocused(true)}
@@ -224,7 +224,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
 
     get getCellType() {
         const columnTypeStr = this._props.getFinfo(this._props.fieldKey)?.fieldType;
-        const cellValue = this._props.Document[this._props.fieldKey];
+        const cellValue = this._props.Doc[this._props.fieldKey];
 
         if (cellValue instanceof ImageField) return ColumnType.Image;
         if (cellValue instanceof DateField) return ColumnType.Date;
@@ -252,8 +252,8 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
         const sides: Array<string | undefined> = [];
         sides[0] = selectedCell(this._props) ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined; // left
         sides[1] = selectedCell(this._props) ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined; // right
-        sides[2] = !this._props.isolatedSelection(this._props.Document)[0] && selectedCell(this._props) ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined; // top
-        sides[3] = !this._props.isolatedSelection(this._props.Document)[1] && selectedCell(this._props) ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined; // bottom
+        sides[2] = !this._props.isolatedSelection(this._props.Doc)[0] && selectedCell(this._props) ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined; // top
+        sides[3] = !this._props.isolatedSelection(this._props.Doc)[1] && selectedCell(this._props) ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined; // bottom
         return sides;
     }
 
@@ -272,7 +272,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
                     if (this._props.refSelectModeInfo.enabled && !selectedCell(this._props)) {
                         e.stopPropagation();
                         e.preventDefault();
-                        this._props.selectReference(this._props.Document, this._props.col);
+                        this._props.selectReference(this._props.Doc, this._props.col);
                         return;
                     }
 
@@ -280,9 +280,9 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
                     const ctrl: boolean = e.ctrlKey;
                     if (this._props.isRowActive?.()) {
                         if (selectedCell(this._props) && ctrl) {
-                            this._props.selectCell(this._props.Document, this._props.col, shift, ctrl);
+                            this._props.selectCell(this._props.Doc, this._props.col, shift, ctrl);
                             e.stopPropagation();
-                        } else !selectedCell(this._props) && this._props.selectCell(this._props.Document, this._props.col, shift, ctrl);
+                        } else !selectedCell(this._props) && this._props.selectCell(this._props.Doc, this._props.col, shift, ctrl);
                     }
                 })}
                 style={{
@@ -321,8 +321,8 @@ export class SchemaImageCell extends ObservableReactComponent<SchemaTableCellPro
     }
 
     get url() {
-        const field = Cast(this._props.Document[this._props.fieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc
-        const alts = DocListCast(this._props.Document[this._props.fieldKey + '_alternates']); // retrieve alternate documents that may be rendered as alternate images
+        const field = Cast(this._props.Doc[this._props.fieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc
+        const alts = DocListCast(this._props.Doc[this._props.fieldKey + '_alternates']); // retrieve alternate documents that may be rendered as alternate images
         const altpaths = alts
             .map(doc => Cast(doc[Doc.LayoutFieldKey(doc)], ImageField, null)?.url)
             .filter(url => url)
@@ -359,7 +359,7 @@ export class SchemaImageCell extends ObservableReactComponent<SchemaTableCellPro
     };
 
     render() {
-        const aspect = Doc.NativeAspect(this._props.Document); // aspect ratio
+        const aspect = Doc.NativeAspect(this._props.Doc); // aspect ratio
         // let width = Math.max(75, this._props.columnWidth); // get a with that is no smaller than 75px
         // const height = Math.max(75, width / aspect); // get a height either proportional to that or 75 px
         const height = this._props.rowHeight() ? this._props.rowHeight() - (this._props.padding || 6) * 2 : undefined;
@@ -379,7 +379,7 @@ export class SchemaDateCell extends ObservableReactComponent<SchemaTableCellProp
     @observable _pickingDate: boolean = false;
     @computed get date(): DateField {
         // if the cell is a date field, cast then contents to a date. Otherrwwise, make the contents undefined.
-        return DateCast(this._props.Document[this._props.fieldKey]);
+        return DateCast(this._props.Doc[this._props.fieldKey]);
     }
 
     handleChange = undoable((date: Date | null) => {
@@ -388,7 +388,7 @@ export class SchemaDateCell extends ObservableReactComponent<SchemaTableCellProp
         //     this.applyToDoc(this._document, this._props.row, this._props.col, script.run);
         // } else {
         // ^ DateCast is always undefined for some reason, but that is what the field should be set to
-        date && (this._props.Document[this._props.fieldKey] = new DateField(date));
+        date && (this._props.Doc[this._props.fieldKey] = new DateField(date));
         // }
     }, 'date change');
 
@@ -451,11 +451,11 @@ export class SchemaBoolCell extends ObservableReactComponent<SchemaTableCellProp
                     onPointerDown={e => e.stopPropagation()}
                     style={{ marginRight: 4 }}
                     type="checkbox"
-                    checked={BoolCast(this._props.Document[this._props.fieldKey])}
+                    checked={BoolCast(this._props.Doc[this._props.fieldKey])}
                     onChange={undoable((value: React.ChangeEvent<HTMLInputElement> | undefined) => {
                         if ((value?.nativeEvent as MouseEvent | PointerEvent).shiftKey) {
                             this._props.setColumnValues(this._props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + (value?.target?.checked.toString() ?? ''));
-                        } else Doc.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + (value?.target?.checked.toString() ?? ''));
+                        } else Doc.SetField(this._props.Doc, this._props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + (value?.target?.checked.toString() ?? ''));
                     }, 'set bool cell')}
                 />
 
@@ -463,14 +463,14 @@ export class SchemaBoolCell extends ObservableReactComponent<SchemaTableCellProp
                     contents=""
                     fieldContents={fieldProps}
                     editing={selectedCell(this._props) ? undefined : false}
-                    GetValue={() => Field.toKeyValueString(this._props.Document, this._props.fieldKey)}
+                    GetValue={() => Field.toKeyValueString(this._props.Doc, this._props.fieldKey)}
                     SetValue={undoable((value: string, shiftDown?: boolean, enterKey?: boolean) => {
                         if (shiftDown && enterKey) {
                             this._props.setColumnValues(this._props.fieldKey.replace(/^_/, ''), value);
                             this._props.finishEdit?.();
                             return true;
                         }
-                        const set = Doc.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), value, Doc.IsDataProto(this._props.Document) ? true : undefined);
+                        const set = Doc.SetField(this._props.Doc, this._props.fieldKey.replace(/^_/, ''), value, Doc.IsDataProto(this._props.Doc) ? true : undefined);
                         this._props.finishEdit?.();
                         return set;
                     }, 'set bool cell')}
@@ -538,10 +538,10 @@ export class SchemaEnumerationCell extends ObservableReactComponent<SchemaTableC
                         }}
                         menuPortalTarget={this._props.menuTarget}
                         menuPosition="absolute"
-                        placeholder={StrCast(this._props.Document[this._props.fieldKey], 'select...')}
+                        placeholder={StrCast(this._props.Doc[this._props.fieldKey], 'select...')}
                         options={options}
                         isMulti={false}
-                        onChange={val => Doc.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), `"${val?.value ?? ''}"`)}
+                        onChange={val => Doc.SetField(this._props.Doc, this._props.fieldKey.replace(/^_/, ''), `"${val?.value ?? ''}"`)}
                     />
                 </div>
             </div>
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index ce1e9280a..6f86383c2 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -304,7 +304,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                 ) : (
                     <DocumentView
                         {...OmitKeys(this._props,this.WrapperKeys.map(val => val.lower)).omit} // prettier-ignore
-                        Document={this._props.Document}
+                        Document={this._renderDoc}
                         renderDepth={this._props.renderDepth}
                         isContentActive={this._props.isContentActive}
                         childFilters={this._props.childFilters}
diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
index d5e37b3b5..117eb05f8 100644
--- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx
+++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
@@ -1,43 +1,39 @@
+import { Colors, Toggle, ToggleType, Type } from '@dash/components';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Checkbox } from '@mui/material';
-import { Colors, Toggle, ToggleType, Type } from '@dash/components';
 import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { ClientUtils, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents } from '../../../../ClientUtils';
+import { returnEmptyString, returnFalse, returnOne, setupMoveUpEvents } from '../../../../ClientUtils';
 import { emptyFunction } from '../../../../Utils';
-import { Doc, DocListCast, Field, FieldType, NumListCast, Opt, StrListCast } from '../../../../fields/Doc';
-import { AclAdmin, AclAugment, AclEdit } from '../../../../fields/DocSymbols';
+import { Doc, DocListCast, Field, Opt, StrListCast } from '../../../../fields/Doc';
 import { InkTool } from '../../../../fields/InkField';
 import { List } from '../../../../fields/List';
-import { PrefetchProxy } from '../../../../fields/Proxy';
 import { Cast, CsvCast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
 import { CsvField } from '../../../../fields/URLField';
-import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util';
+import { TraceMobx } from '../../../../fields/util';
 import { GPTCallType, gptAPICall } from '../../../apis/gpt/GPT';
 import { DocUtils } from '../../../documents/DocUtils';
 import { DocumentType } from '../../../documents/DocumentTypes';
 import { Docs } from '../../../documents/Documents';
-import { LinkManager } from '../../../util/LinkManager';
 import { UndoManager, undoable } from '../../../util/UndoManager';
 import { ContextMenu } from '../../ContextMenu';
 import { ViewBoxAnnotatableComponent } from '../../DocComponent';
 import { MarqueeAnnotator } from '../../MarqueeAnnotator';
 import { PinProps } from '../../PinFuncs';
 import { SidebarAnnos } from '../../SidebarAnnos';
-import { CollectionFreeFormView } from '../../collections/collectionFreeForm';
 import { AnchorMenu } from '../../pdf/AnchorMenu';
 import { GPTPopup, GPTPopupMode } from '../../pdf/GPTPopup/GPTPopup';
 import { DocumentView } from '../DocumentView';
 import { FieldView, FieldViewProps } from '../FieldView';
 import { FocusViewOptions } from '../FocusViewOptions';
 import './DataVizBox.scss';
-import { Col, DataVizTemplateInfo, DocCreatorMenu, LayoutType} from './DocCreatorMenu/DocCreatorMenu';
+import { Col, DocCreatorMenu } from './DocCreatorMenu/DocCreatorMenu';
+import { TemplateFieldSize, TemplateFieldType } from './DocCreatorMenu/TemplateBackend';
 import { Histogram } from './components/Histogram';
 import { LineChart } from './components/LineChart';
 import { PieChart } from './components/PieChart';
 import { TableBox } from './components/TableBox';
-import { TemplateFieldSize, TemplateFieldType } from './DocCreatorMenu/TemplateBackend';
 
 export enum DataVizView {
     TABLE = 'table',
@@ -440,10 +436,10 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
         };
         if (!this.records.length) return 'no data/visualization';
         switch (this.dataVizView) {
-            case DataVizView.TABLE:     return <TableBox  {...sharedProps} specHighlightedRow={this._specialHighlightedRow} docView={this.DocumentView} selectAxes={this.selectAxes} selectTitleCol={this.selectTitleCol}/>;
-            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;}} 
+            case DataVizView.TABLE:     return <TableBox  {...sharedProps} Doc={this.Document} specHighlightedRow={this._specialHighlightedRow} docView={this.DocumentView} selectAxes={this.selectAxes} selectTitleCol={this.selectTitleCol}/>;
+            case DataVizView.LINECHART: return <LineChart {...sharedProps} Doc={this.Document} dataDoc={this.dataDoc} fieldKey={this.fieldKey} ref={r => {this._vizRenderer = r ?? undefined;}} vizBox={this} />;
+            case DataVizView.HISTOGRAM: return <Histogram {...sharedProps} Doc={this.Document} dataDoc={this.dataDoc} fieldKey={this.fieldKey} ref={r => {this._vizRenderer = r ?? undefined;}} />;
+            case DataVizView.PIECHART:  return <PieChart  {...sharedProps} Doc={this.Document} dataDoc={this.dataDoc} fieldKey={this.fieldKey} ref={r => {this._vizRenderer = r ?? undefined;}} 
             margin={{ top: 10, right: 15, bottom: 15, left: 15 }} />;
             default:
         } // prettier-ignore
@@ -574,9 +570,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
 
         const cols = Array.from(Object.keys(this.records[0])).filter(header => header !== '' && header !== undefined);
 
-        cols.forEach(col => {
-            this.setColumnDefault(col, `${this.records[rowToCheck][col]}`);
-        });
+        cols.forEach(col => this.setColumnDefault(col, `${this.records[rowToCheck][col]}`));
     };
 
     updateGPTSummary = async () => {
@@ -706,7 +700,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                         ref={this._sidebarRef}
                         {...this._props}
                         fieldKey={this.fieldKey}
-                        Document={this.Document}
+                        Doc={this.Document}
                         layoutDoc={this.layoutDoc}
                         dataDoc={this.dataDoc}
                         usePanelWidth
diff --git a/src/client/views/nodes/DataVizBox/components/Histogram.tsx b/src/client/views/nodes/DataVizBox/components/Histogram.tsx
index 5a9442d2f..776d65211 100644
--- a/src/client/views/nodes/DataVizBox/components/Histogram.tsx
+++ b/src/client/views/nodes/DataVizBox/components/Histogram.tsx
@@ -17,7 +17,7 @@ import { scaleCreatorNumerical, yAxisCreator } from '../utils/D3Utils';
 import './Chart.scss';
 
 export interface HistogramProps {
-    Document: Doc;
+    Doc: Doc;
     layoutDoc: Doc;
     axes: string[];
     titleCol: string;
@@ -88,7 +88,7 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
     }
 
     @computed get parentViz() {
-        return DocCast(this._props.Document.dataViz_parentViz);
+        return DocCast(this._props.Doc.dataViz_parentViz);
     }
 
     @computed get rangeVals(): { xMin?: number; xMax?: number; yMin?: number; yMax?: number } {
@@ -126,7 +126,7 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
         const anchor = Docs.Create.ConfigDocument({
             title: 'histogram doc selection' + this._currSelected,
         });
-        PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this._props.Document);
+        PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this._props.Doc);
         return anchor;
     };
 
@@ -416,11 +416,9 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
                 let barColor;
                 const barColors = StrListCast(this._props.layoutDoc.dataViz_histogram_barColors).map(each => each.split('::'));
                 barColors.forEach(each => {
-                    // eslint-disable-next-line prefer-destructuring
                     if (d[0] && d[0].toString() && each[0] == d[0].toString()) barColor = each[1];
                     else {
                         const range = StrCast(each[0]).split(' to ');
-                        // eslint-disable-next-line prefer-destructuring
                         if (Number(range[0]) <= d[0] && d[0] <= Number(range[1])) barColor = each[1];
                     }
                 });
@@ -454,11 +452,9 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
                 let barColor;
                 const barColors = StrListCast(this._props.layoutDoc.dataViz_histogram_barColors).map(each => each.split('::'));
                 barColors.forEach(each => {
-                    // eslint-disable-next-line prefer-destructuring
                     if (d[0] && d[0].toString() && each[0] == d[0].toString()) barColor = each[1];
                     else {
                         const range = StrCast(each[0]).split(' to ');
-                        // eslint-disable-next-line prefer-destructuring
                         if (Number(range[0]) <= d[0] && d[0] <= Number(range[1])) barColor = each[1];
                     }
                 });
@@ -471,7 +467,7 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
         this.updateSavedUI();
         this._histogramData;
         let curSelectedBarName = '';
-        let titleAccessor: any = 'dataViz_histogram_title';
+        let titleAccessor = 'dataViz_histogram_title';
         if (this._props.axes.length === 2) titleAccessor = titleAccessor + this._props.axes[0] + '-' + this._props.axes[1];
         else if (this._props.axes.length > 0) titleAccessor += this._props.axes[0];
         if (!this._props.layoutDoc[titleAccessor]) this._props.layoutDoc[titleAccessor] = this.defaultGraphTitle;
@@ -503,7 +499,6 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
         let selectedBarColor;
         const barColors = StrListCast(this._props.layoutDoc.histogramBarColors).map(each => each.split('::'));
         barColors.forEach(each => {
-            // eslint-disable-next-line prefer-destructuring
             each[0] === curSelectedBarName && (selectedBarColor = each[1]);
         });
 
diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
index b55d509ff..6b047546c 100644
--- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx
+++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
@@ -22,7 +22,7 @@ export interface SelectedDataPoint extends DataPoint {
 }
 export interface LineChartProps {
     vizBox: DataVizBox;
-    Document: Doc;
+    Doc: Doc;
     layoutDoc: Doc;
     axes: string[];
     titleCol: string;
@@ -53,7 +53,7 @@ export class LineChart extends ObservableReactComponent<LineChartProps> {
     }
 
     @computed get titleAccessor() {
-        let titleAccessor: any = 'dataViz_lineChart_title';
+        let titleAccessor = 'dataViz_lineChart_title';
         if (this._props.axes.length === 2) titleAccessor = titleAccessor + this._props.axes[0] + '-' + this._props.axes[1];
         else if (this._props.axes.length > 0) titleAccessor += this._props.axes[0];
         return titleAccessor;
@@ -74,7 +74,7 @@ export class LineChart extends ObservableReactComponent<LineChartProps> {
         return this._props.axes[1] + ' vs. ' + this._props.axes[0] + ' Line Chart';
     }
     @computed get parentViz() {
-        return DocCast(this._props.Document.dataViz_parentViz);
+        return DocCast(this._props.Doc.dataViz_parentViz);
     }
     @computed get incomingHighlited() {
         // return selected x and y axes
@@ -113,7 +113,7 @@ export class LineChart extends ObservableReactComponent<LineChartProps> {
             //
             title: 'line doc selection' + (this._currSelected?.x ?? ''),
         });
-        PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this._props.Document);
+        PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this._props.Doc);
         anchor.config_dataVizSelection = this._currSelected ? new List<number>([this._currSelected.x, this._currSelected.y]) : undefined;
         return anchor;
     };
diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx
index 86e6ad8e4..0ae70786f 100644
--- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx
+++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx
@@ -16,7 +16,7 @@ import { PinProps, PinDocView } from '../../../PinFuncs';
 import './Chart.scss';
 
 export interface PieChartProps {
-    Document: Doc;
+    Doc: Doc;
     layoutDoc: Doc;
     axes: string[];
     titleCol: string;
@@ -83,7 +83,7 @@ export class PieChart extends ObservableReactComponent<PieChartProps> {
     }
 
     @computed get parentViz() {
-        return DocCast(this._props.Document.dataViz_parentViz);
+        return DocCast(this._props.Doc.dataViz_parentViz);
     }
 
     componentWillUnmount() {
@@ -114,7 +114,7 @@ export class PieChart extends ObservableReactComponent<PieChartProps> {
             //
             title: 'piechart doc selection' + this._currSelected,
         });
-        PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this._props.Document);
+        PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this._props.Doc);
         return anchor;
     };
 
@@ -169,7 +169,6 @@ export class PieChart extends ObservableReactComponent<PieChartProps> {
                 // inside the slice of it crosses an odd number of edges
                 const showSelected = this.byCategory ? pieDataSet[index] : this._pieChartData[index];
                 let key = 'data'; // key that represents slice
-                // eslint-disable-next-line prefer-destructuring
                 if (Object.keys(showSelected)[0] === 'frequency') key = Object.keys(showSelected)[1];
                 if (changeSelectedVariables) {
                     let sameAsAny = false;
@@ -296,7 +295,7 @@ export class PieChart extends ObservableReactComponent<PieChartProps> {
             if (descriptionField) dataPointVal[descriptionField] = each[descriptionField];
             try {
                 dataPointVal[percentField] = Number(dataPointVal[percentField].replace(/\$/g, '').replace(/%/g, '').replace(/#/g, '').replace(/</g, ''));
-            } catch (error) {
+            } catch {
                 /* empty */
             }
             possibleDataPointVals.push(dataPointVal);
@@ -306,7 +305,6 @@ export class PieChart extends ObservableReactComponent<PieChartProps> {
         // to make sure all important slice information is on 'd' object
         let addKey: any = false;
         if (pieDataSet.length && Object.keys(pieDataSet[0])[0] === 'frequency') {
-            // eslint-disable-next-line prefer-destructuring
             addKey = Object.keys(pieDataSet[0])[1];
         }
         arcs.append('path')
@@ -324,7 +322,6 @@ export class PieChart extends ObservableReactComponent<PieChartProps> {
                     const sliceTitle = dataPoint[this._props.axes[0]];
                     const accessByName = StrCast(sliceTitle) ? StrCast(sliceTitle).replace(/\$/g, '').replace(/%/g, '').replace(/#/g, '').replace(/</g, '') : sliceTitle;
                     sliceColors.forEach(each => {
-                        // eslint-disable-next-line prefer-destructuring
                         each[0] === accessByName && (sliceColor = each[1]);
                     });
                 }
@@ -337,7 +334,7 @@ export class PieChart extends ObservableReactComponent<PieChartProps> {
                 });
                 return selectThisData ? 'slice hover' : 'slice';
             })
-            // @ts-ignore
+            // @ts-expect-error  types don't match
             .attr('d', arc)
             .on('click', onPointClick)
             .on('mouseover', onHover)
@@ -388,7 +385,7 @@ export class PieChart extends ObservableReactComponent<PieChartProps> {
     };
 
     render() {
-        let titleAccessor: any = 'dataViz_pie_title';
+        let titleAccessor = 'dataViz_pie_title';
         if (this._props.axes.length === 2) titleAccessor = titleAccessor + this._props.axes[0] + '-' + this._props.axes[1];
         else if (this._props.axes.length > 0) titleAccessor += this._props.axes[0];
         if (!this._props.layoutDoc[titleAccessor]) this._props.layoutDoc[titleAccessor] = this.defaultGraphTitle;
@@ -420,7 +417,6 @@ export class PieChart extends ObservableReactComponent<PieChartProps> {
         let selectedSliceColor;
         const sliceColors = StrListCast(this._props.layoutDoc.dataViz_pie_sliceColors).map(each => each.split('::'));
         sliceColors.forEach(each => {
-            // eslint-disable-next-line prefer-destructuring
             if (each[0] === curSelectedSliceName!) selectedSliceColor = each[1];
         });
 
diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
index b6183946a..b73123691 100644
--- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx
+++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
@@ -19,7 +19,7 @@ import './Chart.scss';
 const { DATA_VIZ_TABLE_ROW_HEIGHT } = require('../../../global/globalCssVariables.module.scss'); // prettier-ignore
 
 interface TableBoxProps {
-    Document: Doc;
+    Doc: Doc;
     layoutDoc: Doc;
     records: { [key: string]: unknown }[];
     selectAxes: (axes: string[]) => void;
@@ -81,7 +81,7 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> {
     }
 
     @computed get parentViz() {
-        return DocCast(this._props.Document.dataViz_parentViz);
+        return DocCast(this._props.Doc.dataViz_parentViz);
     }
 
     @computed get columns() {
@@ -139,21 +139,21 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> {
             e,
             moveEv => {
                 // dragging off a column to create a brushed DataVizBox
-                const sourceAnchorCreator = () => this._props.docView?.()?.Document || this._props.Document;
+                const sourceAnchorCreator = () => this._props.docView?.()?.Document || this._props.Doc;
                 const targetCreator = (annotationOn: Doc | undefined) => {
                     const doc = this._props.docView?.()?.Document;
                     if (doc) {
                         const embedding = Doc.MakeEmbedding(doc);
                         embedding._dataViz = DataVizView.TABLE;
                         embedding._dataViz_axes = new List<string>([col]);
-                        embedding._dataViz_parentViz = this._props.Document;
+                        embedding._dataViz_parentViz = this._props.Doc;
                         embedding.annotationOn = annotationOn;
                         embedding.histogramBarColors = Field.Copy(this._props.layoutDoc.histogramBarColors);
                         embedding.defaultHistogramColor = this._props.layoutDoc.defaultHistogramColor;
                         embedding.pieSliceColors = Field.Copy(this._props.layoutDoc.pieSliceColors);
                         return embedding;
                     }
-                    return this._props.Document;
+                    return this._props.Doc;
                 };
                 if (this._props.docView?.() && !ClientUtils.isClick(moveEv.clientX, moveEv.clientY, downX, downY, Date.now())) {
                     DragManager.StartAnchorAnnoDrag(moveEv.target instanceof HTMLElement ? [moveEv.target] : [], new DragManager.AnchorAnnoDragData(this._props.docView()!, sourceAnchorCreator, targetCreator), downX, downY, {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index e9353b001..c355e57d4 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -758,7 +758,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
                     }}>
                     <DocumentContentsView
                         {...this._props}
-                        layoutFieldKey={StrCast(this._props.Document.layout_fieldKey, 'layout')}
+                        layoutFieldKey={StrCast(this._renderDoc.layout_fieldKey, 'layout')}
                         pointerEvents={this.contentPointerEvents}
                         setContentViewBox={this.setContentView}
                         childFilters={this.childFilters}
@@ -831,7 +831,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
             onPointerDown={action(() => { this._changingTitleField = true; })} // prettier-ignore
             style={{ width: 'max-content', background: SnappingManager.userBackgroundColor, color: SnappingManager.userColor, transformOrigin: 'left', transform: `scale(${this.titleHeight / 30 /* height of Dropdown */})` }}>
             <FieldsDropdown
-                Document={this.Document}
+                Doc={this.Document}
                 placeholder={placeholder}
                 selectFunc={action((field: string | number) => {
                     if (this.layoutDoc.layout_showTitle) {
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 2e40f39ed..abba99c3d 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -15,6 +15,7 @@ import { FocusViewOptions } from './FocusViewOptions';
 import { OpenWhere } from './OpenWhere';
 import { WebField } from '../../../fields/URLField';
 import { ContextMenuProps } from '../ContextMenuItem';
+import { FormattedTextBox } from './formattedText/FormattedTextBox';
 
 export type FocusFuncType = (doc: Doc, options: FocusViewOptions) => Opt<number>;
 export type StyleProviderFuncType = (
@@ -83,8 +84,7 @@ export interface FieldViewSharedProps {
     onDoubleClickScript?: () => ScriptField;
     onPointerDownScript?: () => ScriptField;
     onPointerUpScript?: () => ScriptField;
-    // eslint-disable-next-line no-use-before-define
-    onKey?: (e: React.KeyboardEvent, fieldProps: FieldViewProps) => boolean | undefined;
+    onKey?: (e: React.KeyboardEvent, textBox: FormattedTextBox) => boolean | undefined;
     fitWidth?: (doc: Doc) => boolean | undefined;
     dontCenter?: 'x' | 'y' | 'xy' | undefined;
     searchFilterDocs: () => Doc[];
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 5d9718760..d121b492f 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -648,7 +648,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                             onClick={action(async () => {
                                 this._regenerateLoading = true;
                                 if (this._fireflyRefStrength) {
-                                    DrawingFillHandler.drawingToImage(this.props.Document, this._fireflyRefStrength, this._regenInput || StrCast(this.Document.title), this.Document)?.then(action(() => (this._regenerateLoading = false)));
+                                    DrawingFillHandler.drawingToImage(this.Document, this._fireflyRefStrength, this._regenInput || StrCast(this.Document.title), this.Document)?.then(action(() => (this._regenerateLoading = false)));
                                 } else {
                                     SmartDrawHandler.Instance.regenerate([this.Document], undefined, undefined, this._regenInput || StrCast(this.Document.title), true).then(
                                         action(newImgs => {
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 32c9efbd9..8911fac6d 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -58,8 +58,8 @@ export class KeyValueBox extends ViewBoxBaseComponent<FieldViewProps>() {
     onEnterKey = (e: React.KeyboardEvent): void => {
         if (e.key === 'Enter') {
             e.stopPropagation();
-            if (this._keyInput.current?.value && this._valInput.current?.value && this._props.Document) {
-                if (KeyValueBox.SetField(this._props.Document, this._keyInput.current.value, this._valInput.current.value)) {
+            if (this._keyInput.current?.value && this._valInput.current?.value && this.Document) {
+                if (KeyValueBox.SetField(this.Document, this._keyInput.current.value, this._valInput.current.value)) {
                     this._keyInput.current.value = '';
                     this._valInput.current.value = '';
                     document.body.focus();
@@ -137,7 +137,7 @@ export class KeyValueBox extends ViewBoxBaseComponent<FieldViewProps>() {
     rowHeight = () => 30;
 
     @computed get createTable() {
-        const doc = this._props.Document;
+        const doc = this.Document;
         if (!doc) {
             return (
                 <tr>
@@ -305,8 +305,8 @@ export class KeyValueBox extends ViewBoxBaseComponent<FieldViewProps>() {
         openItems.push({
             description: 'Default Perspective',
             event: () => {
-                this._props.addDocTab(this._props.Document, OpenWhere.close);
-                this._props.addDocTab(this._props.Document, OpenWhere.addRight);
+                this._props.addDocTab(this.Document, OpenWhere.close);
+                this._props.addDocTab(this.Document, OpenWhere.addRight);
             },
             icon: 'image',
         });
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 65d78f754..a563b7c1b 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -1300,7 +1300,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                         ref={this._sidebarRef}
                         {...this._props}
                         fieldKey={this.fieldKey}
-                        Document={this.Document}
+                        Doc={this.Document}
                         layoutDoc={this.layoutDoc}
                         dataDoc={this.dataDoc}
                         usePanelWidth
diff --git a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
index ddeaf4d0d..e0efab576 100644
--- a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
+++ b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
@@ -798,7 +798,6 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<FieldViewProps>
                                           PanelHeight={returnOne}
                                           NativeWidth={returnOne}
                                           NativeHeight={returnOne}
-                                          onKey={undefined}
                                           onDoubleClickScript={undefined}
                                           childFilters={returnEmptyFilter}
                                           childFiltersByRanges={returnEmptyFilter}
@@ -830,7 +829,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<FieldViewProps>
                         ref={this._sidebarRef}
                         {...this._props}
                         fieldKey={this.fieldKey}
-                        Document={this.Document}
+                        Doc={this.Document}
                         layoutDoc={this.layoutDoc}
                         dataDoc={this.dataDoc}
                         usePanelWidth
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 5484179be..78ddafa88 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -535,7 +535,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                 <SidebarAnnos
                     ref={this._sidebarRef}
                     {...this._props}
-                    Document={this.Document}
+                    Doc={this.Document}
                     layoutDoc={this.layoutDoc}
                     dataDoc={this.dataDoc}
                     setHeight={emptyFunction}
@@ -606,6 +606,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                         pdfBox={this}
                         sidebarAddDoc={this.sidebarAddDocument}
                         addDocTab={this.sidebarAddDocTab}
+                        Doc={this.Document}
                         layoutDoc={this.layoutDoc}
                         dataDoc={this.dataDoc}
                         pdf={this._pdf}
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 6289470b6..999f9c1cd 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -301,7 +301,6 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
                 <div className="videoBox-viewer">
                     <div style={{ position: 'relative', height: this.videoPanelHeight() }}>
                         <CollectionFreeFormView
-                            // eslint-disable-next-line react/jsx-props-no-spreading
                             {...this._props}
                             setContentViewBox={emptyFunction}
                             NativeWidth={returnZero}
@@ -329,7 +328,6 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
                     <div style={{ background: SettingsManager.userColor, position: 'relative', height: this.formattedPanelHeight() }}>
                         {!(this.dataDoc[this.fieldKey + '_dictation'] instanceof Doc) ? null : (
                             <FormattedTextBox
-                                // eslint-disable-next-line react/jsx-props-no-spreading
                                 {...this._props}
                                 Document={DocCast(this.dataDoc[this.fieldKey + '_dictation'])}
                                 fieldKey="text"
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index e7a10cc29..4b3f96bcf 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1220,7 +1220,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                         {...this._props}
                         whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
                         fieldKey={this.fieldKey + '_' + this._urlHash}
-                        Document={this.Document}
+                        Doc={this.Document}
                         layoutDoc={this.layoutDoc}
                         dataDoc={this.dataDoc}
                         setHeight={emptyFunction}
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index aa2829aaf..bb0efa917 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -162,7 +162,7 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi
                     this._expanded = !this._props.editable ? false : !this._expanded;
                 })}>
                 <SchemaTableCell
-                    Document={this._dashDoc}
+                    Doc={this._dashDoc}
                     col={0}
                     deselectCell={emptyFunction}
                     selectCell={() => (this._expanded ? true : undefined)}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 9078648e9..925109bfb 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -78,14 +78,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     public static LayoutString(fieldStr: string) {
         return FieldView.LayoutString(FormattedTextBox, fieldStr);
     }
-    public static MakeConfig(rules?: RichTextRules, props?: FormattedTextBoxProps) {
+    public static MakeConfig(rules?: RichTextRules, textBox?: FormattedTextBox) {
         return {
             schema,
             plugins: [
                 inputRules(rules?.inpRules ?? { rules: [] }),
-                ...(props ? [FormattedTextBox.richTextMenuPlugin(props)] : []),
+                ...(textBox?._props ? [FormattedTextBox.richTextMenuPlugin(textBox._props)] : []),
                 history(),
-                keymap(buildKeymap(schema, props ?? {})),
+                keymap(buildKeymap(schema, textBox)),
                 keymap(baseKeymap),
                 new Plugin({ props: { attributes: { class: 'ProseMirror-example-setup-style' } } }),
                 new Plugin({ view: () => new FormattedTextBoxComment() }),
@@ -153,7 +153,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     }
 
     // eslint-disable-next-line no-return-assign
-    @computed get config()                     { return FormattedTextBox.MakeConfig(this._rules = new RichTextRules(this.Document, this), this._props); } // prettier-ignore
+    @computed get config()                     { return FormattedTextBox.MakeConfig(this._rules = new RichTextRules(this.Document, this), this); } // prettier-ignore
     @computed get _recordingDictation()        { return this.dataDoc?.mediaState === mediaState.Recording; } // prettier-ignore
     @computed get SidebarShown()               {  return !!(this._showSidebar || this.layoutDoc._layout_showSidebar);  } // prettier-ignore
     @computed get allSidebarDocs()             { return DocListCast(this.dataDoc[this.sidebarKey]); } // prettier-ignore
@@ -1954,7 +1954,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                 <SidebarAnnos
                     ref={this._sidebarRef}
                     {...this._props}
-                    Document={this.Document}
+                    Doc={this.Document}
                     layoutDoc={this.layoutDoc}
                     dataDoc={this.dataDoc}
                     usePanelWidth
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index 3c84e5a10..a75efd77b 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -1,8 +1,8 @@
 import { chainCommands, deleteSelection, exitCode, joinBackward, joinDown, joinUp, lift, newlineInCode, selectNodeBackward, setBlockType, splitBlockKeepMarks, toggleMark, wrapIn } from 'prosemirror-commands';
 import { redo, undo } from 'prosemirror-history';
-import { Schema } from 'prosemirror-model';
+import { MarkType, Node, ResolvedPos, Schema } from 'prosemirror-model';
 import { liftListItem, sinkListItem, splitListItem, wrapInList } from 'prosemirror-schema-list';
-import { EditorState, NodeSelection, TextSelection, Transaction } from 'prosemirror-state';
+import { Command, EditorState, NodeSelection, TextSelection, Transaction } from 'prosemirror-state';
 import { liftTarget } from 'prosemirror-transform';
 import { EditorView } from 'prosemirror-view';
 import { ClientUtils } from '../../../../ClientUtils';
@@ -13,17 +13,18 @@ import { Docs } from '../../../documents/Documents';
 import { RTFMarkup } from '../../../util/RTFMarkup';
 import { DocumentView } from '../DocumentView';
 import { OpenWhere } from '../OpenWhere';
+import { FormattedTextBox } from './FormattedTextBox';
 
 const mac = typeof navigator !== 'undefined' ? /Mac/.test(navigator.platform) : false;
 
-export type KeyMap = { [key: string]: any };
+export type KeyMap = { [key: string]: Command };
 
 export const updateBullets = (tx2: Transaction, schema: Schema, assignedMapStyle?: string, from?: number, to?: number) => {
     let mapStyle = assignedMapStyle;
-    tx2.doc.descendants((node: any, offset: any /* , index: any */) => {
+    tx2.doc.descendants((node: Node, offset: number /* , index: any */) => {
         if ((from === undefined || to === undefined || (from <= offset + node.nodeSize && to >= offset)) && (node.type === schema.nodes.ordered_list || node.type === schema.nodes.list_item)) {
-            const { path } = tx2.doc.resolve(offset) as any;
-            let depth = Array.from(path).reduce((p: number, c: any) => p + (c.type === schema.nodes.ordered_list ? 1 : 0), 0);
+            const { path } = tx2.doc.resolve(offset) as unknown as { path: (number | Node)[] }; // bcz: can't access path .. need to FIX
+            let depth = Array.from(path).reduce((p: number, c: number | Node) => p + ((c as Node).type === schema.nodes.ordered_list ? 1 : 0), 0);
             if (node.type === schema.nodes.ordered_list) {
                 if (depth === 0 && !assignedMapStyle) mapStyle = node.attrs.mapStyle;
                 depth++;
@@ -34,25 +35,25 @@ export const updateBullets = (tx2: Transaction, schema: Schema, assignedMapStyle
     return tx2;
 };
 
-export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMap {
-    const keys: { [key: string]: any } = {};
+export function buildKeymap<S extends Schema<string>>(schema: S, tbox?: FormattedTextBox): KeyMap {
+    const keys: { [key: string]: Command } = {};
 
-    function bind(key: string, cmd: any) {
+    function bind(key: string, cmd: Command) {
         keys[key] = cmd;
     }
 
     function onKey(): boolean | undefined {
         // bcz: this is pretty hacky -- prosemirror doesn't send us the keyboard event, but the 'event' variable is in scope.. so we access it anyway
-        // eslint-disable-next-line no-restricted-globals
-        return props.onKey?.(event, props);
+        return tbox?._props.onKey?.(event, tbox);
     }
 
-    const canEdit = (state: any) => {
-        const permissions = GetEffectiveAcl(props.TemplateDataDocument ?? props.Document[DocData]);
+    const canEdit = (state: EditorState) => {
+        if (!tbox) return true;
+        const permissions = GetEffectiveAcl(tbox._props.TemplateDataDocument ?? tbox.Document[DocData]);
         switch (permissions) {
             case AclAugment:
                 {
-                    const prevNode = state.selection.$cursor.nodeBefore;
+                    const prevNode = (state.selection as any).$cursor.nodeBefore;
                     const prevUser = !prevNode ? ClientUtils.CurrentUserEmail() : prevNode.marks.lastElement()?.attrs.userid;
                     if (prevUser !== ClientUtils.CurrentUserEmail()) {
                         return false;
@@ -64,7 +65,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMa
         return true;
     };
 
-    const toggleEditableMark = (mark: any) => (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && toggleMark(mark)(state, dispatch);
+    const toggleEditableMark = (mark: MarkType) => (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => canEdit(state) && toggleMark(mark)(state, dispatch);
 
     // History commands
     bind('Mod-z', undo);
@@ -84,13 +85,13 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMa
     bind('Mod-U', toggleEditableMark(schema.marks.underline));
 
     // Commands for lists
-    bind('Ctrl-i', (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && wrapInList(schema.nodes.ordered_list)(state as any, dispatch as any));
+    bind('Ctrl-i', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => canEdit(state) && wrapInList(schema.nodes.ordered_list)(state, dispatch));
 
     bind('Ctrl-Tab', () => onKey() || true);
     bind('Alt-Tab', () => onKey() || true);
     bind('Meta-Tab', () => onKey() || true);
     bind('Meta-Enter', () => onKey() || true);
-    bind('Tab', (state: EditorState, dispatch: (tx: Transaction) => void) => {
+    bind('Tab', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => {
         if (onKey()) return true;
         if (!canEdit(state)) return true;
         const ref = state.selection;
@@ -101,13 +102,13 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMa
                 const tx3 = updateBullets(tx2, schema);
                 marks && tx3.ensureMarks([...marks]);
                 marks && tx3.setStoredMarks([...marks]);
-                dispatch(tx3);
+                dispatch?.(tx3);
             })
         ) {
             // couldn't sink into an existing list, so wrap in a new one
             const newstate = state.applyTransaction(state.tr.setSelection(TextSelection.create(state.doc, range!.start, range!.end)));
             if (
-                !wrapInList(schema.nodes.ordered_list)(newstate.state as any, (tx2: Transaction) => {
+                !wrapInList(schema.nodes.ordered_list)(newstate.state, (tx2: Transaction) => {
                     const tx25 = updateBullets(tx2, schema);
                     const olNode = tx25.doc.nodeAt(range!.start)!;
                     const tx3 = tx25.setNodeMarkup(range!.start, olNode.type, olNode.attrs, marks);
@@ -115,16 +116,16 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMa
                     marks && tx3.ensureMarks([...marks]);
                     marks && tx3.setStoredMarks([...marks]);
                     const tx4 = tx3.setSelection(TextSelection.near(tx3.doc.resolve(state.selection.to + 2)));
-                    dispatch(tx4);
+                    dispatch?.(tx4);
                 })
             ) {
                 console.log('bullet promote fail');
             }
         }
-        return undefined;
+        return false;
     });
 
-    bind('Shift-Tab', (state: EditorState, dispatch: (tx: Transaction) => void) => {
+    bind('Shift-Tab', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => {
         if (onKey()) return true;
         if (!canEdit(state)) return true;
         const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
@@ -134,72 +135,83 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMa
                 const tx3 = updateBullets(tx2, schema);
                 marks && tx3.ensureMarks([...marks]);
                 marks && tx3.setStoredMarks([...marks]);
-                dispatch(tx3);
+                dispatch?.(tx3);
             })
         ) {
             console.log('bullet demote fail');
         }
-        return undefined;
+        return false;
     });
 
     // Command to create a new Tab with a PDF of all the command shortcuts
-    bind('Mod-/', () => {
+    // eslint-disable-next-line @typescript-eslint/no-unused-vars
+    bind('Mod-/', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => {
         const newDoc = Docs.Create.PdfDocument(ClientUtils.prepend('/assets/cheat-sheet.pdf'), { _width: 300, _height: 300 });
-        props.addDocTab(newDoc, OpenWhere.addRight);
+        tbox?._props.addDocTab(newDoc, OpenWhere.addRight);
+        return false;
     });
 
     // Commands to modify BlockType
-    bind('Ctrl->', (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state && wrapIn(schema.nodes.blockquote)(state as any, dispatch as any)));
-    bind('Alt-\\', (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && setBlockType(schema.nodes.paragraph)(state as any, dispatch as any));
-    bind('Shift-Ctrl-\\', (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && setBlockType(schema.nodes.code_block)(state as any, dispatch as any));
+    bind('Ctrl->', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => canEdit(state) && wrapIn(schema.nodes.blockquote)(state, dispatch));
+    bind('Alt-\\', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => canEdit(state) && setBlockType(schema.nodes.paragraph)(state, dispatch));
+    bind('Shift-Ctrl-\\', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => canEdit(state) && setBlockType(schema.nodes.code_block)(state, dispatch));
 
-    bind('Ctrl-m', (state: EditorState, dispatch: (tx: Transaction) => void) => {
+    bind('Ctrl-m', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => {
         if (canEdit(state)) {
             const tr = state.tr.replaceSelectionWith(schema.nodes.equation.create({ fieldKey: 'math' + Utils.GenerateGuid() }));
-            dispatch(tr.setSelection(new NodeSelection(tr.doc.resolve(tr.selection.$from.pos - 1))));
+            dispatch?.(tr.setSelection(new NodeSelection(tr.doc.resolve(tr.selection.$from.pos - 1))));
+            return true;
         }
+        return false;
     });
 
     for (let i = 1; i <= 6; i++) {
-        bind('Shift-Ctrl-' + i, (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && setBlockType(schema.nodes.heading, { level: i })(state as any, dispatch as any));
+        bind('Shift-Ctrl-' + i, (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => canEdit(state) && setBlockType(schema.nodes.heading, { level: i })(state, dispatch));
     }
 
     // Command to create a horizontal break line
     const hr = schema.nodes.horizontal_rule;
-    bind('Mod-_', (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && dispatch(state.tr.replaceSelectionWith(hr.create()).scrollIntoView()));
+    bind('Mod-_', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => {
+        if (canEdit(state)) {
+            dispatch?.(state.tr.replaceSelectionWith(hr.create()).scrollIntoView());
+            return true;
+        }
+        return false;
+    });
 
     // Command to unselect all
-    bind('Escape', (state: EditorState, dispatch: (tx: Transaction) => void) => {
-        dispatch(state.tr.setSelection(TextSelection.create(state.doc, state.selection.from, state.selection.from)));
-        (document.activeElement as any).blur?.();
+    bind('Escape', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => {
+        dispatch?.(state.tr.setSelection(TextSelection.create(state.doc, state.selection.from, state.selection.from)));
+        (document.activeElement as HTMLElement)?.blur?.();
         DocumentView.DeselectAll();
+        return true;
     });
 
     bind('Alt-Enter', () => onKey() || true);
     bind('Ctrl-Enter', () => onKey() || true);
-    bind('Cmd-a', (state: EditorState, dispatch: (tx: Transaction) => void) => {
-        dispatch(state.tr.setSelection(new TextSelection(state.doc.resolve(1), state.doc.resolve(state.doc.content.size - 1))));
+    bind('Cmd-a', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => {
+        dispatch?.(state.tr.setSelection(new TextSelection(state.doc.resolve(1), state.doc.resolve(state.doc.content.size - 1))));
         return true;
     });
     bind('Cmd-?', () => {
         RTFMarkup.Instance.setOpen(true);
         return true;
     });
-    bind('Cmd-e', (state: EditorState, dispatch: (tx: Transaction) => void) => {
+    bind('Cmd-e', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => {
         if (!state.selection.empty) {
             const mark = state.schema.marks.summarizeInclusive.create();
             const tr = state.tr.addMark(state.selection.$from.pos, state.selection.$to.pos, mark);
             const content = tr.selection.content();
-            tr.selection.replaceWith(tr, schema.nodes.summary.create({ visibility: false, text: content, textslice: content.toJSON() }));
-            dispatch(tr);
+            tr.selection.replaceWith(tr, schema.nodes.summary.create({ visibility: false, text: content, textslice: content.toJSON() }, undefined, state.selection.$anchor.marks() ?? []));
+            dispatch?.(tr);
         }
         return true;
     });
-    bind('Cmd-]', (state: EditorState, dispatch: (tx: Transaction) => void) => {
-        const resolved = state.doc.resolve(state.selection.from) as any;
+    bind('Cmd-]', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => {
+        const resolved = state.doc.resolve(state.selection.from) as ResolvedPos & { path: (Node | number)[] }; // bcz: this is bad. path is not publically visible
         const { tr } = state;
         if (resolved?.parent.type.name === 'paragraph') {
-            tr.setNodeMarkup(resolved.path[resolved.path.length - 4], schema.nodes.paragraph, { ...resolved.parent.attrs, align: 'right' }, resolved.parent.marks);
+            tr.setNodeMarkup(resolved.path[resolved.path.length - 4] as number, schema.nodes.paragraph, { ...resolved.parent.attrs, align: 'right' }, resolved.parent.marks);
         } else {
             const node = resolved.nodeAfter;
             const sm = state.storedMarks || undefined;
@@ -207,14 +219,14 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMa
                 tr.replaceRangeWith(state.selection.from, state.selection.from, schema.nodes.paragraph.create({ align: 'right' })).setStoredMarks([...node.marks, ...(sm || [])]);
             }
         }
-        dispatch(tr);
+        dispatch?.(tr);
         return true;
     });
-    bind('Cmd-\\', (state: EditorState, dispatch: (tx: Transaction) => void) => {
-        const resolved = state.doc.resolve(state.selection.from) as any;
+    bind('Cmd-\\', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => {
+        const resolved = state.doc.resolve(state.selection.from) as ResolvedPos & { path: (Node | number)[] }; // bcz: this is bad. path is not publically visible
         const { tr } = state;
         if (resolved?.parent.type.name === 'paragraph') {
-            tr.setNodeMarkup(resolved.path[resolved.path.length - 4], schema.nodes.paragraph, { ...resolved.parent.attrs, align: 'center' }, resolved.parent.marks);
+            tr.setNodeMarkup(resolved.path[resolved.path.length - 4] as number, schema.nodes.paragraph, { ...resolved.parent.attrs, align: 'center' }, resolved.parent.marks);
         } else {
             const node = resolved.nodeAfter;
             const sm = state.storedMarks || undefined;
@@ -222,14 +234,14 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMa
                 tr.replaceRangeWith(state.selection.from, state.selection.from, schema.nodes.paragraph.create({ align: 'center' })).setStoredMarks([...node.marks, ...(sm || [])]);
             }
         }
-        dispatch(tr);
+        dispatch?.(tr);
         return true;
     });
-    bind('Cmd-[', (state: EditorState, dispatch: (tx: Transaction) => void) => {
-        const resolved = state.doc.resolve(state.selection.from) as any;
+    bind('Cmd-[', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => {
+        const resolved = state.doc.resolve(state.selection.from) as ResolvedPos & { path: (Node | number)[] }; // bcz: this is bad. path is not publically visible
         const { tr } = state;
         if (resolved?.parent.type.name === 'paragraph') {
-            tr.setNodeMarkup(resolved.path[resolved.path.length - 4], schema.nodes.paragraph, { ...resolved.parent.attrs, align: 'left' }, resolved.parent.marks);
+            tr.setNodeMarkup(resolved.path[resolved.path.length - 4] as number, schema.nodes.paragraph, { ...resolved.parent.attrs, align: 'left' }, resolved.parent.marks);
         } else {
             const node = resolved.nodeAfter;
             const sm = state.storedMarks || undefined;
@@ -237,16 +249,16 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMa
                 tr.replaceRangeWith(state.selection.from, state.selection.from, schema.nodes.paragraph.create({ align: 'left' })).setStoredMarks([...node.marks, ...(sm || [])]);
             }
         }
-        dispatch(tr);
+        dispatch?.(tr);
         return true;
     });
 
-    bind('Cmd-f', (state: EditorState, dispatch: (tx: Transaction) => void) => {
+    bind('Cmd-f', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => {
         const content = state.tr.selection.empty ? undefined : state.tr.selection.content().content.textBetween(0, state.tr.selection.content().size + 1);
         const newNode = schema.nodes.footnote.create({}, content ? state.schema.text(content) : undefined);
         const { tr } = state;
         tr.replaceSelectionWith(newNode); // replace insertion with a footnote.
-        dispatch(
+        dispatch?.(
             tr.setSelection(
                 new NodeSelection( // select the footnote node to open its display
                     tr.doc.resolve(
@@ -259,25 +271,25 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMa
         return true;
     });
 
-    bind('Ctrl-a', (state: EditorState, dispatch: (tx: Transaction) => void) => {
-        dispatch(state.tr.setSelection(new TextSelection(state.doc.resolve(1), state.doc.resolve(state.doc.content.size - 1))));
+    bind('Ctrl-a', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => {
+        dispatch?.(state.tr.setSelection(new TextSelection(state.doc.resolve(1), state.doc.resolve(state.doc.content.size - 1))));
         return true;
     });
 
     // backspace = chainCommands(deleteSelection, joinBackward, selectNodeBackward);
-    const backspace = (state: EditorState, dispatch: (tx: Transaction) => void, view: EditorView) => {
+    const backspace = (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined, view?: EditorView) => {
         if (onKey()) return true;
         if (!canEdit(state)) return true;
 
         if (
             !deleteSelection(state, (tx: Transaction) => {
-                dispatch(updateBullets(tx, schema));
+                dispatch?.(updateBullets(tx, schema));
             })
         ) {
             if (
                 !joinBackward(state, (tx: Transaction) => {
-                    dispatch(updateBullets(tx, schema));
-                    if (view.state.selection.$anchor.node(-1)?.type === schema.nodes.list_item) {
+                    dispatch?.(updateBullets(tx, schema));
+                    if (view?.state.selection.$anchor.node(-1)?.type === schema.nodes.list_item) {
                         // gets rid of an extra paragraph when joining two list items together.
                         joinBackward(view.state, (tx2: Transaction) => view.dispatch(tx2));
                     }
@@ -285,7 +297,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMa
             ) {
                 if (
                     !selectNodeBackward(state, (tx: Transaction) => {
-                        dispatch(updateBullets(tx, schema));
+                        dispatch?.(updateBullets(tx, schema));
                     })
                 ) {
                     return false;
@@ -299,7 +311,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMa
     // newlineInCode, createParagraphNear, liftEmptyBlock, splitBlock
     // command to break line
 
-    const enter = (state: EditorState, dispatch: (tx: Transaction) => void, view: EditorView, once = true) => {
+    const enter = (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined, view?: EditorView, once = true) => {
         if (onKey()) return true;
         if (!canEdit(state)) return true;
 
@@ -311,31 +323,31 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMa
             !state.selection.$from.node().content.size &&
             trange
         ) {
-            dispatch(state.tr.lift(trange, depth) as any);
+            dispatch?.(state.tr.lift(trange, depth));
             return true;
         }
 
         const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
-        if (!newlineInCode(state, dispatch as any)) {
-            const olNode = view.state.selection.$anchor.node(-2);
-            const liNode = view.state.selection.$anchor.node(-1);
+        if (!newlineInCode(state, dispatch)) {
+            const olNode = view?.state.selection.$anchor.node(-2);
+            const liNode = view?.state.selection.$anchor.node(-1);
             // prettier-ignore
             if (liNode?.type === schema.nodes.list_item && !liNode.textContent && 
-                olNode?.type === schema.nodes.ordered_list && once && view.state.selection.$from.depth === 3) 
+                olNode?.type === schema.nodes.ordered_list && once && view?.state.selection.$from.depth === 3) 
             {
                 // handles case of hitting enter at then end of a top-level empty list item - the result is to create a paragraph
-                for (let i = 0; i < 10 && view.state.selection.$from.depth > 1 && liftListItem(schema.nodes.list_item)(view.state, view.dispatch); i++);
+                for (let i = 0; i < 10 && view?.state.selection.$from.depth > 1 && liftListItem(schema.nodes.list_item)(view.state, view.dispatch); i++);
             } else if (
-                !splitListItem(schema.nodes.list_item)(state as any, (tx2: Transaction) => {
+                !splitListItem(schema.nodes.list_item)(state, (tx2: Transaction) => {
                     const tx3 = updateBullets(tx2, schema);
                     marks && tx3.ensureMarks([...marks]);
                     marks && tx3.setStoredMarks([...marks]);
-                    dispatch(tx3);
+                    dispatch?.(tx3);
                     // removes an extra paragraph created when selecting text across two list items or splitting an empty list item
-                    !once && view.dispatch(view.state.tr.deleteRange(view.state.selection.from - 5, view.state.selection.from - 2));
+                    !once && view?.dispatch(view?.state.tr.deleteRange(view.state.selection.from - 5, view.state.selection.from - 2));
                 })
             ) {
-                if (once && view.state.selection.$from.node(-2)?.type === schema.nodes.ordered_list && view.state.selection.$from.node(-1)?.type === schema.nodes.list_item && view.state.selection.$from.node(-1)?.textContent === '') {
+                if (once && view?.state.selection.$from.node(-2)?.type === schema.nodes.ordered_list && view?.state.selection.$from.node(-1)?.type === schema.nodes.list_item && view.state.selection.$from.node(-1)?.textContent === '') {
                     // handles case of hitting enter on an empty list item which needs to create a second empty paragraph, then split it by calling enter() again
                     view.dispatch(view.state.tr.insert(view.state.selection.from, schema.nodes.paragraph.create({})));
                     enter(view.state, view.dispatch, view, false);
@@ -346,12 +358,12 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMa
                             const tonode = tx3.selection.$to.node();
                             if (tx3.selection.to && tx3.doc.nodeAt(tx3.selection.to - 1)) {
                                 const tx4 = tx3.setNodeMarkup(tx3.selection.to - 1, tonode.type, fromattrs, tonode.marks).setStoredMarks(marks || []);
-                                dispatch(tx4);
+                                dispatch?.(tx4);
                             }
 
-                            if (view.state.selection.$anchor.depth > 0 &&
-                                view.state.selection.$anchor.node(view.state.selection.$anchor.depth-1).type === schema.nodes.list_item &&
-                                view.state.selection.$anchor.nodeAfter?.type === schema.nodes.text && once) {
+                            if ((view?.state.selection.$anchor.depth ??0) > 0 &&
+                                view?.state.selection.$anchor.node(view.state.selection.$anchor.depth-1).type === schema.nodes.list_item &&
+                                view?.state.selection.$anchor.nodeAfter?.type === schema.nodes.text && once) {
                                 // if text is selected across list items, then we need to forcibly insert a new line since the splitBlock code joins the two list items.
                                 enter(view.state, dispatch, view, false);
                             }
@@ -368,14 +380,14 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMa
 
     // Command to create a blank space
     bind('Space', () => {
-        const editDoc = props.TemplateDataDocument ?? props.Document[DocData];
+        const editDoc = tbox?._props.TemplateDataDocument ?? tbox?.Document[DocData];
         if (editDoc && ![AclAdmin, AclAugment, AclEdit].includes(GetEffectiveAcl(editDoc))) return true;
         return false;
     });
 
-    bind('Alt-ArrowUp', (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && joinUp(state, dispatch as any));
-    bind('Alt-ArrowDown', (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && joinDown(state, dispatch as any));
-    bind('Mod-BracketLeft', (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && lift(state, dispatch as any));
+    bind('Alt-ArrowUp', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => canEdit(state) && joinUp(state, dispatch));
+    bind('Alt-ArrowDown', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => canEdit(state) && joinDown(state, dispatch));
+    bind('Mod-BracketLeft', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => canEdit(state) && lift(state, dispatch));
 
     const cmd = chainCommands(exitCode, (state, dispatch) => {
         if (dispatch) {
diff --git a/src/client/views/nodes/formattedText/SummaryView.tsx b/src/client/views/nodes/formattedText/SummaryView.tsx
index 238267f6e..6dea891a0 100644
--- a/src/client/views/nodes/formattedText/SummaryView.tsx
+++ b/src/client/views/nodes/formattedText/SummaryView.tsx
@@ -1,12 +1,11 @@
 import { TextSelection } from 'prosemirror-state';
-import { Fragment, Node, Slice } from 'prosemirror-model';
+import { Attrs, Fragment, Node, Slice } from 'prosemirror-model';
 import * as ReactDOM from 'react-dom/client';
 import * as React from 'react';
+import { EditorView } from 'prosemirror-view';
 
-interface ISummaryView {}
 // currently nothing needs to be rendered for the internal view of a summary.
-// eslint-disable-next-line react/prefer-stateless-function
-export class SummaryViewInternal extends React.Component<ISummaryView> {
+export class SummaryViewInternal extends React.Component<object> {
     render() {
         return null;
     }
@@ -18,30 +17,30 @@ export class SummaryViewInternal extends React.Component<ISummaryView> {
 // method instead of changing prosemirror's text when the expand/elide buttons are clicked.
 export class SummaryView {
     dom: HTMLSpanElement; // container for label and value
-    root: any;
+    root: ReactDOM.Root;
 
-    constructor(node: any, view: any, getPos: any) {
+    constructor(node: Node, view: EditorView, getPos: () => number | undefined) {
         this.dom = document.createElement('span');
         this.dom.className = this.className(node.attrs.visibility);
-        this.dom.onpointerdown = (e: any) => {
+        this.dom.onpointerdown = (e: PointerEvent) => {
             this.onPointerDown(e, node, view, getPos);
         };
-        this.dom.onkeypress = function (e: any) {
+        this.dom.onkeypress = function (e: KeyboardEvent) {
             e.stopPropagation();
         };
-        this.dom.onkeydown = function (e: any) {
+        this.dom.onkeydown = function (e: KeyboardEvent) {
             e.stopPropagation();
         };
-        this.dom.onkeyup = function (e: any) {
+        this.dom.onkeyup = function (e: KeyboardEvent) {
             e.stopPropagation();
         };
-        this.dom.onmousedown = function (e: any) {
+        this.dom.onmousedown = function (e: MouseEvent) {
             e.stopPropagation();
         };
 
         const js = node.toJSON;
-        node.toJSON = function (...args: any[]) {
-            return js.apply(this, args);
+        node.toJSON = function (...args: unknown[]) {
+            return js.apply(this, args as []);
         };
 
         this.root = ReactDOM.createRoot(this.dom);
@@ -54,7 +53,7 @@ export class SummaryView {
     }
     selectNode() {}
 
-    updateSummarizedText(start: any, view: any) {
+    updateSummarizedText(start: number, view: EditorView) {
         const mtype = view.state.schema.marks.summarize;
         const mtypeInc = view.state.schema.marks.summarizeInclusive;
         let endPos = start;
@@ -65,7 +64,7 @@ export class SummaryView {
             // eslint-disable-next-line no-loop-func
             view.state.doc.nodesBetween(start, i, (node: Node /* , pos: number, parent: Node, index: number */) => {
                 if (node.isLeaf && !visited.has(node) && !skip) {
-                    if (node.marks.find((m: any) => m.type === mtype || m.type === mtypeInc)) {
+                    if (node.marks.find(m => m.type === mtype || m.type === mtypeInc)) {
                         visited.add(node);
                         endPos = i + node.nodeSize - 1;
                     } else skip = true;
@@ -75,21 +74,18 @@ export class SummaryView {
         return TextSelection.create(view.state.doc, start, endPos);
     }
 
-    onPointerDown = (e: any, node: any, view: any, getPos: any) => {
+    onPointerDown = (e: PointerEvent, node: Node, view: EditorView, getPos: () => number | undefined) => {
         const visible = !node.attrs.visibility;
-        const attrs = { ...node.attrs, visibility: visible };
-        let textSelection = TextSelection.create(view.state.doc, getPos() + 1);
-        if (!visible) {
-            // update summarized text and save in attrs
-            textSelection = this.updateSummarizedText(getPos() + 1, view);
-            attrs.text = textSelection.content();
-            attrs.textslice = attrs.text.toJSON();
-        }
+        const textSelection = visible //
+            ? TextSelection.create(view.state.doc, (getPos() ?? 0) + 1)
+            : this.updateSummarizedText((getPos() ?? 0) + 1, view); // update summarized text and save in attrs
+        const text = textSelection.content();
+        const attrs = { ...node.attrs, visibility: visible, ...(!visible ? { text, textslice: text.toJSON() } : {}) } as Attrs;
         view.dispatch(
             view.state.tr
                 .setSelection(textSelection) // select the current summarized text (or where it will be if its collapsed)
                 .replaceSelection(!visible ? new Slice(Fragment.fromArray([]), 0, 0) : node.attrs.text) // collapse/expand it
-                .setNodeMarkup(getPos(), undefined, attrs)
+                .setNodeMarkup(getPos() ?? 0, undefined, attrs)
         ); // update the attrs
         e.preventDefault();
         e.stopPropagation();
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 2142adac8..73c2f5eb8 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -35,7 +35,7 @@ export * from 'pdfjs-dist/build/pdf.mjs';
 
 interface IViewerProps extends FieldViewProps {
     pdfBox: PDFBox;
-    Document: Doc;
+    Doc: Doc;
     dataDoc: Doc;
     layoutDoc: Doc;
     fieldKey: string;
@@ -113,8 +113,8 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
             () => this._props.layoutDoc._layout_autoHeight,
             layoutAutoHeight => {
                 if (layoutAutoHeight) {
-                    this._props.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._props.layoutDoc._nativeHeight = NumCast(this._props.Doc[this._props.fieldKey + '_nativeHeight']);
+                    this._props.setHeight?.(NumCast(this._props.Doc[this._props.fieldKey + '_nativeHeight']) * (this._props.NativeDimScaling?.() || 1));
                 }
             }
         );
@@ -125,7 +125,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
             { fireImmediately: true }
         );
         this._disposers.curPage = reaction(
-            () => Cast(this._props.Document._layout_curPage, 'number', null),
+            () => Cast(this._props.Doc._layout_curPage, 'number', null),
             page => page !== undefined && page !== this._pdfViewer?.currentPageNumber && this.gotoPage(page),
             { fireImmediately: true }
         );
@@ -181,7 +181,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
     scrollFocus = (doc: Doc, scrollTop: number, options: FocusViewOptions) => {
         const mainCont = this._mainCont.current;
         let focusSpeed: Opt<number>;
-        if (doc !== this._props.Document && mainCont) {
+        if (doc !== this._props.Doc && mainCont) {
             const windowHeight = this._props.PanelHeight() / (this._props.NativeDimScaling?.() || 1);
             const scrollTo = ClientUtils.scrollIntoView(scrollTop, doc[Height](), NumCast(this._props.layoutDoc._layout_scrollTop), windowHeight, windowHeight * 0.1, this._scrollHeight);
             if (scrollTo !== undefined && scrollTo !== this._props.layoutDoc._layout_scrollTop) {
@@ -216,11 +216,11 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
             { fireImmediately: true }
         );
         this._disposers.scroll = reaction(
-            () => Math.abs(NumCast(this._props.Document._layout_scrollTop)),
+            () => Math.abs(NumCast(this._props.Doc._layout_scrollTop)),
             pos => {
                 if (!this._ignoreScroll) {
                     this._showWaiting && this.setupPdfJsViewer();
-                    const viewTrans = quickScroll?.loc ?? StrCast(this._props.Document._viewTransition);
+                    const viewTrans = quickScroll?.loc ?? StrCast(this._props.Doc._viewTransition);
                     const durationMiliStr = viewTrans.match(/([0-9]*)ms/);
                     const durationSecStr = viewTrans.match(/([0-9.]*)s/);
                     const duration = durationMiliStr ? Number(durationMiliStr[1]) : durationSecStr ? Number(durationSecStr[1]) * 1000 : 0;
@@ -371,9 +371,9 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
         // if alt+left click, drag and annotate
         this._downX = e.clientX;
         this._downY = e.clientY;
-        if ((this._props.Document._freeform_scale || 1) !== 1) return;
+        if ((this._props.Doc._freeform_scale || 1) !== 1) return;
         if ((e.button !== 0 || e.altKey) && this._props.isContentActive()) {
-            this._setPreviewCursor?.(e.clientX, e.clientY, true, false, this._props.Document);
+            this._setPreviewCursor?.(e.clientX, e.clientY, true, false, this._props.Doc);
         }
         if (!e.altKey && e.button === 0 && this._props.isContentActive() && Doc.ActiveTool !== InkTool.Ink) {
             this._props.select(false);
@@ -433,7 +433,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
     addDrawingAnnotation = (drawing: Doc) => {
         // drawing.x = this._props.pdfBox.ScreenToLocalBoxXf().TranslateX
         // const scaleX = this._mainCont.current.offsetWidth / boundingRect.width;
-        drawing.y = NumCast(drawing.y) + NumCast(this._props.Document.layout_scrollTop);
+        drawing.y = NumCast(drawing.y) + NumCast(this._props.Doc.layout_scrollTop);
         this._props.addDocument?.(drawing);
     };
 
@@ -478,7 +478,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
     onClick = (e: React.MouseEvent) => {
         this._scrollStopper?.();
         if (this._setPreviewCursor && e.button === 0 && Math.abs(e.clientX - this._downX) < ClientUtils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < ClientUtils.DRAG_THRESHOLD) {
-            this._setPreviewCursor(e.clientX, e.clientY, false, false, this._props.Document);
+            this._setPreviewCursor(e.clientX, e.clientY, false, false, this._props.Doc);
         }
         // e.stopPropagation();  // bcz: not sure why this was here.  We need to allow the DocumentView to get clicks to process doubleClicks
     };
@@ -506,7 +506,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
     @computed get annotationLayer() {
         const inlineAnnos = this.inlineTextAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).filter(anno => !anno.hidden);
         return (
-            <div className="pdfViewerDash-annotationLayer" style={{ height: Doc.NativeHeight(this._props.Document), transform: `scale(${NumCast(this._props.layoutDoc._freeform_scale, 1)})` }} ref={this._annotationLayer}>
+            <div className="pdfViewerDash-annotationLayer" style={{ height: Doc.NativeHeight(this._props.Doc), transform: `scale(${NumCast(this._props.layoutDoc._freeform_scale, 1)})` }} ref={this._annotationLayer}>
                 {inlineAnnos.map(anno => (
                     <Annotation {...this._props} fieldKey={this._props.fieldKey + '_annotations'} pointerEvents={this.pointerEvents} containerDataDoc={this._props.dataDoc} annoDoc={anno} key={`${anno[Id]}-annotation`} />
                 ))}
@@ -597,7 +597,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
                     onClick={this.onClick}
                     style={{
                         overflowX: NumCast(this._props.layoutDoc._freeform_scale, 1) !== 1 ? 'scroll' : undefined,
-                        height: !this._props.Document._layout_fitWidth && window.screen.width > 600 ? Doc.NativeHeight(this._props.Document) : `100%`,
+                        height: !this._props.Doc._layout_fitWidth && window.screen.width > 600 ? Doc.NativeHeight(this._props.Doc) : `100%`,
                     }}>
                     {this.pdfViewerDiv}
                     {this.annotationLayer}
@@ -606,12 +606,12 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
                     {!this._mainCont.current || !this._annotationLayer.current || !this.props.pdfBox.DocumentView ? null : (
                         <MarqueeAnnotator
                             ref={this._marqueeref}
-                            Document={this._props.Document}
+                            Document={this._props.Doc}
                             getPageFromScroll={this.getPageFromScroll}
                             anchorMenuClick={this._props.anchorMenuClick}
                             scrollTop={0}
                             annotationLayerScaling={() => Pdfjs.PixelsPerInch.PDF_TO_CSS_UNITS}
-                            annotationLayerScrollTop={NumCast(this._props.Document._layout_scrollTop)}
+                            annotationLayerScrollTop={NumCast(this._props.Doc._layout_scrollTop)}
                             addDocument={this.addDocumentWrapper}
                             docView={this.props.pdfBox.DocumentView}
                             screenTransform={this.screenToMarqueeXf}
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 8b7e77fba..1f6e80bd1 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -27,7 +27,7 @@ const MAX_ITERATIONS = 25;
 const ERROR = 0.03;
 
 export interface SearchBoxItemProps {
-    Document: Doc;
+    Doc: Doc;
     searchString: string;
     isLinkSearch: boolean;
     matchedKeys: string[];
@@ -64,7 +64,7 @@ export class SearchBoxItem extends ObservableReactComponent<SearchBoxItemProps>
     });
 
     componentWillUnmount(): void {
-        const doc = this._props.Document;
+        const doc = this._props.Doc;
         DocumentView.getFirstDocumentView(doc)?.ComponentView?.search?.('', undefined, true);
     }
 
@@ -79,23 +79,23 @@ export class SearchBoxItem extends ObservableReactComponent<SearchBoxItemProps>
 
     render() {
         // eslint-disable-next-line no-use-before-define
-        const formattedType = SearchBox.formatType(StrCast(this._props.Document.type), StrCast(this._props.Document.type_collection));
-        const { title } = this._props.Document;
+        const formattedType = SearchBox.formatType(StrCast(this._props.Doc.type), StrCast(this._props.Doc.type_collection));
+        const { title } = this._props.Doc;
 
         return (
             <Tooltip placement="right" title={<div className="dash-tooltip">{title as string}</div>}>
                 <div
                     onClick={
                         this._props.isLinkSearch
-                            ? () => this.makeLink(this._props.Document)
+                            ? () => this.makeLink(this._props.Doc)
                             : e => {
-                                  this.onResultClick(this._props.Document);
+                                  this.onResultClick(this._props.Doc);
                                   e.stopPropagation();
                               }
                     }
                     style={{
                         fontWeight: Doc.Links(this._props.linkFrom).find(
-                            link => Doc.AreProtosEqual(Doc.getOppositeAnchor(link, this._props.linkFrom!), this._props.Document) || Doc.AreProtosEqual(DocCast(Doc.getOppositeAnchor(link, this._props.linkFrom!)?.annotationOn), this._props.Document)
+                            link => Doc.AreProtosEqual(Doc.getOppositeAnchor(link, this._props.linkFrom!), this._props.Doc) || Doc.AreProtosEqual(DocCast(Doc.getOppositeAnchor(link, this._props.linkFrom!)?.annotationOn), this._props.Doc)
                         )
                             ? 'bold'
                             : '',
@@ -449,7 +449,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
                 resultsJSX.push(
                     <SearchBoxItem
                         key={Document[Id]}
-                        Document={Document}
+                        Doc={Document}
                         selectItem={action((doc: Doc) => {
                             this._selectedResult = doc;
                         })}
diff --git a/src/client/views/smartdraw/StickerPalette.tsx b/src/client/views/smartdraw/StickerPalette.tsx
index 080a05d42..2260d1f73 100644
--- a/src/client/views/smartdraw/StickerPalette.tsx
+++ b/src/client/views/smartdraw/StickerPalette.tsx
@@ -25,7 +25,7 @@ import { DrawingOptions, SmartDrawHandler } from './SmartDrawHandler';
 import './StickerPalette.scss';
 
 interface StickerPaletteProps {
-    Document: Doc;
+    Doc: Doc;
 }
 
 enum StickerPaletteMode {
@@ -132,7 +132,7 @@ export class StickerPalette extends ObservableReactComponent<StickerPaletteProps
         this._canInteract = true;
         this._opts = { text: '', complexity: 5, size: 200, autoColor: true, x: 0, y: 0 };
         this._gptRes = [];
-        this._props.Document.$data = undefined;
+        this._props.Doc.$data = undefined;
     });
 
     /**
@@ -142,8 +142,8 @@ export class StickerPalette extends ObservableReactComponent<StickerPaletteProps
     @undoBatch
     generateDrawings = action(() => {
         this._isLoading = true;
-        const prevDrawings = DocListCast(this._props.Document.$data);
-        this._props.Document.$data = undefined;
+        const prevDrawings = DocListCast(this._props.Doc.$data);
+        this._props.Doc.$data = undefined;
         SmartDrawHandler.Instance.AddDrawing = this.addDrawing;
         this._canInteract = false;
         Promise.all(
@@ -164,7 +164,7 @@ export class StickerPalette extends ObservableReactComponent<StickerPaletteProps
     addDrawing = (drawing: Doc, opts: DrawingOptions, gptRes: string) => {
         this._gptRes.push(gptRes);
         drawing.$freeform_fitContentsToBox = true;
-        Doc.AddDocToList(this._props.Document, 'data', drawing);
+        Doc.AddDocToList(this._props.Doc, 'data', drawing);
     };
 
     /**
@@ -173,8 +173,8 @@ export class StickerPalette extends ObservableReactComponent<StickerPaletteProps
      * presses the "save drawing" button.
      */
     saveDrawing = () => {
-        const cIndex = NumCast(this._props.Document.carousel_index);
-        const focusedDrawing = DocListCast(this._props.Document.data)[cIndex];
+        const cIndex = NumCast(this._props.Doc.carousel_index);
+        const focusedDrawing = DocListCast(this._props.Doc.data)[cIndex];
         focusedDrawing.$title = this._opts.text?.match(/^(.*?)~~~.*$/)?.[1] || this._opts.text;
         focusedDrawing.$ai_drawing_input = this._opts.text;
         focusedDrawing.$ai_drawing_complexity = this._opts.complexity;
@@ -313,7 +313,7 @@ export class StickerPalette extends ObservableReactComponent<StickerPaletteProps
         <>
             {this.renderCreateInput()}
             {this.renderCreateOptions()}
-            {this.renderDoc(this._props.Document, (r: DocumentView) => {
+            {this.renderDoc(this._props.Doc, (r: DocumentView) => {
                 this._docCarouselView = r;
             })}
             <div className="palette-buttons">
diff --git a/src/fields/RichTextUtils.ts b/src/fields/RichTextUtils.ts
index 42dd0d432..e16073ff4 100644
--- a/src/fields/RichTextUtils.ts
+++ b/src/fields/RichTextUtils.ts
@@ -20,6 +20,7 @@ import { Doc, Opt } from './Doc';
 import { Id } from './FieldSymbols';
 import { RichTextField } from './RichTextField';
 import { Cast, StrCast } from './Types';
+import { Upload } from '../server/SharedMediaTypes';
 
 export namespace RichTextUtils {
     const delimiter = '\n';
@@ -127,7 +128,7 @@ export namespace RichTextUtils {
                     return { baseUrl: embeddedObject.imageProperties!.contentUri! };
                 });
 
-                const uploads = await Networking.PostToServer('/googlePhotosMediaGet', { mediaItems });
+                const uploads = (await Networking.PostToServer('/googlePhotosMediaGet', { mediaItems })) as Upload.FileInformation[];
 
                 if (uploads.length !== mediaItems.length) {
                     throw new AssertionError({ expected: mediaItems.length, actual: uploads.length, message: 'Error with internally uploading inlineObjects!' });
diff --git a/src/fields/Types.ts b/src/fields/Types.ts
index 474882959..af9cb1180 100644
--- a/src/fields/Types.ts
+++ b/src/fields/Types.ts
@@ -60,16 +60,13 @@ export type CastCtor = ToConstructor<FieldType> | ListSpec<FieldType>;
 type WithoutList<T extends FieldType> = T extends List<infer R> ? (R extends RefField ? (R | Promise<R>)[] : R[]) : T;
 
 export function Cast<T extends CastCtor>(field: FieldResult, ctor: T): FieldResult<ToType<T>>;
-// eslint-disable-next-line no-redeclare
 export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal: WithoutList<WithoutRefField<ToType<T>>> | null): WithoutList<ToType<T>>;
-// eslint-disable-next-line no-redeclare
 export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal?: ToType<T> | null): FieldResult<ToType<T>> | undefined {
     if (field instanceof Promise) {
         return defaultVal === undefined ? (field.then(f => Cast(f, ctor) as any) as any) : defaultVal === null ? undefined : defaultVal;
     }
     if (field !== undefined && !(field instanceof Promise)) {
         if (typeof ctor === 'string') {
-            // eslint-disable-next-line valid-typeof
             if (typeof field === ctor) {
                 return field as ToType<T>;
             }
@@ -140,9 +137,7 @@ export function ImageCastWithSuffix(field: FieldResult, suffix: string, defaultV
 }
 
 export function FieldValue<T extends FieldType, U extends WithoutList<T>>(field: FieldResult<T>, defaultValue: U): WithoutList<T>;
-// eslint-disable-next-line no-redeclare
 export function FieldValue<T extends FieldType>(field: FieldResult<T>): Opt<T>;
-// eslint-disable-next-line no-redeclare
 export function FieldValue<T extends FieldType>(field: FieldResult<T>, defaultValue?: T): Opt<T> {
     return field instanceof Promise || field === undefined ? defaultValue : field;
 }
diff --git a/src/server/DashSession/DashSessionAgent.ts b/src/server/DashSession/DashSessionAgent.ts
index 891316b80..8688ec049 100644
--- a/src/server/DashSession/DashSessionAgent.ts
+++ b/src/server/DashSession/DashSessionAgent.ts
@@ -213,9 +213,9 @@ export class DashSessionAgent extends AppliedSessionAgent {
             // indicate success or failure
             mainLog(`${error === null ? green('successfully dispatched') : red('failed to dispatch')} ${zipName} to ${cyan(to)}`);
             error && mainLog(red(error.message));
-        } catch (error: any) {
+        } catch (error: unknown) {
             mainLog(red('unable to dispatch zipped backup...'));
-            mainLog(red(error.message));
+            mainLog(red((error as { message: string }).message));
         }
     }
 }
-- 
cgit v1.2.3-70-g09d2


From a3eab5f2b8eca6ff7a21ea9d043b90c953c35f03 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 26 Mar 2025 16:36:15 -0400
Subject: fix so that views use don't use the rootDocument, but rather the
 current rendering document.  got rid of resolvedDataDoc -- just use
 rootDocument[DocData]

---
 src/client/views/DocComponent.tsx                  | 44 +++++++++++-----------
 src/client/views/DocumentDecorations.tsx           |  2 +-
 .../views/collections/CollectionCarouselView.tsx   |  3 +-
 .../CollectionStackingViewFieldColumn.tsx          |  5 ++-
 src/client/views/collections/CollectionSubView.tsx | 12 +++---
 .../views/collections/CollectionTreeView.tsx       |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 .../collectionGrid/CollectionGridView.tsx          |  5 ++-
 .../CollectionMulticolumnView.tsx                  |  5 ++-
 .../CollectionMultirowView.tsx                     |  5 ++-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  2 +-
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  2 +-
 src/fields/Doc.ts                                  | 19 +++++-----
 14 files changed, 58 insertions(+), 52 deletions(-)

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

diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 3a868d1f9..e3d47317c 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -36,7 +36,7 @@ export function DocComponent<P extends DocComponentProps>() {
          * NOTE: it is very unlikely that you really want to use this method. Instead
          *       consider: Document, layoutDoc, dataDoc
          */
-        get _renderDoc() {
+        get Document() {
             return this._props.Document;
         }
 
@@ -46,8 +46,8 @@ export function DocComponent<P extends DocComponentProps>() {
          * necessarily the Doc being rendered in the current React component.
          * This Doc inherits from the dataDoc, and may or may not inherit (or be) the layoutDoc.
          */
-        get Document() {
-            return DocCast(this._renderDoc.rootDocument, this._renderDoc);
+        get rootDoc() {
+            return DocCast(this.Document.rootDocument, this.Document);
         }
         /**
          * This is the document being rendered by the React component. In the
@@ -56,14 +56,14 @@ export function DocComponent<P extends DocComponentProps>() {
          * This may or may not inherit from the data doc.
          */
         @computed get layoutDoc() {
-            return this._props.LayoutTemplateString ? this._renderDoc : Doc.Layout(this._renderDoc, this._props.LayoutTemplate?.());
+            return this._props.LayoutTemplateString ? this.Document : Doc.Layout(this.Document, this._props.LayoutTemplate?.());
         }
 
         /**
          * This is the unique data repository for a document that stores the intrinsic document data.
          */
         @computed get dataDoc() {
-            return this._renderDoc[DocData];
+            return this.Document[DocData];
         }
     }
     return Component;
@@ -92,10 +92,8 @@ export function ViewBoxBaseComponent<P extends FieldViewProps>() {
          * This is the doc that is being rendered. It will be either:
          *   1) the same as Document if the root of a regular or compound Doc is rendered
          *   2) the same as the layoutDoc if a component of a compound Doc is rendered.
-         * NOTE: it is very unlikely that you really want to use this method. Instead
-         *       consider: Document, layoutDoc, dataDoc
          */
-        get _renderDoc() {
+        get Document() {
             return this._props.Document;
         }
 
@@ -104,9 +102,12 @@ export function ViewBoxBaseComponent<P extends FieldViewProps>() {
          * this is the outermost Doc that represents the entire compound Doc.  It is not
          * necessarily the Doc being rendered in the current React component.
          * This Doc inherits from the dataDoc, and may or may not inherit (or be) the layoutDoc.
+         *
+         * NOTE: it is very unlikely that you really want to use this method. Instead
+         *       consider: Document, layoutDoc, dataDoc
          */
-        get Document() {
-            return DocCast(this._renderDoc.rootDocument, this._renderDoc);
+        get rootDoc() {
+            return DocCast(this.Document.rootDocument, this.Document);
         }
         /**
          * This is the document being rendered by the React component. In the
@@ -115,14 +116,14 @@ export function ViewBoxBaseComponent<P extends FieldViewProps>() {
          * This may or may not inherit from the data doc.
          */
         @computed get layoutDoc() {
-            return Doc.Layout(this._renderDoc);
+            return Doc.Layout(this.Document);
         }
 
         /**
          * This is the unique data repository for a dcoument that stores the intrinsic document data
          */
         @computed get dataDoc() {
-            return this._renderDoc.isTemplateForField || this._renderDoc.isTemplateDoc ? (this._props.TemplateDataDocument ?? this._renderDoc[DocData]) : this._renderDoc[DocData];
+            return this.Document.isTemplateForField || this.Document.isTemplateDoc ? (this._props.TemplateDataDocument ?? this.Document[DocData]) : this.Document[DocData];
         }
 
         /**
@@ -165,10 +166,8 @@ export function ViewBoxAnnotatableComponent<P extends FieldViewProps>() {
          * This is the doc that is being rendered. It will be either:
          *   1) the same as Document if the root of a regular or compound Doc is rendered
          *   2) the same as the layoutDoc if a component of a compound Doc is rendered.
-         * NOTE: it would unlikely that you really want to use this instead of the
-         *       other Doc options (Document, layoutDoc, dataDoc)
          */
-        get _renderDoc() {
+        get Document() {
             return this._props.Document;
         }
 
@@ -177,24 +176,27 @@ export function ViewBoxAnnotatableComponent<P extends FieldViewProps>() {
          * this is the outermost Doc that represents the entire compound Doc.  It is not
          * necessarily the Doc being rendered in the current React component.
          * This Doc inherits from the dataDoc, and may or may not inherit (or be) the layoutDoc.
+         *
+         * NOTE: it would unlikely that you really want to use this instead of the
+         *       other Doc options (Document, layoutDoc, dataDoc)
          */
-        @computed get Document() {
-            return DocCast(this._renderDoc.rootDocument, this._renderDoc);
+        @computed get rootDoc() {
+            return DocCast(this.Document.rootDocument, this.Document);
         }
         /**
          * This is the document being rendered.  It may be a template so it may or may no inherit from the data doc.
          */
         @computed get layoutDoc() {
-            return Doc.Layout(this._renderDoc);
+            return Doc.Layout(this.Document);
         }
 
         /**
          * This is the unique data repository for a dcoument that stores the intrinsic document data
          */
         @computed get dataDoc() {
-            return this._renderDoc.isTemplateForField || this._renderDoc.isTemplateDoc ? 
-                (this._props.TemplateDataDocument ?? this._renderDoc[DocData]) : 
-                this._props.Document[DocData]; // prettier-ignore
+            return this.Document.isTemplateForField || this.Document.isTemplateDoc ? 
+                (this._props.TemplateDataDocument ?? this.Document[DocData]) : 
+                this.Document[DocData]; // prettier-ignore
         }
 
         /**
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 19b987cb5..92b4d6fbf 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -635,7 +635,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
         if (DocumentView.Selected().length === 1) {
             const selected = DocumentView.Selected()[0];
             if (this._titleControlString.startsWith('$')) {
-                return Field.toJavascriptString(selected._renderDoc[this._titleControlString.substring(1)] as FieldType) || '-unset-';
+                return Field.toJavascriptString(selected.Document[this._titleControlString.substring(1)] as FieldType) || '-unset-';
             }
             return this._accumulatedTitle;
         }
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index a7d217076..3fc56d7d9 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -16,6 +16,7 @@ import { FocusViewOptions } from '../nodes/FocusViewOptions';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import './CollectionCarouselView.scss';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
+import { DocData } from '../../../fields/DocSymbols';
 
 @observer
 export class CollectionCarouselView extends CollectionSubView() {
@@ -136,7 +137,7 @@ export class CollectionCarouselView extends CollectionSubView() {
                 renderDepth={this._props.renderDepth + 1}
                 LayoutTemplate={this._props.childLayoutTemplate}
                 LayoutTemplateString={this._props.childLayoutString}
-                TemplateDataDocument={DocCast(Doc.Layout(doc).resolvedDataDoc)}
+                TemplateDataDocument={DocCast(Doc.Layout(doc).rootDocument)?.[DocData]}
                 childFilters={this.childDocFilters}
                 focus={this.focus}
                 hideDecorations={BoolCast(this.layoutDoc.layout_hideDecorations)}
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 3b9d167c6..e765b78b4 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -7,7 +7,7 @@ import { Doc, DocListCast, Opt } from '../../../fields/Doc';
 import { RichTextField } from '../../../fields/RichTextField';
 import { PastelSchemaPalette, SchemaHeaderField } from '../../../fields/SchemaHeaderField';
 import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, NumCast } from '../../../fields/Types';
+import { BoolCast, DocCast, NumCast } from '../../../fields/Types';
 import { ImageField } from '../../../fields/URLField';
 import { TraceMobx } from '../../../fields/util';
 import { emptyFunction } from '../../../Utils';
@@ -26,6 +26,7 @@ import { EditableView } from '../EditableView';
 import { DocumentView } from '../nodes/DocumentView';
 import { ObservableReactComponent } from '../ObservableReactComponent';
 import './CollectionStackingView.scss';
+import { DocData } from '../../../fields/DocSymbols';
 
 // So this is how we are storing a column
 interface CSVFieldColumnProps {
@@ -270,7 +271,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
                     event: () => {
                         const created = Docs.Create.CarouselDocument([], { _width: 400, _height: 200, title: fieldKey });
                         if (created) {
-                            const container = this._props.Doc.resolvedDataDoc ? Doc.GetProto(this._props.Doc) : this._props.Doc;
+                            const container = DocCast(this._props.Doc.rootDocument)?.[DocData] ? Doc.GetProto(this._props.Doc) : this._props.Doc;
                             if (container.isTemplateDoc) {
                                 Doc.MakeMetadataFieldTemplate(created, container);
                                 return Doc.AddDocToList(container, Doc.LayoutFieldKey(container), created);
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index e34ba93a9..cfd52f9ee 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -108,11 +108,11 @@ export function CollectionSubView<X>() {
         }
 
         get dataDoc() {
-            return this._props.TemplateDataDocument instanceof Doc && this._renderDoc.isTemplateForField //
+            return this._props.TemplateDataDocument instanceof Doc && this.Document.isTemplateForField //
                 ? Doc.GetProto(this._props.TemplateDataDocument)
-                : this.Document.resolvedDataDoc
-                  ? this._renderDoc
-                  : this.Document[DocData]; // if the layout document has a resolvedDataDoc, then we don't want to get its parent which would be the unexpanded template
+                : this.Document.rootDocument
+                  ? this.Document
+                  : this.Document[DocData]; // if the layout document has a rootDocument, then we don't want to get its parent which would be the unexpanded template
         }
 
         get childContainerViewPath() {
@@ -133,7 +133,7 @@ export function CollectionSubView<X>() {
         @computed get childLayoutPairs(): { layout: Doc; data: Doc }[] {
             const { TemplateDataDocument } = this._props;
             const validPairs = this.childDocs
-                .map(doc => Doc.GetLayoutDataDocPair(this._renderDoc, !this._props.isAnnotationOverlay ? TemplateDataDocument : undefined, doc))
+                .map(doc => Doc.GetLayoutDataDocPair(this.Document, !this._props.isAnnotationOverlay ? TemplateDataDocument : undefined, doc))
                 .filter(
                     pair =>
                         // filter out any documents that have a proto that we don't have permissions to
@@ -299,7 +299,7 @@ export function CollectionSubView<X>() {
             const dragData = de.complete.docDragData;
             if (dragData) {
                 const sourceDragAction = dragData.dropAction;
-                const sameCollection = !dragData.draggedDocuments.some(d => d.embedContainer !== this._renderDoc);
+                const sameCollection = !dragData.draggedDocuments.some(d => d.embedContainer !== this.Document);
                 dragData.dropAction = !sameCollection // if doc from another tree
                     ? sourceDragAction || targetDropAction // then use the source's dragAction otherwise the target's
                     : sourceDragAction === dropActionType.inPlace // if source drag is inPlace
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 1960e12bd..418c437d5 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -187,7 +187,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     @action
     addDoc = (docs: Doc | Doc[], relativeTo: Opt<Doc>, before?: boolean): boolean => {
         const addDocRelativeTo = (adocs: Doc | Doc[]) => (adocs as Doc[]).reduce((flg, doc) => flg && Doc.AddDocToList(this.Document[DocData], this._props.fieldKey, doc, relativeTo, before), true);
-        if (this.Document.resolvedDataDoc instanceof Promise) return false;
+        if (this.Document.rootDocument instanceof Promise) return false;
         const doclist = toList(docs);
         const res = relativeTo === undefined ? this._props.addDocument?.(doclist) || false : addDocRelativeTo(doclist);
         res &&
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 09a1b0c53..09d43c5b0 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -286,7 +286,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     // this search order, for example, allows icons of cropped images to find the panx/pany/zoom on the cropped image's data doc instead of the usual layout doc because the zoom/panX/panY define the cropped image
     panX = () => this.fitContentBounds?.cx ?? NumCast(this.Document[this.panXFieldKey], NumCast(this.Document.freeform_panX, 1));
     panY = () => this.fitContentBounds?.cy ?? NumCast(this.Document[this.panYFieldKey], NumCast(this.Document.freeform_panY, 1));
-    zoomScaling = () => this.fitContentBounds?.scale ?? NumCast(Doc.Layout(this.Document)[this.scaleFieldKey], 1); // , NumCast(DocCast(this.Document.resolvedDataDoc)?.[this.scaleFieldKey], 1));
+    zoomScaling = () => this.fitContentBounds?.scale ?? NumCast(Doc.Layout(this.Document)[this.scaleFieldKey], 1); // , NumCast(DocCast(this.Document.rootDocument)?.[this.scaleFieldKey], 1));
     PanZoomCenterXf = () => (this._props.isAnnotationOverlay && this.zoomScaling() === 1 ? `` : `translate(${this.centeringShiftX}px, ${this.centeringShiftY}px) scale(${this.zoomScaling()}) translate(${-this.panX()}px, ${-this.panY()}px)`);
     ScreenToContentsXf = () => this.screenToFreeformContentsXf.copy();
     getActiveDocuments = () => this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => pair.layout);
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index e7f1de7f1..8393c719b 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -4,7 +4,7 @@ import * as React from 'react';
 import { returnFalse, returnZero, setupMoveUpEvents } from '../../../../ClientUtils';
 import { Doc, Opt } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
-import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
+import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { emptyFunction } from '../../../../Utils';
 import { Docs } from '../../../documents/Documents';
 import { DragManager } from '../../../util/DragManager';
@@ -16,6 +16,7 @@ import { DocumentView } from '../../nodes/DocumentView';
 import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView';
 import './CollectionGridView.scss';
 import Grid, { Layout } from './Grid';
+import { DocData } from '../../../../fields/DocSymbols';
 
 @observer
 export class CollectionGridView extends CollectionSubView() {
@@ -192,7 +193,7 @@ export class CollectionGridView extends CollectionSubView() {
         <DocumentView
             {...this._props}
             Document={layout}
-            TemplateDataDocument={layout.resolvedDataDoc as Doc}
+            TemplateDataDocument={DocCast(layout.rootDocument)?.[DocData]}
             NativeWidth={returnZero}
             NativeHeight={returnZero}
             fitWidth={this._props.childLayoutFitWidth}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 8aae24df0..58582a142 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -6,7 +6,7 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import { FaChevronRight } from 'react-icons/fa';
 import { Doc, DocListCast } from '../../../../fields/Doc';
-import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
+import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { DragManager } from '../../../util/DragManager';
 import { SettingsManager } from '../../../util/SettingsManager';
 import { Transform } from '../../../util/Transform';
@@ -18,6 +18,7 @@ import ResizeBar from './MulticolumnResizer';
 import WidthLabel from './MulticolumnWidthLabel';
 import { dropActionType } from '../../../util/DropActionTypes';
 import { SnappingManager } from '../../../util/SnappingManager';
+import { DocData } from '../../../../fields/DocSymbols';
 
 interface WidthSpecifier {
     magnitude: number;
@@ -272,7 +273,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
         return (
             <DocumentView
                 Document={childLayout}
-                TemplateDataDocument={childLayout.resolvedDataDoc as Doc}
+                TemplateDataDocument={DocCast(childLayout.rootDocument)?.[DocData]}
                 styleProvider={this._props.styleProvider}
                 containerViewPath={this.childContainerViewPath}
                 LayoutTemplate={this._props.childLayoutTemplate}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index bda8e91ac..f95928cf6 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -2,7 +2,7 @@ import { action, computed, makeObservable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast } from '../../../../fields/Doc';
-import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
+import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { DragManager } from '../../../util/DragManager';
 import { dropActionType } from '../../../util/DropActionTypes';
 import { Transform } from '../../../util/Transform';
@@ -11,6 +11,7 @@ import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView'
 import './CollectionMultirowView.scss';
 import HeightLabel from './MultirowHeightLabel';
 import ResizeBar from './MultirowResizer';
+import { DocData } from '../../../../fields/DocSymbols';
 
 interface HeightSpecifier {
     magnitude: number;
@@ -253,7 +254,7 @@ export class CollectionMultirowView extends CollectionSubView() {
         return (
             <DocumentView
                 Document={layout}
-                TemplateDataDocument={layout.resolvedDataDoc as Doc}
+                TemplateDataDocument={DocCast(layout.rootDocument)?.[DocData]}
                 styleProvider={this._props.styleProvider}
                 containerViewPath={this.childContainerViewPath}
                 LayoutTemplate={this._props.childLayoutTemplate}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 6f86383c2..943d2175c 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -304,7 +304,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                 ) : (
                     <DocumentView
                         {...OmitKeys(this._props,this.WrapperKeys.map(val => val.lower)).omit} // prettier-ignore
-                        Document={this._renderDoc}
+                        Document={this.Document}
                         renderDepth={this._props.renderDepth}
                         isContentActive={this._props.isContentActive}
                         childFilters={this._props.childFilters}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 3268ba0f7..4ea2c66f3 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -758,7 +758,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
                     }}>
                     <DocumentContentsView
                         {...this._props}
-                        layoutFieldKey={StrCast(this._renderDoc.layout_fieldKey, 'layout')}
+                        layoutFieldKey={StrCast(this.Document.layout_fieldKey, 'layout')}
                         pointerEvents={this.contentPointerEvents}
                         setContentViewBox={this.setContentView}
                         childFilters={this.childFilters}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 5f132ecdf..416cd577e 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1882,7 +1882,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                     this.dataDoc[this.fieldKey + '_scrollHeight'] = scrollHeight;
                 };
 
-                if (this.Document === this.layoutDoc || this.layoutDoc.resolvedDataDoc) {
+                if (this.Document === this.layoutDoc || this.layoutDoc.rootDocument) {
                     setScrollHeight();
                 } else {
                     setTimeout(setScrollHeight, 10); // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template.  So set a timeout and hope its arrived...
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 76d302db7..ba088d674 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -405,7 +405,7 @@ export class Doc extends RefField {
     }
     @computed get __DATA__(): Doc {
         const self = this[SelfProxy];
-        return self.resolvedDataDoc && !self.isTemplateForField ? self : Doc.GetProto(Cast(Doc.Layout(self).resolvedDataDoc, Doc, null) || self);
+        return self.rootDocument && !self.isTemplateForField ? self : Doc.GetProto(Cast(Doc.Layout(self).rootDocument, Doc, null) || self);
     }
     @computed get __LAYOUT__(): Doc | undefined {
         const self = this[SelfProxy];
@@ -871,15 +871,15 @@ export namespace Doc {
         const expandedLayoutFieldKey = 'layout_' + templateLayoutDoc.title + '(' + templateField + ')';
         let expandedTemplateLayout = targetDoc?.[expandedLayoutFieldKey];
 
-        if (templateLayoutDoc.resolvedDataDoc instanceof Promise) {
+        if (templateLayoutDoc.rootDocument instanceof Promise) {
             expandedTemplateLayout = undefined;
             _pendingMap.add(targetDoc[Id] + expandedLayoutFieldKey);
         } else if (expandedTemplateLayout === undefined && !_pendingMap.has(targetDoc[Id] + expandedLayoutFieldKey)) {
-            if (templateLayoutDoc.resolvedDataDoc === targetDoc[DocData]) {
+            if (DocCast(templateLayoutDoc.rootDocument)?.[DocData] === targetDoc[DocData]) {
                 expandedTemplateLayout = templateLayoutDoc; // reuse an existing template layout if its for the same document with the same params
             } else {
                 // eslint-disable-next-line no-param-reassign
-                templateLayoutDoc.resolvedDataDoc && (templateLayoutDoc = DocCast(templateLayoutDoc.proto, templateLayoutDoc)); // if the template has already been applied (ie, a nested template), then use the template's prototype
+                templateLayoutDoc.rootDocument && (templateLayoutDoc = DocCast(templateLayoutDoc.proto, templateLayoutDoc)); // if the template has already been applied (ie, a nested template), then use the template's prototype
                 if (!targetDoc[expandedLayoutFieldKey]) {
                     _pendingMap.add(targetDoc[Id] + expandedLayoutFieldKey);
                     setTimeout(
@@ -888,7 +888,6 @@ export namespace Doc {
                             const dataDoc = Doc.GetProto(targetDoc);
                             newLayoutDoc.rootDocument = targetDoc;
                             newLayoutDoc.embedContainer = targetDoc;
-                            newLayoutDoc.resolvedDataDoc = dataDoc;
                             newLayoutDoc.acl_Guest = SharingPermissions.Edit;
                             if (dataDoc[templateField] === undefined && (templateLayoutDoc[templateField] as List<Doc>)?.length) {
                                 dataDoc[templateField] = ObjectField.MakeCopy(templateLayoutDoc[templateField] as List<Doc>);
@@ -912,9 +911,9 @@ export namespace Doc {
             console.log('Warning: GetLayoutDataDocPair childDoc not defined');
             return { layout: childDoc, data: childDoc };
         }
-        const resolvedDataDoc = Doc.AreProtosEqual(containerDataDoc, containerDoc) || (!Doc.isTemplateDoc(childDoc) && !Doc.isTemplateForField(childDoc)) ? undefined : containerDataDoc;
+        const data = Doc.AreProtosEqual(containerDataDoc, containerDoc) || (!Doc.isTemplateDoc(childDoc) && !Doc.isTemplateForField(childDoc)) ? undefined : containerDataDoc;
         const templateRoot = DocCast(containerDoc?.rootDocument);
-        return { layout: Doc.expandTemplateLayout(childDoc, templateRoot), data: resolvedDataDoc };
+        return { layout: Doc.expandTemplateLayout(childDoc, templateRoot), data };
     }
 
     export function FindReferences(infield: Doc | List<Doc>, references: Set<Doc>, system: boolean | undefined) {
@@ -1083,7 +1082,7 @@ export namespace Doc {
 
     export function ApplyTemplateTo(templateDoc: Doc, target: Doc, targetKey: string, titleTarget: string | undefined) {
         if (!Doc.AreProtosEqual(target[targetKey] as Doc, templateDoc)) {
-            if (target.resolvedDataDoc) {
+            if (target.rootDocument) {
                 target[targetKey] = new PrefetchProxy(templateDoc);
             } else {
                 titleTarget && (Doc.GetProto(target).title = titleTarget);
@@ -1301,7 +1300,7 @@ export namespace Doc {
             highlightedDocs.add(doc);
             doc[Highlight] = true;
             doc[Animation] = presentationEffect;
-            if (dataAndDisplayDocs && !doc.resolvedDataDoc) {
+            if (dataAndDisplayDocs && !doc.rootDocument) {
                 // if doc is a layout template then we don't want to highlight the proto since that will be the entire template, not just the specific layout field
                 highlightedDocs.add(doc[DocData]);
                 doc[DocData][Highlight] = true;
@@ -1331,7 +1330,7 @@ export namespace Doc {
               : Cast(doc.dragFactory, Doc, null)?.isTemplateDoc
                 ? doc.dragFactory
                 : Cast(Doc.Layout(doc), Doc, null)?.isTemplateDoc
-                  ? Cast(Doc.Layout(doc), Doc, null).resolvedDataDoc
+                  ? Cast(Doc.Layout(doc), Doc, null).rootDocument
                       ? Doc.Layout(doc).proto
                       : Doc.Layout(doc)
                   : undefined;
-- 
cgit v1.2.3-70-g09d2


From 63772da2b6f07365023d10c5df93c1e8c4f0b6b6 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sun, 30 Mar 2025 11:16:37 -0400
Subject: changed Doc.Layout calls to doc[DocLayout].  fixed flashcard ui
 placement on card view.  fixed css scaling for styleprovider icons and
 annotation resizer

---
 src/client/util/DropConverter.ts                   |   4 +-
 src/client/views/DocComponent.tsx                  |   6 +-
 src/client/views/DocumentDecorations.scss          | 112 ++++++++++--------
 src/client/views/DocumentDecorations.tsx           |  18 +--
 src/client/views/PropertiesView.tsx                |  12 +-
 src/client/views/StyleProvider.scss                |  16 ++-
 src/client/views/StyleProvider.tsx                 |   6 +-
 .../views/collections/CollectionCarouselView.scss  |   4 +-
 .../views/collections/CollectionCarouselView.tsx   |   4 +-
 src/client/views/collections/TabDocView.scss       |  12 +-
 src/client/views/collections/TabDocView.tsx        |  27 ++++-
 src/client/views/collections/TreeView.tsx          |   6 +-
 .../CollectionFreeFormClusters.ts                  |  17 ++-
 .../CollectionFreeFormLayoutEngines.tsx            |   7 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  22 ++--
 .../collections/collectionFreeForm/MarqueeView.tsx |   3 +-
 .../collectionSchema/CollectionSchemaView.tsx      |   2 +-
 .../collectionSchema/SchemaTableCell.tsx           |   3 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |   3 +-
 src/client/views/nodes/ComparisonBox.tsx           | 131 +--------------------
 src/client/views/nodes/DocumentContentsView.tsx    | 101 ++++++++++++----
 src/client/views/nodes/DocumentView.tsx            |  44 +------
 src/client/views/nodes/FieldView.tsx               |   9 +-
 src/client/views/nodes/ImageBox.tsx                |  40 ++++---
 src/client/views/nodes/KeyValueBox.tsx             |   3 +-
 src/client/views/nodes/KeyValuePair.tsx            |   3 +-
 src/client/views/nodes/PDFBox.scss                 |   6 +-
 src/client/views/nodes/PDFBox.tsx                  |   4 +-
 .../nodes/formattedText/FormattedTextBox.scss      |   5 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |   3 +-
 .../views/nodes/importBox/ImportElementBox.tsx     |   2 -
 src/client/views/pdf/PDFViewer.tsx                 |   3 +-
 src/fields/Doc.ts                                  |  48 +++-----
 src/fields/util.ts                                 |  25 ++--
 34 files changed, 317 insertions(+), 394 deletions(-)

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

diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index b5d29be4c..7d3f63448 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -1,5 +1,5 @@
 import { Doc, DocListCast, StrListCast } from '../../fields/Doc';
-import { DocData } from '../../fields/DocSymbols';
+import { DocData, DocLayout } from '../../fields/DocSymbols';
 import { ObjectField } from '../../fields/ObjectField';
 import { RichTextField } from '../../fields/RichTextField';
 import { ComputedField, ScriptField } from '../../fields/ScriptField';
@@ -93,7 +93,7 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) {
     data?.draggedDocuments.forEach((doc, i) => {
         let dbox = doc;
         // bcz: isButtonBar is intended to allow a collection of linear buttons to be dropped and nested into another collection of buttons... it's not being used yet, and isn't very elegant
-        if (doc.type === DocumentType.FONTICON || StrCast(Doc.Layout(doc).layout).includes(FontIconBox.name)) {
+        if (doc.type === DocumentType.FONTICON || StrCast(doc[DocLayout].layout).includes(FontIconBox.name)) {
             if (data.dropPropertiesToRemove || dbox.dropPropertiesToRemove) {
                 // dbox = Doc.MakeEmbedding(doc);  // don't need to do anything if dropping an icon doc onto an icon bar since there should be no layout data for an icon
                 dbox = Doc.MakeEmbedding(dbox);
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index e3d47317c..d33f3b713 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -3,7 +3,7 @@ import * as React from 'react';
 import { returnFalse } from '../../ClientUtils';
 import { DateField } from '../../fields/DateField';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
-import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, DocData } from '../../fields/DocSymbols';
+import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, DocData, DocLayout } from '../../fields/DocSymbols';
 import { List } from '../../fields/List';
 import { DocCast, toList } from '../../fields/Types';
 import { GetEffectiveAcl, inheritParentAcls } from '../../fields/util';
@@ -116,7 +116,7 @@ export function ViewBoxBaseComponent<P extends FieldViewProps>() {
          * This may or may not inherit from the data doc.
          */
         @computed get layoutDoc() {
-            return Doc.Layout(this.Document);
+            return this.Document[DocLayout];
         }
 
         /**
@@ -187,7 +187,7 @@ export function ViewBoxAnnotatableComponent<P extends FieldViewProps>() {
          * This is the document being rendered.  It may be a template so it may or may no inherit from the data doc.
          */
         @computed get layoutDoc() {
-            return Doc.Layout(this.Document);
+            return this.Document[DocLayout];
         }
 
         /**
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index a5afb1305..732c2645e 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -91,30 +91,71 @@ $resizeHandler: 8px;
             }
         }
 
-        .documentDecorations-closeButton {
+        .documentDecorations-closeWrapper {
             display: flex;
-            align-items: center;
-            justify-content: center;
-            background: #fb9d75;
-            border: solid 1.5px #a94442;
-            color: #fb9d75;
-            transition: 0.1s ease;
-            opacity: 1;
-            pointer-events: all;
-            width: 20px;
-            height: 20px;
-            min-width: 20px;
-            border-radius: 100%;
-            opacity: 0.5;
-            cursor: pointer;
-
-            &:hover {
-                color: #a94442;
+            .documentDecorations-closeButton {
+                display: flex;
+                align-items: center;
+                justify-content: center;
+                background: #fb9d75;
+                border: solid 1.5px #a94442;
+                color: #fb9d75;
+                transition: 0.1s ease;
                 opacity: 1;
+                pointer-events: all;
+                width: 20px;
+                height: 20px;
+                min-width: 20px;
+                border-radius: 100%;
+                opacity: 0.5;
+                cursor: pointer;
+
+                &:hover {
+                    color: #a94442;
+                    opacity: 1;
+                    width: 20px;
+                    min-width: 20px;
+                }
+
+                > svg {
+                    margin: 0;
+                }
             }
 
-            > svg {
-                margin: 0;
+            .documentDecorations-minimizeButton {
+                display: none;
+                align-items: center;
+                justify-content: center;
+                background: #ffdd00;
+                border: solid 1.5px #a94442;
+                color: #ffdd00;
+                transition: 0.1s ease;
+                font-size: 11px;
+                opacity: 1;
+                grid-column: 2;
+                pointer-events: all;
+                width: 20px;
+                height: 20px;
+                min-width: 20px;
+                border-radius: 100%;
+                opacity: 0.5;
+                cursor: pointer;
+
+                &:hover {
+                    color: #a94442;
+                    opacity: 1;
+                }
+
+                > svg {
+                    margin: 0;
+                }
+            }
+            &:hover {
+                width: 40px;
+                min-width: 40px;
+                .documentDecorations-minimizeButton {
+                    display: flex;
+                }
             }
         }
 
@@ -145,38 +186,9 @@ $resizeHandler: 8px;
             }
         }
 
-        .documentDecorations-minimizeButton {
-            display: flex;
-            align-items: center;
-            justify-content: center;
-            background: #ffdd00;
-            border: solid 1.5px #a94442;
-            color: #ffdd00;
-            transition: 0.1s ease;
-            font-size: 11px;
-            opacity: 1;
-            grid-column: 2;
-            pointer-events: all;
-            width: 20px;
-            height: 20px;
-            min-width: 20px;
-            border-radius: 100%;
-            opacity: 0.5;
-            cursor: pointer;
-
-            &:hover {
-                color: #a94442;
-                opacity: 1;
-            }
-
-            > svg {
-                margin: 0;
-            }
-        }
-
         .documentDecorations-title {
             opacity: 1;
-            width: calc(100% - 60px); // = margin-left + margin-right
+            width: 100%;
             grid-column: 3;
             pointer-events: auto;
             overflow: hidden;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 92b4d6fbf..7842c233a 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -320,7 +320,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
                 const maxDist = Math.min((this.Bounds.r - this.Bounds.x) / 2, (this.Bounds.b - this.Bounds.y) / 2);
                 const dist = moveEv.clientX < x && moveEv.clientY < y ? 0 : Math.sqrt((moveEv.clientX - x) * (moveEv.clientX - x) + (moveEv.clientY - y) * (moveEv.clientY - y));
                 DocumentView.SelectedDocs().forEach(doc => {
-                    const docMax = Math.min(NumCast(doc.width) / 2, NumCast(doc.height) / 2);
+                    const docMax = Math.min(NumCast(doc._width) / 2, NumCast(doc._height) / 2);
                     const radius = Math.min(1, dist / maxDist) * docMax; // set radius based on ratio of drag distance to half diagonal distance of bounding box
                     doc._layout_borderRounding = `${radius}px`;
                 });
@@ -388,7 +388,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
         DocumentView.Selected().forEach(dv => {
             const accumRot = (NumCast(dv.Document._rotation) / 180) * Math.PI;
             const localRotCtr = dv.screenToViewTransform().transformPoint(rcScreen.X, rcScreen.Y);
-            const localRotCtrOffset = [localRotCtr[0] - NumCast(dv.Document.width) / 2, localRotCtr[1] - NumCast(dv.Document.height) / 2];
+            const localRotCtrOffset = [localRotCtr[0] - NumCast(dv.Document._width) / 2, localRotCtr[1] - NumCast(dv.Document._height) / 2];
             const startRotCtr = Utils.rotPt(localRotCtrOffset[0], localRotCtrOffset[1], -accumRot);
             const unrotatedDocPos = { x: NumCast(dv.Document.x) + localRotCtrOffset[0] - startRotCtr.x, y: NumCast(dv.Document.y) + localRotCtrOffset[1] - startRotCtr.y };
             infos.set(dv.Document, { unrotatedDocPos, startRotCtr, accumRot });
@@ -623,8 +623,8 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
             .forEach(({ oldbds: { doc, x, y, width, height }, inkPts }) => {
                 doc.$data = new InkField(inkPts.map(
                     (ipt) => ({// (new x — oldx) + newWidth * (oldxpoint /oldWidth)
-                        X: NumCast(doc.x) - x + (NumCast(doc.width) * ipt.X) / width,
-                        Y: NumCast(doc.y) - y + (NumCast(doc.height) * ipt.Y) / height,
+                        X: NumCast(doc.x) - x + (NumCast(doc._width) * ipt.X) / width,
+                        Y: NumCast(doc.y) - y + (NumCast(doc._height) * ipt.Y) / height,
                     }))); // prettier-ignore
                 Doc.SetNativeWidth(doc, undefined);
                 Doc.SetNativeHeight(doc, undefined);
@@ -719,7 +719,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
         // Radius constants
         const useRounding = seldocview.ComponentView instanceof ImageBox || seldocview.ComponentView instanceof FormattedTextBox || seldocview.ComponentView instanceof CollectionFreeFormView;
         const borderRadius = numberValue(Cast(seldocview.Document.layout_borderRounding, 'string', null));
-        const docMax = Math.min(NumCast(seldocview.Document.width) / 2, NumCast(seldocview.Document.height) / 2);
+        const docMax = Math.min(NumCast(seldocview.Document._width) / 2, NumCast(seldocview.Document._height) / 2);
         const maxDist = Math.min((this.Bounds.r - this.Bounds.x) / 2, (this.Bounds.b - this.Bounds.y) / 2);
         const radiusHandle = (borderRadius / docMax) * maxDist;
         const radiusHandleLocation = Math.min(radiusHandle, maxDist);
@@ -773,7 +773,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
                     </span>
                 )}
                 {sharingMenu}
-                {!useLock ? null : (
+                {!useLock || hideTitle ? null : (
                     <Tooltip key="lock" title={<div className="dash-tooltip">toggle ability to interact with document</div>} placement="top">
                         <div className="documentDecorations-lock" style={{ color: seldocview.Document._lockedPosition ? 'red' : undefined }} onPointerDown={this.onLockDown}>
                             <FontAwesomeIcon size="sm" icon="lock" />
@@ -818,8 +818,10 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
                                     display: hideDeleteButton && hideTitle && hideOpenButton ? 'none' : undefined,
                                 }}
                                 onPointerDown={this.onContainerDown}>
-                                {hideDeleteButton ? null : topBtn('close', 'times', undefined, () => this.onCloseClick(true), 'Close')}
-                                {hideResizers || hideDeleteButton ? null : topBtn('minimize', 'window-maximize', undefined, () => this.onCloseClick(undefined), 'Minimize')}
+                                <div className="documentDecorations-closeWrapper">
+                                    {hideDeleteButton ? null : topBtn('close', 'times', undefined, () => this.onCloseClick(true), 'Close')}
+                                    {hideResizers || hideDeleteButton ? null : topBtn('minimize', 'window-maximize', undefined, () => this.onCloseClick(undefined), 'Minimize')}
+                                </div>
                                 {titleArea}
                                 {hideOpenButton ? <div /> : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Lightbox (ctrl: as alias, shift: in new collection, opption: in editor view)')}
                             </div>
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 7e9cd002b..7fcb15afe 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -13,7 +13,7 @@ import ResizeObserver from 'resize-observer-polyfill';
 import { ClientUtils, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents } from '../../ClientUtils';
 import { emptyFunction } from '../../Utils';
 import { Doc, DocListCast, Field, FieldResult, FieldType, HierarchyMapping, NumListCast, Opt, ReverseHierarchyMap, StrListCast, returnEmptyDoclist } from '../../fields/Doc';
-import { AclAdmin, DocAcl, DocData } from '../../fields/DocSymbols';
+import { AclAdmin, DocAcl, DocData, DocLayout } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { InkField } from '../../fields/InkField';
 import { List } from '../../fields/List';
@@ -205,7 +205,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
             const ids = new Set<string>(reqdKeys);
             const docs: Doc[] =
                 DocumentView.Selected().length < 2 //
-                    ? [this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc]
+                    ? [this.layoutFields ? this.selectedDoc[DocLayout] : this.dataDoc]
                     : DocumentView.Selected().map(dv => (this.layoutFields ? dv.layoutDoc : dv.dataDoc));
             docs.forEach(doc =>
                 Object.keys(doc)
@@ -264,7 +264,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
         const docs =
             DocumentView.Selected().length < 2 && this.selectedDoc
                 ? [this.layoutFields
-                        ? Doc.Layout(this.selectedDoc) //
+                        ? this.selectedDoc[DocLayout] //
                         : this.dataDoc!]
                 : DocumentView.Selected().map(dv => (this.layoutFields ? dv.layoutDoc : dv.dataDoc)); // prettier-ignore
         docs.forEach(doc => {
@@ -330,7 +330,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
             return '-- multiple selected --';
         }
         if (this.selectedDoc) {
-            const layoutDoc = Doc.Layout(this.selectedDoc);
+            const layoutDoc = this.selectedDoc[DocLayout];
             const panelHeight = StrCast(Doc.LayoutField(layoutDoc)).includes('FormattedTextBox') ? this.rtfHeight : this.docHeight;
             const panelWidth = StrCast(Doc.LayoutField(layoutDoc)).includes('FormattedTextBox') ? this.rtfWidth : this.docWidth;
             return (
@@ -894,9 +894,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
         });
     }
     @computed get borderColor() {
-        const doc = this.selectedDoc;
-        const layoutDoc = doc ? Doc.Layout(doc) : doc;
-        return StrCast(layoutDoc.color);
+        return StrCast(this.selectedDoc._color);
     }
     set borderColor(value) { this.selectedDoc && (this.selectedDoc.$color = value || undefined); } // prettier-ignore
 
diff --git a/src/client/views/StyleProvider.scss b/src/client/views/StyleProvider.scss
index ce00f6101..501b9a292 100644
--- a/src/client/views/StyleProvider.scss
+++ b/src/client/views/StyleProvider.scss
@@ -5,10 +5,10 @@
 .styleProvider-lock {
     z-index: 2; // has to be above title which is z-index 1
     font-size: 10;
-    width: 15;
-    height: 15;
+    width: 20;
+    height: 20;
     position: absolute;
-    right: -15;
+    right: -20;
     top: 0;
     background: black;
     pointer-events: all;
@@ -21,22 +21,26 @@
     cursor: default;
 }
 .styleProvider-filter {
-    right: 15;
+    right: 20;
     .styleProvider-filterShift {
         left: 0;
         top: 0;
         position: absolute;
     }
+    .dropdown-container {
+        width: 15px !important;
+        margin: auto !important;
+    }
 }
 .styleProvider-audio {
-    right: 30;
+    right: 40;
 }
 .styleProvider-paint-selected,
 .styleProvider-paint {
     top: 15;
 }
 .styleProvider-paint-selected {
-    right: -30;
+    right: -40;
 }
 .styleProvider-lock:hover,
 .styleProvider-audio:hover,
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index e25227c00..a59d39cfb 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -20,11 +20,13 @@ import { SnappingManager } from '../util/SnappingManager';
 import { undoable, UndoManager } from '../util/UndoManager';
 import { TreeSort } from './collections/TreeSort';
 import { Colors } from './global/globalEnums';
-import { DocumentView, DocumentViewProps } from './nodes/DocumentView';
+import { DocumentViewProps } from './nodes/DocumentContentsView';
+import { DocumentView } from './nodes/DocumentView';
 import { FieldViewProps } from './nodes/FieldView';
 import { StyleProp } from './StyleProp';
 import './StyleProvider.scss';
 import { styleProviderQuiz } from './StyleProviderQuiz';
+import { DocLayout } from '../../fields/DocSymbols';
 
 function toggleLockedPosition(doc: Doc) {
     UndoManager.RunInBatch(() => Doc.toggleLockedPosition(doc), 'toggleBackground');
@@ -87,7 +89,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
     const isNonTransparent = property.includes(':nonTransparent');
     const isNonTransparentLevel = isNonTransparent ? Number(property.replace(/.*:nonTransparent([0-9]+).*/, '$1')) : 0; // property.includes(':nonTransparent');
     const isAnnotated = property.includes(':annotated');
-    const layoutDoc = doc ? Doc.Layout(doc) : doc;
+    const layoutDoc = doc?.[DocLayout];
     const isOpen = property.includes(':treeOpen');
     const boxBackground = property.includes(':docView'); // background color of docView's bounds can be different than the background of contents -- eg FontIconBox
     const {
diff --git a/src/client/views/collections/CollectionCarouselView.scss b/src/client/views/collections/CollectionCarouselView.scss
index 544b3e262..1080aa703 100644
--- a/src/client/views/collections/CollectionCarouselView.scss
+++ b/src/client/views/collections/CollectionCarouselView.scss
@@ -1,8 +1,10 @@
 .collectionCarouselView-outer {
     height: 100%;
-    position: relative;
+    position: absolute;
     overflow: hidden;
     display: flex;
+    transform-origin: top left;
+
     .collectionCarouselView-caption {
         height: 50;
         display: inline-block;
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 3fc56d7d9..c80ac23eb 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -137,7 +137,7 @@ export class CollectionCarouselView extends CollectionSubView() {
                 renderDepth={this._props.renderDepth + 1}
                 LayoutTemplate={this._props.childLayoutTemplate}
                 LayoutTemplateString={this._props.childLayoutString}
-                TemplateDataDocument={DocCast(Doc.Layout(doc).rootDocument)?.[DocData]}
+                TemplateDataDocument={doc[DocData]}
                 childFilters={this.childDocFilters}
                 focus={this.focus}
                 hideDecorations={BoolCast(this.layoutDoc.layout_hideDecorations)}
@@ -243,11 +243,9 @@ export class CollectionCarouselView extends CollectionSubView() {
                         color: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color) as string,
                         left: NumCast(this.layoutDoc._xMargin),
                         top: NumCast(this.layoutDoc._yMargin),
-                        transformOrigin: 'top left',
                         transform: `scale(${this.nativeScaling()})`,
                         width: `calc(${100 / this.nativeScaling()}% - ${(2 * NumCast(this.layoutDoc._xMargin)) / this.nativeScaling()}px)`,
                         height: `calc(${100 / this.nativeScaling()}% - ${(2 * NumCast(this.layoutDoc._yMargin)) / this.nativeScaling()}px)`,
-                        position: 'relative',
                     }}>
                     {this.content}
                     {this.navButtons}
diff --git a/src/client/views/collections/TabDocView.scss b/src/client/views/collections/TabDocView.scss
index 397e35ca9..931cdac2b 100644
--- a/src/client/views/collections/TabDocView.scss
+++ b/src/client/views/collections/TabDocView.scss
@@ -4,7 +4,6 @@
     height: 100%;
     width: 100%;
 }
-
 input.lm_title:focus,
 input.lm_title {
     max-width: unset !important;
@@ -22,7 +21,6 @@ input.lm_title {
 .lm_iconWrap {
     display: flex;
     color: black;
-    width: 15px;
     height: 15px;
     align-items: center;
     align-self: center;
@@ -30,6 +28,16 @@ input.lm_title {
     margin: 3px;
     border-radius: 20%;
 
+    width: auto;
+    svg:nth-of-type(2) {
+        display: none;
+    }
+    &:hover {
+        svg:nth-of-type(2) {
+            display: block;
+        }
+    }
+
     .moreInfoDot {
         background-color: white;
         border-radius: 100%;
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 620be2726..568a08792 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -10,7 +10,7 @@ import ResizeObserver from 'resize-observer-polyfill';
 import { ClientUtils, DashColor, lightOrDark, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick } from '../../../ClientUtils';
 import { emptyFunction } from '../../../Utils';
 import { Doc, Opt, returnEmptyDoclist } from '../../../fields/Doc';
-import { DocData } from '../../../fields/DocSymbols';
+import { DocData, DocLayout } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { FieldId } from '../../../fields/RefField';
@@ -41,6 +41,7 @@ import { CollectionDockingView } from './CollectionDockingView';
 import { CollectionView } from './CollectionView';
 import './TabDocView.scss';
 import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
+import { Tooltip } from '@mui/material';
 
 interface TabMinimapViewProps {
     doc: Doc;
@@ -311,7 +312,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
     @observable _view: DocumentView | undefined = undefined;
     @observable _forceInvalidateScreenToLocal = 0; // screentolocal is computed outside of react using a dom resize ovbserver.  this hack allows the resize observer to trigger a react update
 
-    @computed get layoutDoc() { return this._document && Doc.Layout(this._document); } // prettier-ignore
+    @computed get layoutDoc() { return this._document?.[DocLayout]; } // prettier-ignore
     @computed get isUserActivated() {  return TabDocView.IsSelected(this._document) || this._isAnyChildContentActive; } // prettier-ignore
     @computed get isContentActive() { return this.isUserActivated || this._hovering; } // prettier-ignore
 
@@ -353,7 +354,6 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
 
             if (tab.element[0].children[1].children.length === 1) {
                 iconWrap.className = 'lm_iconWrap lm_moreInfo';
-                iconWrap.title = 'click for menu, drag to embed in document';
                 const dragBtnDown = (e: React.PointerEvent) => {
                     setupMoveUpEvents(
                         this,
@@ -377,7 +377,26 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
                     );
                 };
 
-                const docIcon = <FontAwesomeIcon onPointerDown={dragBtnDown} icon={iconType} />;
+                const docIcon = (
+                    <>
+                        <Tooltip title="click for menu, drag to embed in document">
+                            <FontAwesomeIcon onPointerDown={dragBtnDown} icon={iconType} />
+                        </Tooltip>
+                        <Tooltip title="click to open in lightbox">
+                            <FontAwesomeIcon
+                                onPointerDown={dragBtnDown}
+                                icon="external-link-alt"
+                                onClick={() => {
+                                    if (doc.layout_fieldKey === 'layout_icon') {
+                                        const odoc = Doc.GetEmbeddings(doc).find(embedding => !embedding.embedContainer) ?? Doc.MakeEmbedding(doc);
+                                        Doc.deiconifyView(odoc);
+                                    }
+                                    this.addDocTab(doc, OpenWhere.lightboxAlways);
+                                }}
+                            />
+                        </Tooltip>
+                    </>
+                );
                 const closeIcon = <FontAwesomeIcon icon="eye" />;
                 ReactDOM.createRoot(iconWrap).render(docIcon);
                 ReactDOM.createRoot(closeWrap).render(closeIcon);
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 9889766d5..4dc937864 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -7,7 +7,7 @@ import * as React from 'react';
 import { ClientUtils, lightOrDark, return18, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick } from '../../../ClientUtils';
 import { emptyFunction } from '../../../Utils';
 import { Doc, DocListCast, Field, FieldType, Opt, StrListCast, returnEmptyDoclist } from '../../../fields/Doc';
-import { DocData } from '../../../fields/DocSymbols';
+import { DocData, DocLayout } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { RichTextField } from '../../../fields/RichTextField';
@@ -156,7 +156,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
         return this.Document[DocData];
     }
     @computed get layoutDoc() {
-        return Doc.Layout(this.Document);
+        return this.Document[DocLayout];
     }
     @computed get fieldKey() {
         return StrCast(this.Document._treeView_FieldKey, Doc.LayoutFieldKey(this.Document));
@@ -1309,7 +1309,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
                 const indent = i === 0 ? undefined : (editTitle: boolean) => dentDoc(editTitle, docs[i - 1], undefined, treeViewRefs.get(docs[i - 1]));
                 const outdent = !parentCollectionDoc ? undefined : (editTitle: boolean) => dentDoc(editTitle, parentCollectionDoc, containerPrevSibling, parentTreeView instanceof TreeView ? parentTreeView._props.parentTreeView : undefined);
                 const addDocument = (doc: Doc | Doc[], annotationKey?: string, relativeTo?: Doc, before?: boolean) => add(doc, relativeTo ?? docs[i], before !== undefined ? before : false);
-                const childLayout = Doc.Layout(pair.layout);
+                const childLayout = pair.layout[DocLayout];
                 const rowHeight = () => {
                     const aspect = Doc.NativeAspect(childLayout);
                     return aspect ? Math.min(NumCast(childLayout._width), rowWidth()) / aspect : NumCast(childLayout._height);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts b/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts
index 3838852dd..903d92c90 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts
@@ -31,16 +31,14 @@ export class CollectionFreeFormClusters {
     get selectDocuments()    { return this._view.selectDocuments; } // prettier-ignore
 
     static overlapping(doc1: Doc, doc2: Doc, clusterDistance: number) {
-        const doc2Layout = Doc.Layout(doc2);
-        const doc1Layout = Doc.Layout(doc1);
         const x2 = NumCast(doc2.x) - clusterDistance;
         const y2 = NumCast(doc2.y) - clusterDistance;
-        const w2 = NumCast(doc2Layout._width) + clusterDistance;
-        const h2 = NumCast(doc2Layout._height) + clusterDistance;
+        const w2 = NumCast(doc2._width) + clusterDistance;
+        const h2 = NumCast(doc2._height) + clusterDistance;
         const x = NumCast(doc1.x) - clusterDistance;
         const y = NumCast(doc1.y) - clusterDistance;
-        const w = NumCast(doc1Layout._width) + clusterDistance;
-        const h = NumCast(doc1Layout._height) + clusterDistance;
+        const w = NumCast(doc1._width) + clusterDistance;
+        const h = NumCast(doc1._height) + clusterDistance;
         return doc1.z === doc2.z && intersectRect({ left: x, top: y, width: w, height: h }, { left: x2, top: y2, width: w2, height: h2 });
     }
     handlePointerDown(probe: number[]) {
@@ -49,12 +47,11 @@ export class CollectionFreeFormClusters {
             .reduce((cluster, cd) => {
                 const grouping = this.Document._freeform_useClusters ? NumCast(cd.layout_cluster, -1) : NumCast(cd.group, -1);
                 if (grouping !== -1) {
-                    const layoutDoc = Doc.Layout(cd);
                     const cx = NumCast(cd.x) - this._clusterDistance / 2;
                     const cy = NumCast(cd.y) - this._clusterDistance / 2;
-                    const cw = NumCast(layoutDoc._width) + this._clusterDistance;
-                    const ch = NumCast(layoutDoc._height) + this._clusterDistance;
-                    return !layoutDoc.z && intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 }) ? grouping : cluster;
+                    const cw = NumCast(cd._width) + this._clusterDistance;
+                    const ch = NumCast(cd._height) + this._clusterDistance;
+                    return !cd.z && intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 }) ? grouping : cluster;
                 }
                 return cluster;
             }, -1);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 4ea1de680..158bac7ba 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -1,5 +1,6 @@
 /* eslint-disable no-use-before-define */
 import { Doc, Field, FieldType, FieldResult } from '../../../../fields/Doc';
+import { DocLayout } from '../../../../fields/DocSymbols';
 import { Id, ToString } from '../../../../fields/FieldSymbols';
 import { ObjectField } from '../../../../fields/ObjectField';
 import { RefField } from '../../../../fields/RefField';
@@ -237,7 +238,7 @@ export function computePivotLayout(poolData: Map<string, PoolData>, pivotDoc: Do
             payload: val,
         });
         val?.docs.forEach((doc, i) => {
-            const layoutDoc = Doc.Layout(doc);
+            const layoutDoc = doc[DocLayout];
             let wid = pivotAxisWidth;
             let hgt = pivotAxisWidth / (Doc.NativeAspect(layoutDoc) || 1);
             if (hgt > pivotAxisWidth) {
@@ -249,7 +250,7 @@ export function computePivotLayout(poolData: Map<string, PoolData>, pivotDoc: Do
                 y: -y + (pivotAxisWidth - hgt) / 2,
                 width: wid,
                 height: hgt,
-                backgroundColor: StrCast(layoutDoc.backgroundColor, 'white'),
+                backgroundColor: StrCast(doc.backgroundColor, 'white'),
                 pair: { layout: doc },
                 replica: val.replicas[i],
             });
@@ -362,7 +363,7 @@ export function computeTimelineLayout(poolData: Map<string, PoolData>, pivotDoc:
     function layoutDocsAtTime(keyDocs: Doc[], key: number) {
         keyDocs.forEach(doc => {
             const stack = findStack(x, stacking);
-            const layoutDoc = Doc.Layout(doc);
+            const layoutDoc = doc[DocLayout];
             let wid = pivotAxisWidth;
             let hgt = pivotAxisWidth / (Doc.NativeAspect(layoutDoc) || 1);
             if (hgt > pivotAxisWidth) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 09d43c5b0..f01fb8fc7 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -11,7 +11,7 @@ import ReactLoading from 'react-loading';
 import { ClientUtils, DashColor, lightOrDark, OmitKeys, returnFalse, returnZero, setupMoveUpEvents, UpdateIcon } from '../../../../ClientUtils';
 import { DateField } from '../../../../fields/DateField';
 import { Doc, DocListCast, Field, FieldType, Opt, StrListCast } from '../../../../fields/Doc';
-import { DocData, Height, Width } from '../../../../fields/DocSymbols';
+import { DocData, DocLayout, Height, Width } from '../../../../fields/DocSymbols';
 import { Id } from '../../../../fields/FieldSymbols';
 import { InkData, InkEraserTool, InkField, InkInkTool, InkTool, Segment } from '../../../../fields/InkField';
 import { List } from '../../../../fields/List';
@@ -286,7 +286,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     // this search order, for example, allows icons of cropped images to find the panx/pany/zoom on the cropped image's data doc instead of the usual layout doc because the zoom/panX/panY define the cropped image
     panX = () => this.fitContentBounds?.cx ?? NumCast(this.Document[this.panXFieldKey], NumCast(this.Document.freeform_panX, 1));
     panY = () => this.fitContentBounds?.cy ?? NumCast(this.Document[this.panYFieldKey], NumCast(this.Document.freeform_panY, 1));
-    zoomScaling = () => this.fitContentBounds?.scale ?? NumCast(Doc.Layout(this.Document)[this.scaleFieldKey], 1); // , NumCast(DocCast(this.Document.rootDocument)?.[this.scaleFieldKey], 1));
+    zoomScaling = () => this.fitContentBounds?.scale ?? NumCast(this.Document[this.scaleFieldKey], 1); // , NumCast(DocCast(this.Document.rootDocument)?.[this.scaleFieldKey], 1));
     PanZoomCenterXf = () => (this._props.isAnnotationOverlay && this.zoomScaling() === 1 ? `` : `translate(${this.centeringShiftX}px, ${this.centeringShiftY}px) scale(${this.zoomScaling()}) translate(${-this.panX()}px, ${-this.panY()}px)`);
     ScreenToContentsXf = () => this.screenToFreeformContentsXf.copy();
     getActiveDocuments = () => this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => pair.layout);
@@ -440,7 +440,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             const dropPos = this.Document._currentFrame !== undefined ? [NumCast(dvals.x), NumCast(dvals.y)] : [NumCast(refDoc.x), NumCast(refDoc.y)];
 
             docDragData.droppedDocuments.forEach((d, i) => {
-                const layoutDoc = Doc.Layout(d);
+                const layoutDoc = d[DocLayout];
                 const delta = Utils.rotPt(x - dropPos[0], y - dropPos[1], fromScreenXf.Rotate);
                 if (this.Document._currentFrame !== undefined) {
                     CollectionFreeFormDocumentView.setupKeyframes([d], NumCast(this.Document._currentFrame), false);
@@ -1440,9 +1440,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     }
 
     calculatePanIntoView = (doc: Doc, xf: Transform, scale?: number) => {
-        const layoutdoc = Doc.Layout(doc);
         const pt = xf.transformPoint(NumCast(doc.x), NumCast(doc.y));
-        const pt2 = xf.transformPoint(NumCast(doc.x) + NumCast(layoutdoc._width), NumCast(doc.y) + NumCast(layoutdoc._height));
+        const pt2 = xf.transformPoint(NumCast(doc.x) + NumCast(doc._width), NumCast(doc.y) + NumCast(doc._height));
         const bounds = { left: pt[0], right: pt2[0], top: pt[1], bot: pt2[1], width: pt2[0] - pt[0], height: pt2[1] - pt[1] };
 
         if (scale !== undefined) {
@@ -1489,9 +1488,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
      * @returns whether the new text doc was created and added successfully
      */
     createTextDocCopy = undoable((textBox: FormattedTextBox, below: boolean) => {
-        const textDoc = DocCast(textBox.Document, textBox.Document);
+        const textDoc = DocCast(textBox.Document);
         const newDoc = Doc.MakeCopy(textDoc, true);
-        newDoc['$' + Doc.LayoutFieldKey(newDoc, textBox._props.LayoutTemplateString)] = undefined; // the copy should not copy the text contents of it source, just the render style
+        newDoc['$' + Doc.LayoutFieldKey(newDoc)] = undefined; // the copy should not copy the text contents of it source, just the render style
         newDoc.x = NumCast(textDoc.x) + (below ? 0 : NumCast(textDoc._width) + 10);
         newDoc.y = NumCast(textDoc.y) + (below ? NumCast(textDoc._height) + 10 : 0);
         DocumentView.SetSelectOnLoad(newDoc);
@@ -1600,14 +1599,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     getCalculatedPositions(pair: { layout: Doc; data?: Doc }): PoolData {
         const random = (min: number, max: number, x: number, y: number) => /* min should not be equal to max */ min + (((Math.abs(x * y) * 9301 + 49297) % 233280) / 233280) * (max - min);
         const childDoc = pair.layout;
-        const childDocLayout = Doc.Layout(childDoc);
         const layoutFrameNumber = Cast(this.Document._currentFrame, 'number'); // frame number that container is at which determines layout frame values
-        const contentFrameNumber = Cast(childDocLayout._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
+        const contentFrameNumber = Cast(childDoc._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
         const { z, zIndex } = childDoc;
         const { backgroundColor, color } = contentFrameNumber === undefined ? { backgroundColor: undefined, color: undefined } : CollectionFreeFormDocumentView.getStringValues(childDoc, contentFrameNumber);
         const { x, y, autoDim, _width, _height, opacity, _rotation } =
             layoutFrameNumber === undefined // -1 for width/height means width/height should be PanelWidth/PanelHeight (prevents collectionfreeformdocumentview width/height from getting out of synch with panelWIdth/Height which causes detailView to re-render and lose focus because HTMLtag scaling gets set to a bad intermediate value)
-                ? { autoDim: 1, _width: Cast(childDoc._width, 'number'), _height: Cast(childDoc._height, 'number'), _rotation: Cast(childDocLayout._rotation, 'number'), x: childDoc.x, y: childDoc.y, opacity: this._props.childOpacity?.() }
+                ? { autoDim: 1, _width: Cast(childDoc._width, 'number'), _height: Cast(childDoc._height, 'number'), _rotation: Cast(childDoc._rotation, 'number'), x: childDoc.x, y: childDoc.y, opacity: this._props.childOpacity?.() }
                 : CollectionFreeFormDocumentView.getValues(childDoc, layoutFrameNumber);
         // prettier-ignore
         const rotation = Cast(_rotation,'number',
@@ -1625,8 +1623,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             zIndex: Cast(zIndex, 'number'),
             width: _width,
             height: _height,
-            transition: StrCast(childDocLayout.dataTransition),
-            showTags: BoolCast(childDocLayout.showTags) || BoolCast(this.Document.showChildTags) || BoolCast(this.Document._layout_showTags),
+            transition: StrCast(childDoc.dataTransition),
+            showTags: BoolCast(childDoc.showTags) || BoolCast(this.Document.showChildTags) || BoolCast(this.Document._layout_showTags),
             pointerEvents: Cast(childDoc.pointerEvents, 'string', null),
             pair,
             replica: '',
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index eaa8826ed..ca7e7a311 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -591,8 +591,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
     marqueeSelect = (selectBackgrounds: boolean = false, docType: DocumentType | undefined = undefined) => {
         const selection: Doc[] = [];
         const selectFunc = (doc: Doc) => {
-            const layoutDoc = Doc.Layout(doc);
-            const bounds = { left: NumCast(doc.x), top: NumCast(doc.y), width: NumCast(layoutDoc._width), height: NumCast(layoutDoc._height) };
+            const bounds = { left: NumCast(doc.x), top: NumCast(doc.y), width: NumCast(doc._width), height: NumCast(doc._height) };
             if (!this._lassoFreehand) {
                 intersectRect(bounds, this.Bounds) && selection.push(doc);
             } else {
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 05670562e..5803acca0 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -200,7 +200,7 @@ export class CollectionSchemaView extends CollectionSubView() {
         this._props.setContentViewBox?.(this);
         document.addEventListener('keydown', this.onKeyDown);
 
-        Object.entries(this._documentOptions).forEach((pair: [string, FInfo]) => this.fieldInfos.set(pair[0], pair[1]));
+        Object.entries(this._documentOptions).forEach(pair => this.fieldInfos.set(pair[0], pair[1] as FInfo));
         this._keysDisposer = observe(
             this.dataDoc[this.fieldKey ?? 'data'] as List<Doc>,
             change => {
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 72838074e..173984dc7 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -32,6 +32,7 @@ import { FInfotoColType } from './CollectionSchemaView';
 import './CollectionSchemaView.scss';
 import { SchemaColumnHeader } from './SchemaColumnHeader';
 import { SchemaCellField } from './SchemaCellField';
+import { DocLayout } from '../../../../fields/DocSymbols';
 
 /**
  * SchemaTableCells make up the majority of the visual representation of the SchemaView.
@@ -104,7 +105,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
     public static renderProps(props: SchemaTableCellProps) {
         const { Doc: Document, fieldKey, /* getFinfo,*/ columnWidth, isRowActive } = props;
         let protoCount = 0;
-        const layoutDoc = fieldKey.startsWith('_') ? Doc.Layout(Document) : Document;
+        const layoutDoc = fieldKey.startsWith('_') ? Document[DocLayout] : Document;
         let doc = Document;
         while (doc) {
             if (Object.keys(doc).includes(fieldKey.replace(/^_/, ''))) break;
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 943d2175c..53a3d3631 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -17,9 +17,10 @@ import { ScriptingGlobals } from '../../util/ScriptingGlobals';
 import { DocComponent } from '../DocComponent';
 import { StyleProp } from '../StyleProp';
 import './CollectionFreeFormDocumentView.scss';
-import { DocumentView, DocumentViewProps } from './DocumentView';
+import { DocumentView } from './DocumentView';
 import { FieldViewProps } from './FieldView';
 import { OpenWhere } from './OpenWhere';
+import { DocumentViewProps } from './DocumentContentsView';
 
 export enum GroupActive { // flags for whether a view is activate because of its relationship to a group
     group = 'group', // this is a group that is activated because it's on an active canvas, but is not part of some other group
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 482af336c..6d5891003 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -29,7 +29,6 @@ import '../pdf/GPTPopup/GPTPopup.scss';
 import './ComparisonBox.scss';
 import { DocumentView } from './DocumentView';
 import { FieldView, FieldViewProps } from './FieldView';
-import { FormattedTextBox } from './formattedText/FormattedTextBox';
 import { TraceMobx } from '../../../fields/util';
 
 const API_URL = 'https://api.unsplash.com/search/photos';
@@ -285,13 +284,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
     };
 
     @action handleRenderGPTClick = () => {
-        const phonTrans = DocCast(this.Document.audio) ? DocCast(this.Document.audio).phoneticTranscription : undefined;
-        if (phonTrans) {
-            this._inputValue = StrCast(phonTrans);
-            this.askGPTPhonemes(this._inputValue);
-            this._renderSide = this.backKey;
-            this._outputValue = '';
-        } else if (this._inputValue) this.askGPT(GPTCallType.QUIZDOC);
+        if (this._inputValue) this.askGPT(GPTCallType.QUIZDOC);
     };
 
     onPointerMove = ({ movementX }: PointerEvent) => {
@@ -467,45 +460,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
         this._props.DocumentView?.()._props.addDocument?.(newAudio);
     };
 
-    /**
-     * Gets the transcription of an audio recording by sending the
-     * recording to backend.
-     */
-    pushInfo = () =>
-        axios
-            .post(
-                'http://localhost:105/recognize/', //
-                { file: DocCast(this.Document.audio).$url },
-                { headers: { 'Content-Type': 'application/json' } }
-            )
-            .then(response => {
-                this.Document.phoneticTranscription = response.data.transcription;
-            });
-
-    /**
-     * Extracts the id of the youtube video url.
-     * @param url
-     * @returns
-     */
-    getYouTubeVideoId = (url: string) => {
-        const regExp = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|&v=|\?v=)([^#&?]*).*/;
-        const match = url.match(regExp);
-        return match && match[2].length === 11 ? match[2] : null;
-    };
-
-    /**
-     * Gets the transcript of a youtube video by sending the video url to the backend.
-     * @returns transcription of youtube recording
-     */
-    youtubeUpload = async () =>
-        axios
-            .post(
-                'http://localhost:105/youtube/', //
-                { file: this.getYouTubeVideoId(this.frontText) },
-                { headers: { 'Content-Type': 'application/json' } }
-            )
-            .then(response => response.data.transcription);
-
     /**
      * Calls GPT for each flashcard type.
      */
@@ -539,45 +493,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
     layoutWidth = () => NumCast(this.layoutDoc.width, 200);
     layoutHeight = () => NumCast(this.layoutDoc.height, 200);
 
-    /**
-     * Ask GPT for advice on how to improve speech by comparing the phonetic transcription of
-     * a users audio recording with the phonetic transcription of their intended sentence.
-     * @param phonemes
-     */
-    askGPTPhonemes = async (phonemes: string) => {
-        const sentence = this.frontText;
-        const phon6 = 'huː ɑɹ juː tədeɪ';
-        const phon4 = 'kamo estas hɔi';
-        const promptEng =
-            'Consider all possible phonetic transcriptions of the intended sentence "' +
-            sentence +
-            '" that is standard in American speech without showing the user. Compare each word in the following phonemes with those phonetic transcriptions without displaying anything to the user: "' +
-            phon6 +
-            '". Steps to do this: Align the words with each word in the intended sentence by combining the phonemes to get a pronunciation that resembles the word in order. Do not describe phonetic corrections with the phonetic alphabet - describe it by providing other examples of how it should sound. Note if a word or sound missing, including missing vowels and consonants. If there is an additional word that does not match with the provided sentence, say so. For each word, if any letters mismatch and would sound weird in American speech and they are not allophones of the same phoneme and they are far away from each on the ipa vowel chat and that pronunciation is not normal for the meaning of the word, note this difference and explain how it is supposed to sound. Only note the difference if they are not allophones of the same phoneme and if they are far away on the vowel chart. The goal is to be understood, not sound like a native speaker. Just so you know, "i" sounds like "ee" as in "bee", not "ih" as an "lick". Interpret "ɹ" as the same as "r". Interpret "ʌ" as the same as "ə". If "ɚ", "ɔː", and "ɔ" are options for pronunciation, do not choose "ɚ". Ignore differences with colons. Ignore redundant letters and words and sounds and the splitting of words; do not mention this since there could be repeated words in the sentence. Provide a response like this: "Lets work on improving the pronunciation of "coffee." You said "ceeffee," which is close, but we need to adjust the vowel sound. In American English, "coffee" is pronounced /ˈkɔːfi/, with a long "aw" sound. Try saying "kah-fee." Your intonation is good, but try putting a bit more stress on "like" in the sentence "I would like a coffee with milk." This will make your speech sound more natural. Keep practicing, and lets try saying the whole sentence again!"';
-        const promptSpa =
-            'Consider all possible phonetic transcriptions of the intended sentence "' +
-            'como estás hoy' +
-            '" that is standard in Spanish speech without showing the user. Compare each word in the following phonemes with those phonetic transcriptions without displaying anything to the user: "' +
-            phon4 +
-            '". Steps to do this: Align the words with each word in the intended sentence by combining the phonemes to get a pronunciation that resembles the word in order. Do not describe phonetic corrections with the phonetic alphabet - describe it by providing other examples of how it should sound. Note if a word or sound missing, including missing vowels and consonants. If there is an additional word that does not match with the provided sentence, say so. For each word, if any letters mismatch and would sound weird in Spanish speech and they are not allophones of the same phoneme and they are far away from each on the ipa vowel chat and that pronunciation is not normal for the meaning of the word, note this difference and explain how it is supposed to sound.  Only note the difference if they are not allophones of the same phoneme and if they are far away on the vowel chart; say good job if it would be understood by a native Spanish speaker. Just so you know, "i" sounds like "ee" as in "bee", not "ih" as an "lick". Interpret "ɹ" as the same as "r". Interpret "ʌ" as the same as "ə". Do not make "θ" and "f" interchangable. Do not make "n" and "ɲ" interchangable. Do not make "e" and "i" interchangable. If "ɚ", "ɔː", and "ɔ" are options for pronunciation, do not choose "ɚ". Ignore differences with colons. Ignore redundant letters and words and sounds and the splitting of words; do not mention this since there could be repeated words in the sentence. Identify "ɔi" sounds like "oy". Ignore accents and do not say anything to the user about this.';
-        const promptAll =
-            'Consider all possible phonetic transcriptions of the intended sentence "' +
-            sentence +
-            '" that is standard in ' +
-            this.convertAbr() +
-            ' speech without showing the user. Compare each word in the following phonemes with those phonetic transcriptions without displaying anything to the user: "' +
-            phonemes +
-            '". Steps to do this: Align the words with each word in the intended sentence by combining the phonemes to get a pronunciation that resembles the word in order. Do not describe phonetic corrections with the phonetic alphabet - describe it by providing other examples of how it should sound. Note if a word or sound missing, including missing vowels and consonants. If there is an additional word that does not match with the provided sentence, say so. For each word, if any letters mismatch and would sound weird in ' +
-            this.convertAbr() +
-            ' speech and they are not allophones of the same phoneme and they are far away from each on the ipa vowel chat and that pronunciation is not normal for the meaning of the word, note this difference and explain how it is supposed to sound. Just so you know, "i" sounds like "ee" as in "bee", not "ih" as an "lick". Interpret "ɹ" as the same as "r". Interpret "ʌ" as the same as "ə". Do not make "θ" and "f" interchangable. Do not make "n" and "ɲ" interchangable. Do not make "e" and "i" interchangable. If "ɚ", "ɔː", and "ɔ" are options for pronunciation, do not choose "ɚ". Ignore differences with colons. Ignore redundant letters and words and sounds and the splitting of words; do not mention this since there could be repeated words in the sentence. Provide a response like this: "Lets work on improving the pronunciation of "coffee." You said "cawffee," which is close, but we need to adjust the vowel sound. In American English, "coffee" is pronounced /ˈkɔːfi/, with a long "aw" sound. Try saying "kah-fee." Your intonation is good, but try putting a bit more stress on "like" in the sentence "I would like a coffee with milk." This will make your speech sound more natural. Keep practicing, and lets try saying the whole sentence again!"';
-
-        switch (this._recognition.lang) {
-            case 'en-US': this._outputValue = await gptAPICall(promptEng, GPTCallType.PRONUNCIATION); break;
-            case 'es-ES': this._outputValue = await gptAPICall(promptSpa, GPTCallType.PRONUNCIATION); break;
-            default:      this._outputValue = await gptAPICall(promptAll, GPTCallType.PRONUNCIATION); break;
-        } // prettier-ignore
-    };
-
     /**
      * Display a user's speech to text result.
      * @param e
@@ -637,31 +552,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
         !appearance && ContextMenu.Instance.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'eye' });
     };
 
-    testForTextFields = (whichSlot: string) => {
-        const slotData = Doc.Get(this.dataDoc, whichSlot, true);
-        const slotHasText = slotData instanceof RichTextField || typeof slotData === 'string';
-        const subjectText = RTFCast(this.Document[this.fieldKey])?.Text.trim();
-        const altText = RTFCast(this.Document[this.fieldKey + '_alternate'])?.Text.trim();
-        const layoutTemplateString =
-            slotHasText ? FormattedTextBox.LayoutString(whichSlot):
-            whichSlot === this.frontKey ? (subjectText !== undefined ? FormattedTextBox.LayoutString(this.fieldKey) : undefined) :
-                                          altText !== undefined     ? FormattedTextBox.LayoutString(this.fieldKey + '_alternate'): undefined; // prettier-ignore
-
-        // A bit hacky to try out the concept of using GPT to fill in flashcards
-        //   If the second slot doesn't have anything in it, but the fieldKey slot has text (e.g., this.text is a string)
-        // and the fieldKey + "_alternate" has text that includes a GPT query (indicated by (( && )) ) that is parameterized (optionally) by the fieldKey text (this) or other metadata (this.<field>).
-        // eg., this.text_alternate is
-        //     "((Provide a one sentence definition for (this) that doesn't use any word in (this.excludeWords) ))"
-        //     where (this) is replaced by the text in the fieldKey slot abd this.excludeWords is repalced by the conetnts of the excludeWords field
-        // The GPT call will put the "answer"  in the second slot of the comparison (eg., text_0)
-        if (whichSlot === this.backKey && !layoutTemplateString?.includes(whichSlot)) {
-            const queryText = altText?.replace('(this)', subjectText); // TODO: this should be done in Doc.setField but it doesn't know about the fieldKey ...
-            if (queryText?.match(/\(\(.*\)\)/)) {
-                Doc.SetField(this.Document, whichSlot, ':=' + queryText); // make the second slot be a computed field on the data doc that calls ChatGpt
-            }
-        }
-        return layoutTemplateString;
-    };
     childActiveFunc = () => this._childActive;
 
     contentScreenToLocalXf = () => this._props.ScreenToLocalTransform().scale(this._props.NativeDimScaling?.() || 1);
@@ -682,24 +572,21 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
     displayDoc = (whichSlot: string) => {
         const whichDoc = DocCast(this.dataDoc[whichSlot]);
         const targetDoc = DocCast(whichDoc?.annotationOn, whichDoc);
-        const layoutString = targetDoc ? '' : this.testForTextFields(whichSlot);
 
-        return targetDoc || layoutString ? (
+        return targetDoc ? (
             <>
                 <DocumentView
                     {...this._props}
-                    Document={layoutString ? this.Document : targetDoc}
+                    Document={targetDoc}
                     NativeWidth={returnZero}
                     NativeHeight={returnZero}
                     renderDepth={this.props.renderDepth + 1}
-                    LayoutTemplateString={layoutString}
                     containerViewPath={this._props.docViewPath}
                     ScreenToLocalTransform={this.contentScreenToLocalXf}
                     isDocumentActive={returnFalse}
                     isContentActive={this.childActiveFunc}
                     showTags={undefined}
                     fitWidth={this.childFitWidth} // set to returnTrue to make images fill the comparisonBox-- should be a user option
-                    ignoreUsePath={layoutString ? true : undefined}
                     moveDocument={whichSlot === this.frontKey ? this.moveDocFront : this.moveDocBack}
                     removeDocument={whichSlot === this.frontKey ? this.remDocFront : this.remDocBack}
                     dontSelect={returnTrue}
@@ -745,18 +632,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
             </div>
             <div>
                 <div className="submit-button">
-                    {/* <div className="submit-buttonschema-header-button" onPointerDown={e => this.openContextMenu(e.clientX, e.clientY, false)}>
-                        <FontAwesomeIcon color="white" icon="caret-down" />
-                    </div> */}
-                    {/* <button className="submit-buttonrecord" onClick={this._listening ? this.stopListening : this.startListening} style={{ background: this._listening ? 'lightgray' : '' }}>
-                        {<FontAwesomeIcon icon="microphone" size="lg" />}
-                    </button> */}
-                    {/* <div className="submit-buttonschema-header-button" onPointerDown={e => this.openContextMenu(e.clientX, e.clientY, true)} style={{ left: '50px', zIndex: '100' }}>
-                        <FontAwesomeIcon color="white" icon="caret-down" />
-                    </div> */}
-                    {/* <button className="submit-buttonpronunciation" onClick={this.evaluatePronunciation}>
-                        Evaluate Pronunciation
-                    </button> */}
                     <button className="submit-buttonsubmit" type="button" onClick={this._renderSide === this.backKey ? () => this.animateFlipping(this.frontKey) : this.handleRenderGPTClick}>
                         {this._renderSide === this.backKey ? 'Redo the Question' : 'Submit'}
                     </button>
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index d1eae1784..6f004bed3 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -11,10 +11,85 @@ import { Cast, DocCast, StrCast } from '../../../fields/Types';
 import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
 import { ObservableReactComponent, ObserverJsxParser } from '../ObservableReactComponent';
 import './DocumentView.scss';
-import { FieldViewProps } from './FieldView';
+import { FieldViewProps, FieldViewSharedProps } from './FieldView';
+import { DragManager } from '../../util/DragManager';
+import { dropActionType } from '../../util/DropActionTypes';
+import { Property } from 'csstype';
+
+interface DocOnlyProps {
+    LayoutTemplate?: () => Opt<Doc>;
+    LayoutTemplateString?: string;
+    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
+    hideDecorationTitle?: boolean; // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings
+    hideDocumentButtonBar?: boolean;
+    hideOpenButton?: boolean;
+    hideDeleteButton?: boolean;
+    hideLinkAnchors?: boolean;
+    hideLinkButton?: boolean;
+    hideCaptions?: boolean;
+    contentPointerEvents?: Property.PointerEvents | undefined; // pointer events allowed for content of a document view.  eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents
+    dontCenter?: 'x' | 'y' | 'xy';
+    showTags?: boolean;
+    showAIEditor?: boolean;
+    hideFilterStatus?: 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.
+    dragWhenActive?: boolean;
+    dontHideOnDrag?: boolean;
+    onClickScriptDisable?: 'never' | 'always'; // undefined = only when selected
+    DataTransition?: () => string | undefined;
+    NativeWidth?: () => number;
+    NativeHeight?: () => number;
+    contextMenuItems?: () => { script?: ScriptField; method?: () => void; filter?: ScriptField; label: string; icon: string }[];
+    dragConfig?: (data: DragManager.DocumentDragData) => void;
+    dragStarting?: () => void;
+    dragEnding?: () => void;
+
+    reactParent?: React.Component; // parent React component view (see CollectionFreeFormDocumentView)
+}
+const DocOnlyProps = [
+    'layoutFieldKey',
+    'LayoutTemplate',
+    'LayoutTemplateString',
+    'hideDecorations', // whether to suppress all DocumentDecorations when doc is selected
+    'hideResizeHandles', // whether to suppress resized handles on doc decorations when this document is selected
+    'hideTitle', // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings
+    'hideDecorationTitle', // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings
+    'hideDocumentButtonBar',
+    'hideOpenButton',
+    'hideDeleteButton',
+    'hideLinkAnchors',
+    'hideLinkButton',
+    'hideCaptions',
+    'contentPointerEvents', // pointer events allowed for content of a document view.  eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents
+    'dontCenter',
+    'showTags',
+    'showAIEditor',
+    'hideFilterStatus',
+    'childHideDecorationTitle',
+    'childHideResizeHandles',
+    'childDragAction', // allows child documents to be dragged out of collection without holding the embedKey or dragging the doc decorations title bar.
+    'dragWhenActive',
+    'dontHideOnDrag',
+    'onClickScriptDisable', // undefined = only when selected
+    'DataTransition',
+    'NativeWidth',
+    'NativeHeight',
+    'contextMenuItems',
+    'dragConfig',
+    'dragStarting',
+    'dragEnding',
+
+    'reactParent', // parent React component view (see CollectionFreeFormDocumentView)
+];
+
+export interface DocumentViewProps extends DocOnlyProps, FieldViewSharedProps {}
 
 type BindingProps = Without<FieldViewProps, 'fieldKey'>;
-export interface JsxBindings {
+interface JsxBindings {
     props: BindingProps;
 }
 
@@ -77,7 +152,7 @@ export class HTMLtag extends React.Component<HTMLtagProps> {
     }
 }
 
-export interface DocumentContentsViewProps extends FieldViewProps {
+interface DocumentContentsViewProps extends DocumentViewProps, FieldViewProps {
     layoutFieldKey: string;
 }
 @observer
@@ -118,24 +193,6 @@ export class DocumentContentsView extends ObservableReactComponent<DocumentConte
     }
 
     CreateBindings(onClick: Opt<ScriptField>, onInput: Opt<ScriptField>): JsxBindings {
-        const docOnlyProps = [
-            // these are the properties in DocumentViewProps that need to be removed to pass on only DocumentSharedViewProps to the FieldViews
-            'hideResizeHandles',
-            'hideTitle',
-            'bringToFront',
-            'childContentPointerEvents',
-            'LayoutTemplateString',
-            'LayoutTemplate',
-            'showTags',
-            'layoutFieldKey',
-            'dontCenter',
-            'DataTransition',
-            'contextMenuItems',
-            // 'onClick', // don't need to omit this since it will be set
-            'onDoubleClickScript',
-            'onPointerDownScript',
-            'onPointerUpScript',
-        ];
         const templateDataDoc = this._props.TemplateDataDocument ?? (this.layoutDoc !== this._props.Document ? this._props.Document[DocData] : undefined);
         const list: BindingProps & React.DetailedHTMLProps<React.HtmlHTMLAttributes<HTMLDivElement>, HTMLDivElement> = {
             ...this._props,
@@ -146,7 +203,7 @@ export class DocumentContentsView extends ObservableReactComponent<DocumentConte
         };
         return {
             props: {
-                ...OmitKeys(list, [...docOnlyProps], '').omit,
+                ...OmitKeys(list, DocOnlyProps, '').omit,
             } as BindingProps,
         };
     }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index eae3454ba..c3026d7be 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -43,10 +43,10 @@ import { StyleProp } from '../StyleProp';
 import { TagsView } from '../TagsView';
 import { ViewBoxInterface } from '../ViewBoxInterface';
 import { GroupActive } from './CollectionFreeFormDocumentView';
-import { DocumentContentsView } from './DocumentContentsView';
+import { DocumentContentsView, DocumentViewProps } from './DocumentContentsView';
 import { DocumentLinksButton } from './DocumentLinksButton';
 import './DocumentView.scss';
-import { FieldViewProps, FieldViewSharedProps } from './FieldView';
+import { FieldViewProps } from './FieldView';
 import { FocusViewOptions } from './FocusViewOptions';
 import { OpenWhere, OpenWhereMod } from './OpenWhere';
 import { FormattedTextBox } from './formattedText/FormattedTextBox';
@@ -54,39 +54,8 @@ import { PresEffect, PresEffectDirection } from './trails/PresEnums';
 import SpringAnimation from './trails/SlideEffect';
 import { SpringType, springMappings } from './trails/SpringUtils';
 
-export interface DocumentViewProps extends FieldViewSharedProps {
-    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
-    hideDecorationTitle?: boolean; // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings
-    hideDocumentButtonBar?: boolean;
-    hideOpenButton?: boolean;
-    hideDeleteButton?: boolean;
-    hideLinkAnchors?: boolean;
-    hideLinkButton?: boolean;
-    hideCaptions?: boolean;
-    contentPointerEvents?: Property.PointerEvents | undefined; // pointer events allowed for content of a document view.  eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents
-    dontCenter?: 'x' | 'y' | 'xy';
-    showTags?: boolean;
-    hideFilterStatus?: 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.
-    dragWhenActive?: boolean;
-    dontHideOnDrag?: boolean;
-    onClickScriptDisable?: 'never' | 'always'; // undefined = only when selected
-    DataTransition?: () => string | undefined;
-    NativeWidth?: () => number;
-    NativeHeight?: () => number;
-    contextMenuItems?: () => { script?: ScriptField; method?: () => void; filter?: ScriptField; label: string; icon: string }[];
-    dragConfig?: (data: DragManager.DocumentDragData) => void;
-    dragStarting?: () => void;
-    dragEnding?: () => void;
-
-    reactParent?: React.Component; // parent React component view (see CollectionFreeFormDocumentView)
-}
 @observer
-export class DocumentViewInternal extends DocComponent<FieldViewProps & DocumentViewProps & { showAIEditor: boolean }>() {
+export class DocumentViewInternal extends DocComponent<DocumentViewProps & FieldViewProps>() {
     // this makes mobx trace() statements more descriptive
     public get displayName()  { return 'DocumentViewInternal(' + this.Document.title + ')'; } // prettier-ignore
     public static SelectAfterContextMenu = true; //  whether a document should be selected after it's contextmenu is triggered.
@@ -809,7 +778,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
                         </div>
                     </>
                 )}
-                {this.widgetDecorations ?? null}
+                <div style={{ transformOrigin: 'top right', transform: `scale(${this.uiBtnScaling})`, position: 'absolute', top: 0, right: 0 }}>{this.widgetDecorations ?? null}</div>
             </>
         );
     }
@@ -1271,9 +1240,8 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
     public get ContentDiv()     { return this._docViewInternal?._contentDiv; } // prettier-ignore
     public get ComponentView()  { return this._docViewInternal?._componentView; } // prettier-ignore
     public get allLinks()       { return this._docViewInternal?._allLinks ?? []; } // prettier-ignore
-    public get TagBtnHeight() {
-        return this._docViewInternal?.TagsBtnHeight;
-    }
+    public get TagBtnHeight()   { return this._docViewInternal?.TagsBtnHeight; } // prettier-ignore
+    public get UIBtnScaling()   { return this._docViewInternal?.uiBtnScaling; } // prettier-ignore
 
     get LayoutFieldKey() {
         return Doc.LayoutFieldKey(this.Document, this._props.LayoutTemplateString);
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 933868c46..74059bd12 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -48,8 +48,6 @@ export type StyleProviderFuncType = (
 export interface FieldViewSharedProps {
     Document: Doc;
     TemplateDataDocument?: Doc;
-    LayoutTemplateString?: string;
-    LayoutTemplate?: () => Opt<Doc>;
     renderDepth: number;
     scriptContext?: unknown; // can be assigned anything and will be passed as 'scriptContext' to any OnClick script that executes on this document
     screenXPadding?: () => number; // padding in screen space coordinates (used by text box to reflow around UI buttons in carouselView)
@@ -62,7 +60,8 @@ export interface FieldViewSharedProps {
     ignoreAutoHeight?: boolean;
     disableBrushing?: boolean; // should highlighting for this view be disabled when same document in another view is hovered over.
     hideClickBehaviors?: boolean; // whether to suppress menu item options for changing click behaviors
-    ignoreUsePath?: boolean; // ignore the usePath field for selecting the fieldKey (eg., on text docs)
+    suppressSetHeight?: boolean;
+    dontCenter?: 'x' | 'y' | 'xy' | undefined;
     LocalRotation?: () => number | undefined; // amount of rotation applied to freeformdocumentview containing document view
     containerViewPath?: () => DocumentView[];
     fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _freeform_fitContentsToBox property on a Document
@@ -86,7 +85,6 @@ export interface FieldViewSharedProps {
     onPointerUpScript?: () => ScriptField;
     onKey?: (e: KeyboardEvent, textBox: FormattedTextBox) => boolean | undefined;
     fitWidth?: (doc: Doc) => boolean | undefined;
-    dontCenter?: 'x' | 'y' | 'xy' | undefined;
     searchFilterDocs: () => Doc[];
     showTitle?: () => string;
     whenChildContentsActiveChanged: (isActive: boolean) => void;
@@ -102,7 +100,6 @@ export interface FieldViewSharedProps {
     waitForDoubleClickToClick?: () => 'never' | 'always' | undefined;
     defaultDoubleClick?: () => 'default' | 'ignore' | undefined;
     pointerEvents?: () => Opt<Property.PointerEvents>;
-    suppressSetHeight?: boolean;
 }
 
 /**
@@ -110,13 +107,13 @@ export interface FieldViewSharedProps {
  *  */
 export interface FieldViewProps extends FieldViewSharedProps {
     DocumentView?: () => DocumentView;
-    fieldKey: string;
     isSelected: () => boolean;
     select: (ctrlPressed: boolean, shiftPress?: boolean) => void;
     docViewPath: () => DocumentView[];
     setHeight?: (height: number) => void;
     NativeDimScaling?: () => number; // scaling the DocumentView does to transform its contents into its panel & needed by ScreenToLocal
     isHovering?: () => boolean;
+    fieldKey: string;
 
     // properties intended to be used from within layout strings (otherwise use the function equivalents that work more efficiently with React)
     // See currentUserUtils headerTemplate for examples of creating text boxes from html which set some of these fields
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index d89fe0bd4..030473be4 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -47,6 +47,7 @@ import './ImageBox.scss';
 import { OpenWhere } from './OpenWhere';
 import { RichTextField } from '../../../fields/RichTextField';
 
+const DefaultPath = '/assets/unknown-file-icon-hi.png';
 export class ImageEditorData {
     // eslint-disable-next-line no-use-before-define
     private static _instance: ImageEditorData;
@@ -426,7 +427,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
         const lower = url.href.toLowerCase();
         if (url.protocol === 'data') return url.href;
         if (url.href.indexOf(window.location.origin) === -1 && url.href.indexOf('dashblobstore') === -1) return ClientUtils.CorsProxy(url.href);
-        if (!/\.(png|jpg|jpeg|gif|webp)$/.test(lower) || lower.endsWith('/assets/unknown-file-icon-hi.png')) return `/assets/unknown-file-icon-hi.png`;
+        if (!/\.(png|jpg|jpeg|gif|webp)$/.test(lower) || lower.endsWith(DefaultPath)) return DefaultPath;
 
         const ext = extname(url.href);
         return url.href.replace(ext, (this._error ? '_o' : this._curSuffix) + ext);
@@ -440,7 +441,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
 
     @computed get nativeSize() {
         TraceMobx();
-        if (this.paths.length && this.paths[0].includes('icon-hi')) return { nativeWidth: NumCast(this.layoutDoc._width), nativeHeight: NumCast(this.layoutDoc._height), nativeOrientation: 0 };
+        if (this.paths.length && this.paths[0].includes(DefaultPath)) return { nativeWidth: NumCast(this.layoutDoc._width), nativeHeight: NumCast(this.layoutDoc._height), nativeOrientation: 0 };
         const nativeWidth = NumCast(this.dataDoc[this.fieldKey + '_nativeWidth'], NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth'], 500));
         const nativeHeight = NumCast(this.dataDoc[this.fieldKey + '_nativeHeight'], NumCast(this.layoutDoc[this.fieldKey + '_nativeHeight'], 500));
         const nativeOrientation = NumCast(this.dataDoc[this.fieldKey + '_nativeOrientation'], 1);
@@ -527,7 +528,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     @computed get paths() {
         const field = this.dataDoc[this.fieldKey] instanceof ImageField ? Cast(this.dataDoc[this.fieldKey], ImageField, null) : new ImageField(String(this.dataDoc[this.fieldKey])); // retrieve the primary image URL that is being rendered from the data doc
         const alts = DocListCast(this.dataDoc[this.fieldKey + '_alternates']); // retrieve alternate documents that may be rendered as alternate images
-        const defaultUrl = new URL(ClientUtils.prepend('/assets/unknown-file-icon-hi.png'));
+        const defaultUrl = new URL(ClientUtils.prepend(DefaultPath));
         const altpaths =
             alts
                 ?.map(doc => (doc instanceof Doc ? (ImageCast(doc[Doc.LayoutFieldKey(doc)])?.url ?? defaultUrl) : defaultUrl))
@@ -703,21 +704,24 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     }
     screenToLocalTransform = () => this.ScreenToLocalBoxXf().translate(0, NumCast(this.layoutDoc._layout_scrollTop) * this.ScreenToLocalBoxXf().Scale);
     marqueeDown = (e: React.PointerEvent) => {
-        if (!this.dataDoc[this.fieldKey]) {
-            this.chooseImage();
-        } else if (!e.altKey && e.button === 0 && NumCast(this.layoutDoc._freeform_scale, 1) <= NumCast(this.dataDoc.freeform_scaleMin, 1) && this._props.isContentActive() && Doc.ActiveTool !== InkTool.Ink) {
-            setupMoveUpEvents(
-                this,
-                e,
-                action(moveEv => {
-                    MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
-                    this.marqueeref.current?.onInitiateSelection([moveEv.clientX, moveEv.clientY]);
-                    return true;
-                }),
-                returnFalse,
-                () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations),
-                false
-            );
+        if (!e.altKey && e.button === 0) {
+            if (NumCast(this.layoutDoc._freeform_scale, 1) <= NumCast(this.dataDoc.freeform_scaleMin, 1) && this._props.isContentActive() && Doc.ActiveTool !== InkTool.Ink) {
+                setupMoveUpEvents(
+                    this,
+                    e,
+                    action(moveEv => {
+                        MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
+                        this.marqueeref.current?.onInitiateSelection([moveEv.clientX, moveEv.clientY]);
+                        return true;
+                    }),
+                    returnFalse,
+                    () => {
+                        if (!this.dataDoc[this.fieldKey]) this.chooseImage();
+                        else MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
+                    },
+                    false
+                );
+            }
         }
     };
     @action
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index bb711a5fc..be897b3f3 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -22,6 +22,7 @@ import './KeyValueBox.scss';
 import { KeyValuePair } from './KeyValuePair';
 import { OpenWhere } from './OpenWhere';
 import { FormattedTextBox } from './formattedText/FormattedTextBox';
+import { DocLayout } from '../../../fields/DocSymbols';
 
 export type KVPScript = {
     script: CompiledScript;
@@ -125,7 +126,7 @@ export class KeyValueBox extends ViewBoxBaseComponent<FieldViewProps>() {
     public static SetField = undoable((doc: Doc, key: string, value: string, forceOnDelegateIn?: boolean, setResult?: (value: FieldResult) => void) => {
         const script = KeyValueBox.CompileKVPScript(value);
         if (!script) return false;
-        const ldoc = key.startsWith('_') ? Doc.Layout(doc) : doc;
+        const ldoc = key.startsWith('_') ? doc[DocLayout] : doc;
         const forceOnDelegate = forceOnDelegateIn || (ldoc !== doc && !value.startsWith('='));
         // an '=' value redirects a key targeting the render template to the root document.
         // also, if ldoc and doc are equal, allow redirecting to data document when not using an equal
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index e3c481a75..c9e0aea5a 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -16,6 +16,7 @@ import { DefaultStyleProvider, returnEmptyDocViewList } from '../StyleProvider';
 import './KeyValueBox.scss';
 import './KeyValuePair.scss';
 import { OpenWhere } from './OpenWhere';
+import { DocLayout } from '../../../fields/DocSymbols';
 
 // Represents one row in a key value plane
 
@@ -60,7 +61,7 @@ export class KeyValuePair extends ObservableReactComponent<KeyValuePairProps> {
     };
 
     render() {
-        let doc = this._props.keyName.startsWith('_') ? Doc.Layout(this._props.doc) : this._props.doc;
+        let doc = this._props.keyName.startsWith('_') ? this._props.doc[DocLayout] : this._props.doc;
         const layoutField = doc !== this._props.doc;
         const key = layoutField ? this._props.keyName.replace(/^_/, '') : this._props.keyName;
         let protoCount = 0;
diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss
index f2160feb7..eaea272dc 100644
--- a/src/client/views/nodes/PDFBox.scss
+++ b/src/client/views/nodes/PDFBox.scss
@@ -23,9 +23,9 @@
         // glr: This should really be the same component as text and PDFs
         .pdfBox-sidebarBtn {
             background: global.$black;
-            height: 25px;
-            width: 25px;
-            right: 5px;
+            height: 20px;
+            width: 20px;
+            right: 0px;
             color: global.$white;
             display: flex;
             position: absolute;
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 78ddafa88..36d260fb9 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -499,8 +499,10 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                 title="Toggle Sidebar"
                 style={{
                     display: !this._props.isContentActive() ? 'none' : undefined,
-                    top: StrCast(this.layoutDoc._layout_showTitle) === 'title' ? 20 : 5,
+                    top: StrCast(this.layoutDoc._layout_showTitle) === 'title' ? 20 : 0,
                     backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
+                    transformOrigin: 'top right',
+                    transform: `scale(${this._props.DocumentView?.().UIBtnScaling || 1})`,
                 }}
                 onPointerDown={e => this.sidebarBtnDown(e, true)}>
                 <FontAwesomeIcon style={{ color: Colors.WHITE }} icon="comment-alt" size="sm" />
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index f9de4ab5a..547a2efa8 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -141,8 +141,8 @@ audiotag:hover {
     position: absolute;
     top: 0;
     right: 0;
-    width: 17px;
-    height: 17px;
+    width: 20px;
+    height: 20px;
     font-size: 11px;
     border-radius: 3px;
     color: white;
@@ -153,6 +153,7 @@ audiotag:hover {
     align-items: center;
     cursor: grabbing;
     box-shadow: global.$standard-box-shadow;
+    transform-origin: top right;
     // transition: 0.2s;
     opacity: 0.3;
     &:hover {
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 416cd577e..51b9a4333 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1941,6 +1941,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                     backgroundColor,
                     color,
                     opacity: annotated ? 1 : undefined,
+                    transform: `scale(${this._props.DocumentView?.().UIBtnScaling ?? 1})`,
                 }}>
                 <FontAwesomeIcon icon="comment-alt" />
             </div>
@@ -2050,7 +2051,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         return this._fieldKey;
     }
     @computed get _fieldKey() {
-        const usePath = this._props.ignoreUsePath ? '' : StrCast(this.layoutDoc[`${this._props.fieldKey}_usePath`]);
+        const usePath = StrCast(this.layoutDoc[`${this._props.fieldKey}_usePath`]);
         return this._props.fieldKey + (usePath && (!usePath.includes(':hover') || this._props.isHovering?.() || this._props.isContentActive()) ? `_${usePath.replace(':hover', '')}` : '');
     }
     onPassiveWheel = (e: WheelEvent) => {
diff --git a/src/client/views/nodes/importBox/ImportElementBox.tsx b/src/client/views/nodes/importBox/ImportElementBox.tsx
index 317719032..7e470a642 100644
--- a/src/client/views/nodes/importBox/ImportElementBox.tsx
+++ b/src/client/views/nodes/importBox/ImportElementBox.tsx
@@ -22,9 +22,7 @@ export class ImportElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return (
             <div style={{ backgroundColor: 'pink' }}>
                 <DocumentView
-                    // eslint-disable-next-line react/jsx-props-no-spreading
                     {...this._props} //
-                    LayoutTemplateString={undefined}
                     Document={this.Document}
                     isContentActive={returnFalse}
                     addDocument={returnFalse}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 73c2f5eb8..ed2f661e6 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -30,6 +30,7 @@ import { AnchorMenu } from './AnchorMenu';
 import { Annotation } from './Annotation';
 import { GPTPopup } from './GPTPopup/GPTPopup';
 import './PDFViewer.scss';
+import { DocumentViewProps } from '../nodes/DocumentContentsView';
 if (window?.Worker) GlobalWorkerOptions.workerSrc = 'files/pdf.worker.min.mjs';
 export * from 'pdfjs-dist/build/pdf.mjs';
 
@@ -521,7 +522,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
     panelHeight = () => this._props.PanelHeight() / (this._props.NativeDimScaling?.() || 1);
     transparentFilter = () => [...this._props.childFilters(), ClientUtils.TransparentBackgroundFilter];
     opaqueFilter = () => [...this._props.childFilters(), ClientUtils.noDragDocsFilter, ...(SnappingManager.CanEmbed && this._props.isContentActive() ? [] : [ClientUtils.OpaqueBackgroundFilter])];
-    childStyleProvider = (doc: Doc | undefined, props: Opt<FieldViewProps>, property: string) => {
+    childStyleProvider = (doc: Doc | undefined, props: Opt<FieldViewProps & DocumentViewProps>, property: string) => {
         if (doc instanceof Doc && property === StyleProp.PointerEvents) {
             if (this.inlineTextAnnotations.includes(doc) || this._props.isContentActive() === false) return 'none';
             const isInk = doc.layout_isSvg && !props?.LayoutTemplateString;
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 65719374f..734af4ccd 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -64,7 +64,7 @@ export namespace Field {
                       .trim()
                       .replace(/^new List\((.*)\)$/, '$1');
         };
-        const notOnTemplate = !key.startsWith('_') || Doc.Layout(doc) === doc;
+        const notOnTemplate = !key.startsWith('_') || doc[DocLayout] === doc;
         const isOnDelegate = notOnTemplate && !Doc.IsDataProto(doc) && ((key.startsWith('_') && !Field.IsField(cfield)) || Object.keys(doc).includes(key.replace(/^_/, '')));
         return (isOnDelegate ? '=' : '') + (!Field.IsField(cfield) ? '' : valFunc(cfield));
     }
@@ -407,9 +407,9 @@ export class Doc extends RefField {
     }
     @computed get __DATA__(): Doc {
         const self = this[SelfProxy];
-        return self.rootDocument && !self.isTemplateForField ? self : Doc.GetProto(Cast(Doc.Layout(self).rootDocument, Doc, null) || self);
+        return self.rootDocument && !self.isTemplateForField ? self : Doc.GetProto(Cast(self[DocLayout].rootDocument, Doc, null) || self);
     }
-    @computed get __LAYOUT__(): Doc | undefined {
+    @computed get __LAYOUT__(): Doc {
         const self = this[SelfProxy];
         const templateLayoutDoc = Cast(Doc.LayoutField(self), Doc, null);
         if (templateLayoutDoc) {
@@ -422,7 +422,7 @@ export class Doc extends RefField {
             }
             return Cast(self['layout_' + templateLayoutDoc.title + '(' + renderFieldKey + ')'], Doc, null) || templateLayoutDoc;
         }
-        return undefined;
+        return self;
     }
 
     public async [HandleUpdate](diff: { $set: { [key: string]: FieldType } } | { $unset?: unknown }) {
@@ -687,15 +687,11 @@ export namespace Doc {
     }
 
     export function MakeEmbedding(doc: Doc, id?: string) {
-        const embedding = (!GetT(doc, 'isDataDoc', 'boolean', true) && doc.proto) || doc.type === DocumentType.CONFIG ? Doc.MakeCopy(doc, undefined, id) : Doc.MakeDelegate(doc, id);
-        const layout = Doc.LayoutField(embedding);
-        if (layout instanceof Doc && layout !== embedding && layout === Doc.Layout(embedding)) {
-            Doc.SetLayout(embedding, Doc.MakeEmbedding(layout));
-        }
+        const embedding = (!Doc.IsDataProto(doc) && doc.proto) || doc.type === DocumentType.CONFIG ? Doc.MakeCopy(doc, undefined, id) : Doc.MakeDelegate(doc, id);
         embedding.createdFrom = doc;
-        embedding.proto_embeddingId = doc.$proto_embeddingId = Doc.GetEmbeddings(doc).length - 1;
-        !Doc.GetT(embedding, 'title', 'string', true) && (embedding.title = ComputedField.MakeFunction(`renameEmbedding(this)`));
         embedding.author = ClientUtils.CurrentUserEmail();
+        embedding.proto_embeddingId = doc.$proto_embeddingId = Doc.GetEmbeddings(doc).length - 1;
+        embedding.title = ComputedField.MakeFunction(`renameEmbedding(this)`);
 
         return embedding;
     }
@@ -768,9 +764,8 @@ export namespace Doc {
                         // prune doc and do nothing
                     } else if (
                         !Doc.IsSystem(docAtKey) &&
-                        (key.includes('layout[') ||
-                            key.startsWith('layout') || //
-                            ['embedContainer', 'annotationOn', 'proto'].includes(key) ||
+                        (key.startsWith('layout') ||
+                            ['embedContainer', 'annotationOn', 'proto'].includes(key) || //
                             (['link_anchor_1', 'link_anchor_2'].includes(key) && doc.author === ClientUtils.CurrentUserEmail()))
                     ) {
                         assignKey(await Doc.makeClone(docAtKey, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates));
@@ -1117,10 +1112,10 @@ export namespace Doc {
             Doc.GetProto(templateField)[metadataFieldKey] = ObjectField.MakeCopy(templateFieldValue);
         }
         // get the layout string that the template uses to specify its layout
-        const templateFieldLayoutString = StrCast(Doc.LayoutField(Doc.Layout(templateField)));
+        const templateFieldLayoutString = StrCast(Doc.LayoutField(templateField[DocLayout]));
 
         // change it to render the target metadata field instead of what it was rendering before and assign it to the template field layout document.
-        Doc.Layout(templateField).layout = templateFieldLayoutString.replace(/fieldKey={'[^']*'}/, `fieldKey={'${metadataFieldKey}'}`);
+        templateField[DocLayout].layout = templateFieldLayoutString.replace(/fieldKey={'[^']*'}/, `fieldKey={'${metadataFieldKey}'}`);
 
         return true;
     }
@@ -1157,16 +1152,13 @@ export namespace Doc {
     // a layout field or 'layout' is given.
     export function Layout(doc: Doc, template?: Doc): Doc {
         const expandedTemplate = template && Cast(doc['layout_' + template.title + '(' + StrCast(template.isTemplateForField, 'data') + ')'], Doc, null);
-        return expandedTemplate || doc[DocLayout] || doc;
-    }
-    export function SetLayout(doc: Doc, layout: Doc | string) {
-        doc[StrCast(doc.layout_fieldKey, 'layout')] = layout;
+        return expandedTemplate || doc[DocLayout];
     }
     export function LayoutField(doc: Doc) {
         return doc[StrCast(doc.layout_fieldKey, 'layout')];
     }
     export function LayoutFieldKey(doc: Doc, templateLayoutString?: string): string {
-        const match = StrCast(templateLayoutString || Doc.Layout(doc).layout).match(/fieldKey={'([^']+)'}/);
+        const match = StrCast(templateLayoutString || doc[DocLayout].layout).match(/fieldKey={'([^']+)'}/);
         return match?.[1] || ''; // bcz: TODO check on this .  used to always reference 'layout', now it uses the layout speicfied by the current layout_fieldKey
     }
     export function NativeAspect(doc: Doc, dataDoc?: Doc, useDim?: boolean) {
@@ -1325,17 +1317,7 @@ export namespace Doc {
     }
 
     export function getDocTemplate(doc?: Doc) {
-        return !doc
-            ? undefined
-            : doc.isTemplateDoc
-              ? doc
-              : Cast(doc.dragFactory, Doc, null)?.isTemplateDoc
-                ? doc.dragFactory
-                : Cast(Doc.Layout(doc), Doc, null)?.isTemplateDoc
-                  ? Cast(Doc.Layout(doc), Doc, null).rootDocument
-                      ? Doc.Layout(doc).proto
-                      : Doc.Layout(doc)
-                  : undefined;
+        return !doc ? undefined : doc.isTemplateDoc ? doc : Cast(doc.dragFactory, Doc, null)?.isTemplateDoc ? doc.dragFactory : doc[DocLayout].isTemplateDoc ? (doc[DocLayout].rootDocument ? doc[DocLayout].proto : doc[DocLayout]) : undefined;
     }
 
     export function toggleLockedPosition(doc: Doc) {
@@ -1737,7 +1719,7 @@ ScriptingGlobals.add(function idToDoc(id: string): Doc {
 });
 // eslint-disable-next-line prefer-arrow-callback
 ScriptingGlobals.add(function renameEmbedding(doc: Doc) {
-    return StrCast(doc.$title).replace(/\([0-9]*\)/, '') + `(${doc.proto_embeddingId})`;
+    return StrCast(doc.title).replace(/\([0-9]*\)/, '') + `(${doc.proto_embeddingId})`;
 });
 // eslint-disable-next-line prefer-arrow-callback
 ScriptingGlobals.add(function getProto(doc: Doc) {
diff --git a/src/fields/util.ts b/src/fields/util.ts
index abbe543e8..205c500a5 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -303,30 +303,23 @@ export function SetPropSetterCb(prop: string, propSetter: ((target: Doc, value:
 //
 // target should be either a Doc or ListImpl.  receiver should be a Proxy<Doc> Or List.
 //
-export function setter(target: ListImpl<FieldType> | Doc, inProp: string | symbol | number, value: unknown, receiver: Doc | ListImpl<FieldType>): boolean {
-    if (!inProp) {
+export function setter(target: ListImpl<FieldType> | Doc, prop: string | symbol | number, value: unknown, receiver: Doc | ListImpl<FieldType>): boolean {
+    if (!prop) {
         console.log('WARNING: trying to set an empty property.  This should be fixed. ');
         return false;
     }
-    let prop = inProp;
-    const effectiveAcl = inProp === 'constructor' || typeof inProp === 'symbol' ? AclAdmin : GetPropAcl(target, prop);
+    const effectiveAcl = prop === 'constructor' || typeof prop === 'symbol' ? AclAdmin : GetPropAcl(target, prop);
     if (effectiveAcl !== AclEdit && effectiveAcl !== AclAugment && effectiveAcl !== AclAdmin) return true;
     // if you're trying to change an acl but don't have Admin access / you're trying to change it to something that isn't an acceptable acl, you can't
     if (typeof prop === 'string' && prop.startsWith('acl_') && (effectiveAcl !== AclAdmin || ![...Object.values(SharingPermissions), undefined].includes(value as SharingPermissions))) return true;
 
-    if (typeof prop === 'string' && prop !== '__id' && prop !== '__fieldTuples' && prop.startsWith('$')) {
-        prop = prop.substring(1);
-        if (target.__DATA__ instanceof Doc) {
-            target.__DATA__[prop] = value as FieldResult;
-            return true;
-        }
+    if (target instanceof Doc && typeof prop === 'string' && prop !== '__id' && prop !== '__fieldTuples' && prop.startsWith('$')) {
+        target.__DATA__[prop.substring(1)] = value as FieldResult;
+        return true;
     }
-    if (typeof prop === 'string' && prop !== '__id' && prop !== '__fieldTuples' && prop.startsWith('_')) {
-        if (!prop.startsWith('__')) prop = prop.substring(1);
-        if (target.__LAYOUT__ instanceof Doc) {
-            target.__LAYOUT__[prop] = value as FieldResult;
-            return true;
-        }
+    if (target instanceof Doc && typeof prop === 'string' && prop !== '__id' && prop !== '__fieldTuples' && prop.startsWith('_') && !prop.startsWith('__')) {
+        target.__LAYOUT__[prop.substring(1)] = value as FieldResult;
+        return true;
     }
     if (target.__fieldTuples[prop] instanceof ComputedField) {
         if (target.__fieldTuples[prop].setterscript && value !== undefined && !(value instanceof ComputedField)) {
-- 
cgit v1.2.3-70-g09d2


From f061db01d1936bf3e776b140786ba3bf1c6b71f3 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 31 Mar 2025 14:29:57 -0400
Subject: moved audio annotation indicator/playback button to TagsView. 
 cleaned up tagsview colors and unobscured by bottom resizer

---
 .../components/src/components/Button/Button.scss   |  5 +-
 .../src/components/IconButton/IconButton.scss      |  5 +-
 src/client/util/DictationManager.ts                | 39 ++++++------
 src/client/views/DocViewUtils.ts                   |  3 -
 src/client/views/DocumentButtonBar.tsx             |  2 +-
 src/client/views/DocumentDecorations.tsx           |  4 +-
 src/client/views/StyleProvider.scss                |  5 --
 src/client/views/StyleProvider.tsx                 | 18 +-----
 src/client/views/TagsView.scss                     |  6 +-
 src/client/views/TagsView.tsx                      | 21 ++++---
 src/client/views/nodes/DocumentView.tsx            | 12 ++--
 src/client/views/nodes/IconTagBox.scss             |  1 -
 src/client/views/nodes/IconTagBox.tsx              | 69 +++++++++++++++-------
 .../views/nodes/formattedText/FormattedTextBox.tsx |  4 +-
 14 files changed, 100 insertions(+), 94 deletions(-)

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

diff --git a/packages/components/src/components/Button/Button.scss b/packages/components/src/components/Button/Button.scss
index b2fb48344..c86db9fad 100644
--- a/packages/components/src/components/Button/Button.scss
+++ b/packages/components/src/components/Button/Button.scss
@@ -63,13 +63,16 @@
             filter: opacity(0);
 
             &.active {
-                filter: opacity(0.5);
+                filter: opacity(0.3);
             }
         }
 
         &:hover {
             .background {
                 filter: opacity(0.3);
+                &.active {
+                    filter: opacity(0);
+                }
             }
         }
     }
diff --git a/packages/components/src/components/IconButton/IconButton.scss b/packages/components/src/components/IconButton/IconButton.scss
index 3f0dd26ea..f95d94ae4 100644
--- a/packages/components/src/components/IconButton/IconButton.scss
+++ b/packages/components/src/components/IconButton/IconButton.scss
@@ -54,13 +54,16 @@
             filter: opacity(0);
 
             &.active {
-                filter: opacity(0.5);
+                filter: opacity(0.3);
             }
         }
 
         &:hover {
             .background {
                 filter: opacity(0.3);
+                &.active {
+                    filter: opacity(0);
+                }
             }
         }
     }
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts
index 44fbda319..2eef3da0e 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -356,23 +356,16 @@ export namespace DictationManager {
                 action: (target: DocumentView, matches: RegExpExecArray) => {
                     const count = interpretNumber(matches[1]);
                     const what = matches[2];
-                    const dataDoc = Doc.GetProto(target.Document);
-                    const fieldKey = 'data';
-                    if (isNaN(count)) {
-                        return;
-                    }
-                    for (let i = 0; i < count; i++) {
-                        let created: Doc | undefined;
-                        switch (what) {
-                            case 'image':
-                                created = Docs.Create.ImageDocument('https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg');
-                                break;
-                            case 'nested collection':
-                                created = Docs.Create.FreeformDocument([], {});
-                                break;
-                            default:
+                    if (!isNaN(count)) {
+                        for (let i = 0; i < count; i++) {
+                            const created = (() => {
+                                switch (what) {
+                                    case 'image':            return Docs.Create.ImageDocument('https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg');
+                                    case 'nested collection':return Docs.Create.FreeformDocument([], {});
+                                } // prettier-ignore
+                            })();
+                            created && Doc.AddDocToList(target.dataDoc, Doc.LayoutFieldKey(target.Document), created);
                         }
-                        created && Doc.AddDocToList(dataDoc, fieldKey, created);
                     }
                 },
                 restrictTo: [DocumentType.COL],
@@ -388,13 +381,15 @@ export namespace DictationManager {
             },
         ];
     }
-    export function recordAudioAnnotation(dataDoc: Doc, field: string, onRecording?: (stop: () => void) => void, onEnd?: () => void) {
+    export function recordAudioAnnotation(doc: Doc, fieldIn: string, onRecording?: (stop: () => void) => void, onEnd?: () => void) {
+        const field = '$' + fieldIn + '_audioAnnotations';
         let gumStream: MediaStream | undefined;
         let recorder: MediaRecorder | undefined;
         navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => {
-            let audioTextAnnos = Cast(dataDoc[field + '_audioAnnotations_text'], listSpec('string'), null);
+            let audioTextAnnos = Cast(doc[field + '_text'], listSpec('string'), null);
             if (audioTextAnnos) audioTextAnnos.push('');
-            else audioTextAnnos = dataDoc[field + '_audioAnnotations_text'] = new List<string>(['']);
+            else audioTextAnnos = doc[field + '_text'] = new List<string>(['']);
+            doc._layout_showTags = true;
             DictationManager.Controls.listen({
                 interimHandler: value => { audioTextAnnos[audioTextAnnos.length - 1] = value; }, // prettier-ignore
                 continuous: { indefinite: false },
@@ -415,16 +410,16 @@ export namespace DictationManager {
                 const [{ result }] = await Networking.UploadFilesToServer({ file: file as Blob & { name: string; lastModified: number; webkitRelativePath: string } });
                 if (!(result instanceof Error)) {
                     const audioField = new AudioField(result.accessPaths.agnostic.client);
-                    const audioAnnos = Cast(dataDoc[field + '_audioAnnotations'], listSpec(AudioField), null);
+                    const audioAnnos = Cast(doc[field], listSpec(AudioField), null);
                     if (audioAnnos) audioAnnos.push(audioField);
-                    else dataDoc[field + '_audioAnnotations'] = new List([audioField]);
+                    else doc[field] = new List([audioField]);
                 }
             };
             recorder.start();
             const stopFunc = () => {
                 recorder?.stop();
                 DictationManager.Controls.stop(/* false */);
-                dataDoc.audioAnnoState = AudioAnnoState.stopped;
+                doc._audioAnnoState = AudioAnnoState.stopped;
                 gumStream?.getAudioTracks()[0].stop();
             };
             if (onRecording) onRecording(stopFunc);
diff --git a/src/client/views/DocViewUtils.ts b/src/client/views/DocViewUtils.ts
index 1f5f29c7e..5710187b5 100644
--- a/src/client/views/DocViewUtils.ts
+++ b/src/client/views/DocViewUtils.ts
@@ -1,6 +1,3 @@
-/* eslint-disable prefer-destructuring */
-/* eslint-disable default-param-last */
-/* eslint-disable no-use-before-define */
 import { runInAction } from 'mobx';
 import { Doc, SetActiveAudioLinker } from '../../fields/Doc';
 import { DocUtils } from '../documents/DocUtils';
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index b17dbc93d..9a57a5e6c 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -358,7 +358,7 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
                             view =>
                                 view &&
                                 DictationManager.recordAudioAnnotation(
-                                    view.dataDoc,
+                                    view.Document,
                                     view.LayoutFieldKey,
                                     stopFunc => {
                                         this._stopFunc = stopFunc;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 7842c233a..875eb45e0 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -834,7 +834,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
                                     <div key="c" className="documentDecorations-centerCont" />
                                     <div key="r" className="documentDecorations-rightResizer" onPointerDown={this.onPointerDown} />
                                     <div key="bl" className="documentDecorations-bottomLeftResizer" onPointerDown={this.onPointerDown} />
-                                    <div key="b" className="documentDecorations-bottomResizer" onPointerDown={this.onPointerDown} />
+                                    {(seldocview.TagPanelHeight ?? 0) > 30 ? null : <div key="b" className="documentDecorations-bottomResizer" onPointerDown={this.onPointerDown} />}
                                     <div key="br" className="documentDecorations-bottomRightResizer" onPointerDown={this.onPointerDown} />
                                 </>
                             )}
@@ -866,7 +866,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
                                     top: 30, // offset by height of documentButtonBar so that items can be clicked without overlap interference
                                     transform: `translate(${-this._resizeBorderWidth / 2 + 10}px, ${this._resizeBorderWidth + bounds.b - bounds.y + this._titleHeight}px) `,
                                 }}>
-                                {DocumentView.Selected().length > 1 ? <TagsView Views={DocumentView.Selected()} /> : null}
+                                {DocumentView.Selected().length > 1 ? <TagsView Views={DocumentView.Selected()} background={SnappingManager.userBackgroundColor ?? ''} /> : null}
                             </div>
                         </div>
 
diff --git a/src/client/views/StyleProvider.scss b/src/client/views/StyleProvider.scss
index 501b9a292..99796f1fb 100644
--- a/src/client/views/StyleProvider.scss
+++ b/src/client/views/StyleProvider.scss
@@ -1,5 +1,4 @@
 .styleProvider-filter,
-.styleProvider-audio,
 .styleProvider-paint,
 .styleProvider-paint-selected,
 .styleProvider-lock {
@@ -32,9 +31,6 @@
         margin: auto !important;
     }
 }
-.styleProvider-audio {
-    right: 40;
-}
 .styleProvider-paint-selected,
 .styleProvider-paint {
     top: 15;
@@ -43,7 +39,6 @@
     right: -40;
 }
 .styleProvider-lock:hover,
-.styleProvider-audio:hover,
 .styleProvider-filter:hover {
     opacity: 1;
 }
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index a59d39cfb..432a05146 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -1,7 +1,6 @@
 import { Dropdown, DropdownType, IconButton, IListItemProps, Shadows, Size, Type } from '@dash/components';
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Tooltip } from '@mui/material';
 import { action, untracked } from 'mobx';
 import { extname } from 'path';
 import * as React from 'react';
@@ -9,11 +8,11 @@ import { BsArrowDown, BsArrowDownUp, BsArrowUp } from 'react-icons/bs';
 import { FaFilter } from 'react-icons/fa';
 import { ClientUtils, DashColor, lightOrDark } from '../../ClientUtils';
 import { Doc, Opt, StrListCast } from '../../fields/Doc';
+import { DocLayout } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { InkInkTool } from '../../fields/InkField';
 import { ScriptField } from '../../fields/ScriptField';
 import { BoolCast, Cast, DocCast, ImageCast, NumCast, ScriptCast, StrCast } from '../../fields/Types';
-import { AudioAnnoState } from '../../server/SharedMediaTypes';
 import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
 import { IsFollowLinkScript } from '../documents/DocUtils';
 import { SnappingManager } from '../util/SnappingManager';
@@ -26,7 +25,6 @@ import { FieldViewProps } from './nodes/FieldView';
 import { StyleProp } from './StyleProp';
 import './StyleProvider.scss';
 import { styleProviderQuiz } from './StyleProviderQuiz';
-import { DocLayout } from '../../fields/DocSymbols';
 
 function toggleLockedPosition(doc: Doc) {
     UndoManager.RunInBatch(() => Doc.toggleLockedPosition(doc), 'toggleBackground');
@@ -387,26 +385,12 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
                     </div>
                 );
             };
-            const audio = () => {
-                const audioAnnoState = (audioDoc: Doc) => StrCast(audioDoc.audioAnnoState, AudioAnnoState.stopped);
-                const audioAnnosCount = (audioDoc: Doc) => StrListCast(audioDoc[fieldKey + 'audioAnnotations']).length;
-                if (!doc || renderDepth === -1 || !audioAnnosCount(doc)) return null;
-                const audioIconColors: { [key: string]: string } = { playing: 'green', stopped: 'blue' };
-                return (
-                    <Tooltip title={<div>{StrListCast(doc[fieldKey + 'audioAnnotations_text']).lastElement()}</div>}>
-                        <div className="styleProvider-audio" onPointerDown={() => DocumentView.getFirstDocumentView(doc)?.playAnnotation()}>
-                            <FontAwesomeIcon className="documentView-audioFont" style={{ color: audioIconColors[audioAnnoState(doc)] }} icon='file-audio' size="sm" />
-                        </div>
-                    </Tooltip>
-                );
-            };
         
             return (
                 <>
                     {paint()}
                     {lock()}
                     {filter()}
-                    {audio()}
                 </>
             );
         }
diff --git a/src/client/views/TagsView.scss b/src/client/views/TagsView.scss
index b21d303fb..231d5e11a 100644
--- a/src/client/views/TagsView.scss
+++ b/src/client/views/TagsView.scss
@@ -12,7 +12,7 @@
         .tagsView-list {
             display: flex;
             flex-wrap: wrap;
-            height: 1;
+            height: 100%;
             .iconButton-container {
                 min-height: unset !important;
             }
@@ -57,10 +57,6 @@
     align-items: center;
 }
 
-.tagsView-editing-box {
-    margin-top: 20px;
-}
-
 .tagsView-input-box {
     margin: auto;
     align-self: center;
diff --git a/src/client/views/TagsView.tsx b/src/client/views/TagsView.tsx
index f1bcaac2c..39c952483 100644
--- a/src/client/views/TagsView.tsx
+++ b/src/client/views/TagsView.tsx
@@ -1,5 +1,5 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Button, Colors, IconButton } from '@dash/components';
+import { Button, Colors, IconButton, Type } from '@dash/components';
 import { IReactionDisposer, action, computed, makeObservable, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import React from 'react';
@@ -20,6 +20,7 @@ import { DocumentView } from './nodes/DocumentView';
 import { FaceRecognitionHandler } from './search/FaceRecognitionHandler';
 import { IconTagBox } from './nodes/IconTagBox';
 import { Id } from '../../fields/FieldSymbols';
+import { StyleProp } from './StyleProp';
 
 /**
  * The TagsView is a metadata input/display panel shown at the bottom of a DocumentView in a freeform collection.
@@ -254,6 +255,7 @@ export class TagItem extends ObservableReactComponent<TagItemProps> {
 
 interface TagViewProps {
     Views: DocumentView[];
+    background: string;
 }
 
 /**
@@ -269,7 +271,7 @@ export class TagsView extends ObservableReactComponent<TagViewProps> {
 
     @observable _panelHeightDirty = 0;
     @observable _currentInput = '';
-    @observable _isEditing = !StrListCast(this.View.dataDoc.tags).length;
+    @observable _isEditing: boolean | undefined = undefined;
     _heightDisposer: IReactionDisposer | undefined;
     _lastXf = this.View.screenToContentsTransform();
 
@@ -295,7 +297,9 @@ export class TagsView extends ObservableReactComponent<TagViewProps> {
     }
 
     @computed get isEditing() {
-        return this._isEditing && (this._props.Views.length > 1 || (DocumentView.Selected().length === 1 && DocumentView.Selected().includes(this.View)));
+        const selected = DocumentView.Selected().length === 1 && DocumentView.Selected().includes(this.View);
+        if (this._isEditing === undefined) return selected && !StrListCast(this.View.dataDoc.tags).length && !StrListCast(this.View.dataDoc[Doc.LayoutFieldKey(this.View.Document) + '_audioAnnotations_text']).length;
+        return this._isEditing && (this._props.Views.length > 1 || selected);
     }
 
     /**
@@ -349,7 +353,7 @@ export class TagsView extends ObservableReactComponent<TagViewProps> {
                 ref={r => r && new ResizeObserver(action(() => this._props.Views.length === 1 && (this.View.TagPanelHeight = Math.max(0, (r?.getBoundingClientRect().height ?? 0) - this.InsetDist)))).observe(r)}
                 style={{
                     display: SnappingManager.IsResizing === this.View.Document[Id] ? 'none' : undefined,
-                    backgroundColor: this.isEditing ? Colors.LIGHT_GRAY : Colors.TRANSPARENT,
+                    backgroundColor: this.isEditing ? this._props.background : Colors.TRANSPARENT,
                     borderColor: this.isEditing ? Colors.BLACK : Colors.TRANSPARENT,
                     height: !this._props.Views.lastElement()?.isSelected() ? 0 : undefined,
                 }}>
@@ -361,14 +365,17 @@ export class TagsView extends ObservableReactComponent<TagViewProps> {
                                 tooltip="Close Menu"
                                 onPointerDown={e =>
                                     setupMoveUpEvents(this, e, returnFalse, emptyFunction, upEv => {
-                                        this.setToEditing(!this._isEditing);
+                                        this.setToEditing(!this.isEditing);
                                         upEv.stopPropagation();
                                     })
                                 }
-                                icon={<FontAwesomeIcon icon={this._isEditing ? 'chevron-up' : 'chevron-down'} size="sm" />}
+                                type={Type.TERT}
+                                background="transparent"
+                                color={this.View._props.styleProvider?.(this.View.Document, this.View.ComponentView?._props, StyleProp.FontColor) as string}
+                                icon={<FontAwesomeIcon icon={this.isEditing ? 'chevron-up' : 'chevron-down'} size="sm" />}
                             />
                         )}
-                        <IconTagBox Views={this._props.Views} IsEditing={this._isEditing} />
+                        <IconTagBox Views={this._props.Views} IsEditing={this.isEditing} />
                         {Array.from(tagsList)
                             .filter(tag => (tag.startsWith('#') || tag.startsWith('@')) && !Doc.MyFilterHotKeys.some(key => key.toolType === tag))
                             .map(tag => (
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index c3026d7be..070a13103 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -750,7 +750,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
                             transform: `scale(${this.uiBtnScaling})`,
                             bottom: Number.isNaN(this.maxWidgetSize) ? undefined : this.maxWidgetSize,
                         }}>
-                        {this._props.DocumentView?.() && !this._props.docViewPath().slice(-2)[0].ComponentView?.isUnstyledView?.() ? <TagsView Views={[this._props.DocumentView?.()]} /> : null}
+                        {this._props.DocumentView?.() && !this._props.docViewPath().slice(-2)[0].ComponentView?.isUnstyledView?.() ? <TagsView background={this.backgroundBoxColor} Views={[this._props.DocumentView?.()]} /> : null}
                     </div>
                 ) : (
                     <>
@@ -774,7 +774,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
                             ref={r => this.historyRef(this._oldHistoryWheel, (this._oldHistoryWheel = r))}>
                             <div className="documentView-editorView-resizer" />
                             {this._componentView?.componentAIView?.() ?? null}
-                            {this._props.DocumentView?.() ? <TagsView Views={[this._props.DocumentView?.()]} /> : null}
+                            {this._props.DocumentView?.() ? <TagsView background={this.backgroundBoxColor} Views={[this._props.DocumentView?.()]} /> : null}
                         </div>
                     </>
                 )}
@@ -1319,7 +1319,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
     }
 
     public playAnnotation = () => {
-        const audioAnnoState = this.dataDoc.audioAnnoState ?? AudioAnnoState.stopped;
+        const audioAnnoState = this.Document._audioAnnoState ?? AudioAnnoState.stopped;
         const audioAnnos = Cast(this.dataDoc[this.LayoutFieldKey + '_audioAnnotations'], listSpec(AudioField), null);
         const anno = audioAnnos?.lastElement();
         if (anno instanceof AudioField) {
@@ -1331,13 +1331,13 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
                         autoplay: true,
                         loop: false,
                         volume: 0.5,
-                        onend: action(() => { this.dataDoc.audioAnnoState = AudioAnnoState.stopped; }), // prettier-ignore
+                        onend: action(() => { this.Document._audioAnnoState = AudioAnnoState.stopped; }), // prettier-ignore
                     });
-                    this.dataDoc.audioAnnoState = AudioAnnoState.playing;
+                    this.Document._audioAnnoState = AudioAnnoState.playing;
                     break;
                 case AudioAnnoState.playing:
                     (this.dataDoc[AudioPlay] as Howl)?.stop();
-                    this.dataDoc.audioAnnoState = AudioAnnoState.stopped;
+                    this.Document._audioAnnoState = AudioAnnoState.stopped;
                     break;
                 default:
             }
diff --git a/src/client/views/nodes/IconTagBox.scss b/src/client/views/nodes/IconTagBox.scss
index 202b0c701..d6cf95958 100644
--- a/src/client/views/nodes/IconTagBox.scss
+++ b/src/client/views/nodes/IconTagBox.scss
@@ -4,7 +4,6 @@
     display: flex;
     position: relative;
     pointer-events: none;
-    background-color: rgb(218, 218, 218);
     border-radius: 50px;
     align-items: center;
     gap: 5px;
diff --git a/src/client/views/nodes/IconTagBox.tsx b/src/client/views/nodes/IconTagBox.tsx
index ddabd61e1..e3924eca7 100644
--- a/src/client/views/nodes/IconTagBox.tsx
+++ b/src/client/views/nodes/IconTagBox.tsx
@@ -1,22 +1,23 @@
-import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@mui/material';
 import { computed, makeObservable } from 'mobx';
 import { observer } from 'mobx-react';
 import React from 'react';
-import { returnFalse, setupMoveUpEvents } from '../../../ClientUtils';
-import { emptyFunction } from '../../../Utils';
-import { Doc } from '../../../fields/Doc';
+import { Doc, StrListCast } from '../../../fields/Doc';
 import { StrCast } from '../../../fields/Types';
+import { AudioAnnoState } from '../../../server/SharedMediaTypes';
 import { undoable } from '../../util/UndoManager';
 import { ObservableReactComponent } from '../ObservableReactComponent';
 import { TagItem } from '../TagsView';
 import { DocumentView } from './DocumentView';
 import './IconTagBox.scss';
+import { Size, Toggle, ToggleType, Type } from '@dash/components';
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
+import { StyleProp } from '../StyleProp';
 
 export interface IconTagProps {
     Views: DocumentView[];
-    IsEditing: boolean;
+    IsEditing: boolean | undefined;
 }
 
 /**
@@ -52,13 +53,46 @@ export class IconTagBox extends ObservableReactComponent<IconTagProps> {
      * @param key metadata icon button
      * @returns an icon for the metdata button
      */
-    getButtonIcon = (doc: Doc, key: Doc): JSX.Element => {
+    getButtonIcon = (dv: DocumentView, key: Doc): JSX.Element => {
         const icon = StrCast(key.icon) as IconProp;
         const tag = StrCast(key.toolType);
-        const isActive = TagItem.docHasTag(doc, tag);
-        const color = isActive ? '#4476f7' : '#323232'; // TODO should use theme colors
+        const color = dv._props.styleProvider?.(dv.layoutDoc, dv.ComponentView?._props, StyleProp.FontColor) as string;
+        return (
+            <Toggle
+                tooltip={`Click to add/remove the tag ${tag}`}
+                toggleStatus={TagItem.docHasTag(dv.Document, tag)}
+                toggleType={ToggleType.BUTTON}
+                icon={<FontAwesomeIcon className={`fontIconBox-icon-${ToggleType.BUTTON}`} icon={icon} color={color} />}
+                size={Size.XSMALL}
+                type={Type.PRIM}
+                onClick={() => this.setIconTag(tag, !TagItem.docHasTag(this.View.Document, tag))}
+                color={color}
+            />
+        );
+    };
 
-        return <FontAwesomeIcon icon={icon} style={{ color, height: '20px', width: '20px' }} />;
+    /**
+     * Displays a button to play audio annotations on the document.
+     * NOTE: This should be generalized  --  audio should
+     * @returns
+     */
+    renderAudioButtons = (dv: DocumentView, anno: string) => {
+        const fcolor = dv._props.styleProvider?.(dv.layoutDoc, dv.ComponentView?._props, StyleProp.FontColor) as string;
+        const audioIconColors: { [key: string]: string } = { playing: 'green', stopped: fcolor ?? 'blue', recording: 'red' };
+        const audioAnnoState = (audioDoc: Doc) => StrCast(audioDoc.audioAnnoState, AudioAnnoState.stopped);
+        const color = audioIconColors[audioAnnoState(this.View.Document)];
+        return (
+            <Toggle
+                tooltip={`click to play:${anno}`}
+                toggleStatus={true}
+                toggleType={ToggleType.BUTTON}
+                icon={<FontAwesomeIcon className={`fontIconBox-icon-${ToggleType.BUTTON}`} icon="file-audio" color={color} />}
+                size={Size.XSMALL}
+                type={Type.PRIM}
+                onClick={() => this.View?.playAnnotation()}
+                color={color}
+            />
+        );
     };
 
     /**
@@ -69,22 +103,15 @@ export class IconTagBox extends ObservableReactComponent<IconTagProps> {
             .map(key => ({ key, tag: StrCast(key.toolType) }))
             .filter(({ tag }) => this._props.IsEditing || TagItem.docHasTag(this.View.Document, tag) || (DocumentView.Selected().length === 1 && this.View.IsSelected))
             .map(({ key, tag }) => (
-                <Tooltip key={tag} title={<div className="dash-tooltip">Click to add/remove this card from the {tag} group</div>}>
-                    <button
-                        type="button"
-                        onPointerDown={e =>
-                            setupMoveUpEvents(this, e, returnFalse, emptyFunction, clickEv => {
-                                this.setIconTag(tag, !TagItem.docHasTag(this.View.Document, tag));
-                                clickEv.stopPropagation();
-                            })
-                        }>
-                        {this.getButtonIcon(this.View.Document, key)}
-                    </button>
+                <Tooltip key={tag} title={<div className="dash-tooltip">Click to add/remove the {tag} tag</div>}>
+                    {this.getButtonIcon(this.View, key)}
                 </Tooltip>
             )); // prettier-ignore
 
-        return !buttons.length ? null : (
+        const audioannos = StrListCast(this.View.Document[Doc.LayoutFieldKey(this.View.Document) + '_audioAnnotations_text']);
+        return !buttons.length && !audioannos.length ? null : (
             <div className="card-button-container" style={{ fontSize: '50px' }}>
+                {audioannos.length ? this.renderAudioButtons(this.View, audioannos.lastElement()) : null}
                 {buttons}
             </div>
         );
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 51b9a4333..6955e5401 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -17,7 +17,7 @@ import { BsMarkdownFill } from 'react-icons/bs';
 import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, ClientUtils, DivWidth, returnFalse, returnZero, setupMoveUpEvents, simMouseEvent, smoothScroll, StopEvent } from '../../../../ClientUtils';
 import { DateField } from '../../../../fields/DateField';
 import { CreateLinkToActiveAudio, Doc, DocListCast, Field, FieldType, Opt, StrListCast } from '../../../../fields/Doc';
-import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocCss, DocData, ForceServerWrite, UpdatingFromServer } from '../../../../fields/DocSymbols';
+import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocCss, ForceServerWrite, UpdatingFromServer } from '../../../../fields/DocSymbols';
 import { Id, ToString } from '../../../../fields/FieldSymbols';
 import { InkTool } from '../../../../fields/InkField';
 import { List } from '../../../../fields/List';
@@ -246,7 +246,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                     anchor.followLinkAudio = true;
                     let stopFunc: () => void = emptyFunction;
                     target.$mediaState = mediaState.Recording;
-                    DictationManager.recordAudioAnnotation(target[DocData], Doc.LayoutFieldKey(target), stop => { stopFunc = stop }); // prettier-ignore
+                    DictationManager.recordAudioAnnotation(target, Doc.LayoutFieldKey(target), stop => { stopFunc = stop }); // prettier-ignore
 
                     const reactionDisposer = reaction(
                         () => target.mediaState,
-- 
cgit v1.2.3-70-g09d2


From 031a607100700f818f96b7fbf478f1b75292be9b Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 5 Apr 2025 00:01:53 -0400
Subject: fixed sizing of  text box annotations sidebar button.  cleaned up
 TagsView to not overlap button bar below doc decorations or bottom resize
 handle

---
 src/client/views/AntimodeMenu.tsx                  |  2 +
 src/client/views/DocumentDecorations.scss          |  3 +-
 src/client/views/DocumentDecorations.tsx           | 34 +++++------
 src/client/views/SidebarAnnos.scss                 | 10 ++++
 src/client/views/SidebarAnnos.tsx                  |  7 +--
 src/client/views/TagsView.scss                     |  3 +-
 src/client/views/TagsView.tsx                      | 21 +++++--
 .../views/global/globalCssVariables.module.scss    |  2 +-
 src/client/views/nodes/DocumentView.scss           |  4 +-
 src/client/views/nodes/DocumentView.tsx            |  3 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx | 65 ++++++++++++++--------
 11 files changed, 98 insertions(+), 56 deletions(-)

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

diff --git a/src/client/views/AntimodeMenu.tsx b/src/client/views/AntimodeMenu.tsx
index 99dee6410..b5e56cad5 100644
--- a/src/client/views/AntimodeMenu.tsx
+++ b/src/client/views/AntimodeMenu.tsx
@@ -4,6 +4,7 @@ import { SnappingManager } from '../util/SnappingManager';
 import './AntimodeMenu.scss';
 import { ObservableReactComponent } from './ObservableReactComponent';
 
+// eslint-disable-next-line @typescript-eslint/no-empty-object-type
 export interface AntimodeMenuProps {}
 
 /**
@@ -162,6 +163,7 @@ export abstract class AntimodeMenu<T extends AntimodeMenuProps> extends Observab
                     transitionDuration: this._transitionDuration,
                     transitionDelay: this._transitionDelay,
                     position: this.Pinned ? 'unset' : undefined,
+                    border: `${SnappingManager.userColor} solid 1px`,
                 }}>
                 {buttons}
             </div>
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index 732c2645e..28cebe23c 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -41,6 +41,7 @@ $resizeHandler: 8px;
     .documentDecorations-tagsView {
         position: absolute;
         height: 100%;
+        width: 100%;
         pointer-events: all;
         border-radius: 50%;
         color: black;
@@ -327,7 +328,7 @@ $resizeHandler: 8px;
     .documentDecorations-rightResizer {
         pointer-events: auto;
         background: global.$medium-gray-dim;
-        //opacity: 0.2;
+        opacity: 0.2;
         &:hover {
             opacity: 1;
         }
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 875eb45e0..452dd60ee 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -49,7 +49,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
     static Instance: DocumentDecorations;
     private _resizeHdlId = '';
     private _keyinput = React.createRef<HTMLInputElement>();
-    private _resizeBorderWidth = 16;
+    private _resizeBorderWidth = 8;
     private _linkBoxHeight = 20 + 3; // link button height + margin
     private _titleHeight = 20;
     private _resizeUndo?: UndoManager.Batch;
@@ -98,11 +98,11 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
         const bounds = { ...this.Bounds };
         const leftBounds = this._props.boundsLeft;
         const topBounds = DocumentView.LightboxDoc() ? 0 : this._props.boundsTop;
-        bounds.x = Math.max(leftBounds, bounds.x - this._resizeBorderWidth / 2) + this._resizeBorderWidth / 2;
-        bounds.y = Math.max(topBounds, bounds.y - this._resizeBorderWidth / 2 - this._titleHeight) + this._resizeBorderWidth / 2 + this._titleHeight;
+        bounds.x = Math.max(leftBounds, bounds.x - this._resizeBorderWidth) + this._resizeBorderWidth;
+        bounds.y = Math.max(topBounds, bounds.y - this._resizeBorderWidth - this._titleHeight) + this._resizeBorderWidth + this._titleHeight;
         const borderRadiusDraggerWidth = 15;
-        bounds.r = Math.max(bounds.x, Math.max(leftBounds, Math.min(window.innerWidth, bounds.r + borderRadiusDraggerWidth + this._resizeBorderWidth / 2) - this._resizeBorderWidth / 2 - borderRadiusDraggerWidth));
-        bounds.b = Math.max(bounds.y, Math.max(topBounds, Math.min(window.innerHeight, bounds.b + this._resizeBorderWidth / 2 + this._linkBoxHeight) - this._resizeBorderWidth / 2 - this._linkBoxHeight));
+        bounds.r = Math.max(bounds.x, Math.max(leftBounds, Math.min(window.innerWidth, bounds.r + borderRadiusDraggerWidth + this._resizeBorderWidth) - this._resizeBorderWidth - borderRadiusDraggerWidth));
+        bounds.b = Math.max(bounds.y, Math.max(topBounds, Math.min(window.innerHeight, bounds.b + this._resizeBorderWidth + this._linkBoxHeight) - this._resizeBorderWidth - this._linkBoxHeight));
         return bounds;
     }
 
@@ -790,10 +790,10 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
                 <div
                     className="documentDecorations-background"
                     style={{
-                        width: bounds.r - bounds.x + this._resizeBorderWidth + 'px',
-                        height: bounds.b - bounds.y + this._resizeBorderWidth + 'px',
-                        left: bounds.x - this._resizeBorderWidth / 2,
-                        top: bounds.y - this._resizeBorderWidth / 2,
+                        width: bounds.r - bounds.x + 2 * this._resizeBorderWidth + 'px',
+                        height: bounds.b - bounds.y + 2 * this._resizeBorderWidth + 'px',
+                        left: bounds.x - this._resizeBorderWidth,
+                        top: bounds.y - this._resizeBorderWidth,
                         transformOrigin,
                         background: SnappingManager.ShiftKey ? undefined : 'yellow',
                         pointerEvents: SnappingManager.ShiftKey || SnappingManager.IsResizing ? 'none' : 'all',
@@ -807,10 +807,10 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
                         <div
                             className={`documentDecorations-container  ${this._showNothing ? 'showNothing' : ''}`}
                             style={{
-                                transform: `translate(${bounds.x - this._resizeBorderWidth / 2}px, ${bounds.y - this._resizeBorderWidth / 2 - this._titleHeight}px) rotate(${rotation}deg)`,
+                                transform: `translate(${bounds.x - this._resizeBorderWidth}px, ${bounds.y - this._resizeBorderWidth - this._titleHeight}px) rotate(${rotation}deg)`,
                                 transformOrigin,
-                                width: bounds.r - bounds.x + this._resizeBorderWidth + 'px',
-                                height: bounds.b - bounds.y + this._resizeBorderWidth + (this._showNothing ? 0 : this._titleHeight) + 'px',
+                                width: bounds.r - bounds.x + 2 * this._resizeBorderWidth + 'px',
+                                height: bounds.b - bounds.y + 2 * this._resizeBorderWidth + (this._showNothing ? 0 : this._titleHeight) + 'px',
                             }}>
                             <div
                                 className="documentDecorations-topbar"
@@ -834,7 +834,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
                                     <div key="c" className="documentDecorations-centerCont" />
                                     <div key="r" className="documentDecorations-rightResizer" onPointerDown={this.onPointerDown} />
                                     <div key="bl" className="documentDecorations-bottomLeftResizer" onPointerDown={this.onPointerDown} />
-                                    {(seldocview.TagPanelHeight ?? 0) > 30 ? null : <div key="b" className="documentDecorations-bottomResizer" onPointerDown={this.onPointerDown} />}
+                                    {seldocview.TagPanelHeight !== 0 || seldocview.TagPanelEditing ? null : <div key="b" className="documentDecorations-bottomResizer" onPointerDown={this.onPointerDown} />}
                                     <div key="br" className="documentDecorations-bottomRightResizer" onPointerDown={this.onPointerDown} />
                                 </>
                             )}
@@ -850,12 +850,12 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
                                 />
                             )}
 
-                            {hideDocumentButtonBar || this._showNothing ? null : (
+                            {seldocview.TagPanelEditing || hideDocumentButtonBar || this._showNothing ? null : (
                                 <div
                                     className="link-button-container"
                                     style={{
-                                        top: DocumentView.Selected().length > 1 ? 0 : `${seldocview.showTags ? 4 + seldocview.TagPanelHeight : 4}px`,
-                                        transform: `translate(${-this._resizeBorderWidth / 2 + 10}px, ${this._resizeBorderWidth + bounds.b - bounds.y + this._titleHeight}px) `,
+                                        top: DocumentView.Selected().length > 1 || !seldocview.showTags ? 0 : `${seldocview.TagPanelHeight}px`,
+                                        transform: `translate(${-this._resizeBorderWidth + 10}px, ${(seldocview.TagPanelHeight === 0 ? 2 * this._resizeBorderWidth : this._resizeBorderWidth) + bounds.b - bounds.y + this._titleHeight}px) `,
                                     }}>
                                     <DocumentButtonBar views={() => DocumentView.Selected()} />
                                 </div>
@@ -864,7 +864,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
                                 className="documentDecorations-tagsView"
                                 style={{
                                     top: 30, // offset by height of documentButtonBar so that items can be clicked without overlap interference
-                                    transform: `translate(${-this._resizeBorderWidth / 2 + 10}px, ${this._resizeBorderWidth + bounds.b - bounds.y + this._titleHeight}px) `,
+                                    transform: `translate(${-this._resizeBorderWidth + 10}px, ${2 * this._resizeBorderWidth + bounds.b - bounds.y + this._titleHeight}px) `,
                                 }}>
                                 {DocumentView.Selected().length > 1 ? <TagsView Views={DocumentView.Selected()} background={SnappingManager.userBackgroundColor ?? ''} /> : null}
                             </div>
diff --git a/src/client/views/SidebarAnnos.scss b/src/client/views/SidebarAnnos.scss
index d7de2b641..abfd04f11 100644
--- a/src/client/views/SidebarAnnos.scss
+++ b/src/client/views/SidebarAnnos.scss
@@ -1,3 +1,13 @@
+.sidebarAnnos-container {
+    position: absolute;
+    width: 100%;
+    height: 100%;
+    right: 0;
+    .sidebarAnnos-stacking {
+        width: 100%;
+        position: relative;
+    }
+}
 .sidebarAnnos-tagList {
     display: flex;
     flex-direction: row;
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 3c0611f03..b7e6318b1 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -213,14 +213,11 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
         // 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 : (
             <div
+                className="sidebarAnnos-container"
                 style={{
-                    position: 'absolute',
                     pointerEvents: this._props.isContentActive() ? 'all' : undefined,
                     top: this._props.Doc.type !== DocumentType.RTF && StrCast(this._props.Doc._layout_showTitle) === 'title' ? 15 : 0,
-                    right: 0,
                     background: this._props.styleProvider?.(this._props.Doc, this._props, StyleProp.WidgetColor) as string,
-                    width: `100%`,
-                    height: '100%',
                 }}>
                 <div className="sidebarAnnos-tagList" style={{ height: this.filtersHeight() }} onWheel={e => e.stopPropagation()}>
                     {this.allUsers.length > 1 ? this.allUsers.map(renderUsers) : null}
@@ -228,7 +225,7 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
                     {Array.from(this.allMetadata.keys()).sort().map(renderMeta)}
                 </div>
 
-                <div style={{ width: '100%', height: `calc(100% - 38px)`, position: 'relative' }}>
+                <div className="sidebarAnnos-stacking" style={{ height: `calc(100% - ${this.filtersHeight()}px)` }}>
                     <CollectionStackingView
                         {...this._props}
                         setContentViewBox={emptyFunction}
diff --git a/src/client/views/TagsView.scss b/src/client/views/TagsView.scss
index 231d5e11a..d58b55ab5 100644
--- a/src/client/views/TagsView.scss
+++ b/src/client/views/TagsView.scss
@@ -2,13 +2,12 @@
     display: flex;
     flex-wrap: wrap;
     flex-direction: column;
-    border: 1px solid;
-    border-radius: 4px;
     width: 100%;
     position: relative;
     .tagsView-content {
         width: 100%;
         height: inherit;
+        position: absolute;
         .tagsView-list {
             display: flex;
             flex-wrap: wrap;
diff --git a/src/client/views/TagsView.tsx b/src/client/views/TagsView.tsx
index 39c952483..79c4d8af1 100644
--- a/src/client/views/TagsView.tsx
+++ b/src/client/views/TagsView.tsx
@@ -267,7 +267,6 @@ export class TagsView extends ObservableReactComponent<TagViewProps> {
         super(props);
         makeObservable(this);
     }
-    InsetDist = 25; // how far tag panel is moved up to overlap DocumentView.
 
     @observable _panelHeightDirty = 0;
     @observable _currentInput = '';
@@ -298,8 +297,8 @@ export class TagsView extends ObservableReactComponent<TagViewProps> {
 
     @computed get isEditing() {
         const selected = DocumentView.Selected().length === 1 && DocumentView.Selected().includes(this.View);
-        if (this._isEditing === undefined) return selected && !StrListCast(this.View.dataDoc.tags).length && !StrListCast(this.View.dataDoc[Doc.LayoutFieldKey(this.View.Document) + '_audioAnnotations_text']).length;
-        return this._isEditing && (this._props.Views.length > 1 || selected);
+        if (this._isEditing === undefined) return selected && this.View.TagPanelEditing; // && !StrListCast(this.View.dataDoc.tags).length && !StrListCast(this.View.dataDoc[Doc.LayoutFieldKey(this.View.Document) + '_audioAnnotations_text']).length;
+        return this._isEditing && (this._props.Views.length > 1 || (selected && this.View.TagPanelEditing));
     }
 
     /**
@@ -309,7 +308,10 @@ export class TagsView extends ObservableReactComponent<TagViewProps> {
     @action
     setToEditing = (editing = true) => {
         this._isEditing = editing;
-        editing && this._props.Views.length === 1 && this.View.select(false);
+        if (this._props.Views.length === 1) {
+            this.View.TagPanelEditing = editing;
+            editing && this.View.select(false);
+        }
     };
 
     /**
@@ -350,7 +352,16 @@ export class TagsView extends ObservableReactComponent<TagViewProps> {
         return this.View.ComponentView?.isUnstyledView?.() || (!this.View.showTags && this._props.Views.length === 1) ? null : (
             <div
                 className="tagsView-container"
-                ref={r => r && new ResizeObserver(action(() => this._props.Views.length === 1 && (this.View.TagPanelHeight = Math.max(0, (r?.getBoundingClientRect().height ?? 0) - this.InsetDist)))).observe(r)}
+                ref={r =>
+                    r &&
+                    new ResizeObserver(
+                        action(() => {
+                            if (this._props.Views.length === 1) {
+                                this.View.TagPanelHeight = Math.floor(r?.children[0].children[0].getBoundingClientRect().height ?? 0) - Math.floor(r?.children[0].children[0].children[0].getBoundingClientRect().height ?? 0);
+                            }
+                        })
+                    ).observe(r?.children[0])
+                }
                 style={{
                     display: SnappingManager.IsResizing === this.View.Document[Id] ? 'none' : undefined,
                     backgroundColor: this.isEditing ? this._props.background : Colors.TRANSPARENT,
diff --git a/src/client/views/global/globalCssVariables.module.scss b/src/client/views/global/globalCssVariables.module.scss
index ad0c5c21d..82f6caa52 100644
--- a/src/client/views/global/globalCssVariables.module.scss
+++ b/src/client/views/global/globalCssVariables.module.scss
@@ -4,7 +4,7 @@ $white: #ffffff;
 $off-white: #fdfdfd;
 $light-gray: #dfdfdf;
 $medium-gray: #9f9f9f;
-$medium-gray-dim: #9f9f9f30;
+$medium-gray-dim: #9f9f9f;
 $dark-gray: #323232;
 $black: #000000;
 
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index dd5fd0d0c..f60f33608 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -273,7 +273,9 @@
 
 .documentView-noAIWidgets {
     transform-origin: top left;
-    position: relative;
+    position: absolute;
+    bottom: 0;
+    pointer-events: none;
 }
 
 .documentView-editorView-history {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 37f888ddd..4cf60d356 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -748,7 +748,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
                         style={{
                             width: `${100 / this.uiBtnScaling}%`, //
                             transform: `scale(${this.uiBtnScaling})`,
-                            bottom: Number.isNaN(this.maxWidgetSize) ? undefined : this.maxWidgetSize,
+                            height: Number.isNaN(this.maxWidgetSize) ? undefined : this.TagsBtnHeight * this.uiBtnScaling,
                         }}>
                         {this._props.DocumentView?.() && !this._props.docViewPath().slice(-2)[0].ComponentView?.isUnstyledView?.() ? <TagsView background={this.backgroundBoxColor} Views={[this._props.DocumentView?.()]} /> : null}
                     </div>
@@ -1163,6 +1163,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
     @observable private _selected = false;
     @observable public static CurrentlyPlaying: DocumentView[] = []; // audio or video media views that are currently playing
     @observable public TagPanelHeight = 0;
+    @observable public TagPanelEditing = false;
 
     @computed get showTags() {
         return this.Document._layout_showTags || this._props.showTags;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index f7e6d8e1e..d90be007f 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -154,11 +154,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
 
     // eslint-disable-next-line no-return-assign
     @computed get config()                     { return FormattedTextBox.MakeConfig(this._rules = new RichTextRules(this.Document, this), this); } // prettier-ignore
-    @computed get recordingDictation()        { return this.dataDoc?.mediaState === mediaState.Recording; } // prettier-ignore
-    @computed get SidebarShown()               {  return !!(this._showSidebar || this.layoutDoc._layout_showSidebar);  } // prettier-ignore
+    @computed get recordingDictation()         { return this.dataDoc?.mediaState === mediaState.Recording; } // prettier-ignore
+    @computed get SidebarShown()               { return !!(this._showSidebar || this.layoutDoc._layout_showSidebar);  } // prettier-ignore
     @computed get allSidebarDocs()             { return DocListCast(this.dataDoc[this.sidebarKey]); } // prettier-ignore
     @computed get noSidebar()                  { return this.DocumentView?.()._props.hideDecorationTitle || this._props.noSidebar || this.Document._layout_noSidebar; } // prettier-ignore
-    @computed get layout_sidebarWidthPercent() { return this._showSidebar ? '20%' : StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%'); } // prettier-ignore
+    @computed get sidebarWidthPercent() { return StrCast(this.layoutDoc._layout_sidebarWidthPercent, this._showSidebar ? '20%' :'0%'); } // prettier-ignore
     @computed get sidebarColor()               { return StrCast(this.layoutDoc.sidebar_color, StrCast(this.layoutDoc[this.fieldKey + '_backgroundColor'], '#e4e4e4')); } // prettier-ignore
     @computed get layout_autoHeight()          { return (this._props.forceAutoHeight || this.layoutDoc._layout_autoHeight) && !this._props.ignoreAutoHeight; } // prettier-ignore
     @computed get textHeight()                 { return NumCast(this.dataDoc[this.fieldKey + '_height']); } // prettier-ignore
@@ -743,15 +743,21 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     @action
     toggleSidebar = (preview: boolean = false) => {
         const defaultSidebar = 250;
-        const prevWidth = 1 - this.sidebarWidth() / DivWidth(this._ref.current!);
+        const dw = DivWidth(this._ref.current);
+        const prevWidth = 1 - this.sidebarWidth() / dw / this.nativeScaling();
         if (preview) this._showSidebar = true;
         else {
-            this.layoutDoc[this.sidebarKey + '_freeform_scale_max'] = 1;
-            this.layoutDoc._layout_showSidebar =
-                (this.layoutDoc._layout_sidebarWidthPercent = StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%') === '0%' ? `${(defaultSidebar / (NumCast(this.layoutDoc._width) + defaultSidebar)) * 100}%` : '0%') !== '0%';
+            this.layoutDoc._layout_sidebarWidthPercent =
+                this.sidebarWidthPercent === '0%' //
+                    ? `${(defaultSidebar / (NumCast(this.layoutDoc._width) + defaultSidebar)) * 100}%` //
+                    : '0%';
+            this.layoutDoc._layout_showSidebar = this.sidebarWidthPercent !== '0%';
         }
 
-        this.layoutDoc._width = !preview && this.SidebarShown ? NumCast(this.layoutDoc._width) + defaultSidebar : Math.max(20, NumCast(this.layoutDoc._width) * prevWidth);
+        this.layoutDoc._width =
+            !preview && this.SidebarShown //
+                ? NumCast(this.layoutDoc._width) + defaultSidebar
+                : Math.max(20, NumCast(this.layoutDoc._width) * prevWidth);
     };
     sidebarDown = (e: React.PointerEvent) => {
         const batch = UndoManager.StartBatch('toggle sidebar');
@@ -769,7 +775,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     };
     sidebarMove = (e: PointerEvent, down: number[], delta: number[]) => {
         const localDelta = this.DocumentView?.().screenToViewTransform().transformDirection(delta[0], delta[1]) ?? delta;
-        const sidebarWidth = (NumCast(this.layoutDoc._width) * Number(this.layout_sidebarWidthPercent.replace('%', ''))) / 100;
+        const sidebarWidth = (NumCast(this.layoutDoc._width) * Number(this.sidebarWidthPercent.replace('%', ''))) / 100;
         const width = NumCast(this.layoutDoc._width) + localDelta[0];
         this.layoutDoc._layout_sidebarWidthPercent = Math.max(0, (sidebarWidth + localDelta[0]) / width) * 100 + '%';
         this.layoutDoc.width = width;
@@ -1223,7 +1229,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     }
 
     @computed get contentScaling() {
-        return Doc.NativeAspect(this.Document, this.dataDoc, false) ? this._props.NativeDimScaling?.() || 1 : 1;
+        return Doc.NativeAspect(this.Document, this.dataDoc, false) ? this.nativeScaling() : 1;
     }
     componentDidMount() {
         !this._props.dontSelectOnLoad && this._props.setContentViewBox?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
@@ -1891,7 +1897,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         }
     };
     fitContentsToBox = () => BoolCast(this.Document._freeform_fitContentsToBox);
-    sidebarContentScaling = () => (this._props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1);
+    nativeScaling = () => this._props.NativeDimScaling?.() || 1;
     sidebarAddDocument = (doc: Doc | Doc[], sidebarKey: string = this.sidebarKey) => {
         if (!this.layoutDoc._layout_showSidebar) this.toggleSidebar();
         return this.addDocument(doc, sidebarKey);
@@ -1901,12 +1907,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     setSidebarHeight = (height: number) => {
         this.dataDoc[this.sidebarKey + '_height'] = height;
     };
-    sidebarWidth = () => (Number(this.layout_sidebarWidthPercent.substring(0, this.layout_sidebarWidthPercent.length - 1)) / 100) * this._props.PanelWidth();
+    sidebarWidth = () => (Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100) * this._props.PanelWidth();
     sidebarScreenToLocal = () =>
         this._props
             .ScreenToLocalTransform()
-            .translate(-(this._props.PanelWidth() - this.sidebarWidth()) / (this._props.NativeDimScaling?.() || 1), 0)
-            .scale(1 / NumCast(this.layoutDoc._freeform_scale, 1) / (this._props.NativeDimScaling?.() || 1));
+            .translate(-(this._props.PanelWidth() - this.sidebarWidth()) / this.nativeScaling(), 0)
+            .scale(1 / this.nativeScaling());
 
     @computed get audioHandle() {
         return !this.recordingDictation ? null : (
@@ -1927,6 +1933,20 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
             </div>
         );
     }
+    private _sideBtnWidth = 35;
+    /**
+     * How much the content of the view is being scaled based on its nesting and its fit-to-width settings
+     */
+    @computed get viewScaling() { return this.ScreenToLocalBoxXf().Scale ; } // prettier-ignore
+    /**
+     * The maximum size a UI widget can be scaled so that it won't be bigger in screen pixels than its normal 35 pixel size.
+     */
+    @computed get maxWidgetSize() { return Math.min(this._sideBtnWidth, 0.2 * this._props.PanelWidth())*this.viewScaling; } // prettier-ignore
+    /**
+     * How much to reactively scale a UI element so that it is as big as it can be (up to its normal 35pixel size) without being too big for the Doc content
+     */
+    @computed get uiBtnScaling() { return this.maxWidgetSize / this._sideBtnWidth; } // prettier-ignore
+
     @computed get sidebarHandle() {
         TraceMobx();
         const annotated = DocListCast(this.dataDoc[this.sidebarKey]).filter(d => d?.author).length;
@@ -1941,7 +1961,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                     backgroundColor,
                     color,
                     opacity: annotated ? 1 : undefined,
-                    transform: `scale(${this._props.DocumentView?.().UIBtnScaling ?? 1})`,
+                    transform: `scale(${this.uiBtnScaling})`,
                 }}>
                 <FontAwesomeIcon icon="comment-alt" />
             </div>
@@ -1986,7 +2006,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                         isAnnotationOverlay={false}
                         select={emptyFunction}
                         isAnyChildContentActive={returnFalse}
-                        NativeDimScaling={this.sidebarContentScaling}
+                        NativeDimScaling={this.nativeScaling}
                         whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
                         removeDocument={this.sidebarRemDocument}
                         moveDocument={this.sidebarMoveDocument}
@@ -2003,7 +2023,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
             );
         };
         return (
-            <div className={'formattedTextBox-sidebar' + (Doc.ActiveTool !== InkTool.None ? '-inking' : '')} style={{ width: `${this.layout_sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
+            <div className={'formattedTextBox-sidebar' + (Doc.ActiveTool !== InkTool.None ? '-inking' : '')} style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
                 {renderComponent(StrCast(this.layoutDoc[this.sidebarKey + '_type_collection']))}
             </div>
         );
@@ -2061,7 +2081,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
 
         // if scrollTop is 0, then don't let wheel trigger scroll on any container (which it would since onScroll won't be triggered on this)
         if (this._props.isContentActive()) {
-            const scale = this._props.NativeDimScaling?.() || 1;
+            const scale = this.nativeScaling();
             const styleFromLayout = styleFromLayoutString(this.Document, this._props, scale); // this converts any expressions in the format string to style props.  e.g., <FormattedTextBox height='{this._header_height}px' >
             const height = Number(styleFromLayout.height?.replace('px', ''));
             // prevent default if selected || child is active  but this doc isn't scrollable
@@ -2078,8 +2098,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
 
     render() {
         TraceMobx();
-        const scale = this._props.NativeDimScaling?.() || 1;
-        const rounded = StrCast(this.layoutDoc.layout_borderRounding) === '100%' ? '-rounded' : '';
+        const scale = this.nativeScaling();
+        const rounded = StrCast(this.layoutDoc._layout_borderRounding) === '100%' ? '-rounded' : '';
         setTimeout(() => !this._props.isContentActive() && FormattedTextBoxComment.textBox === this && FormattedTextBoxComment.Hide);
 
         const scrSize = (which: number, view = this._props.docViewPath().slice(-which)[0]) => 
@@ -2107,7 +2127,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                               height: `${100 / scale}%`,
                           }),
                     transition: 'inherit',
-                    paddingBottom: this.tagsHeight,
                     // overflowY: this.layoutDoc._layout_autoHeight ? "hidden" : undefined,
                     color: this.fontColor,
                     fontSize: this.fontSize,
@@ -2140,7 +2159,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                             this._scrollRef = r;
                         }}
                         style={{
-                            width: this.noSidebar ? '100%' : `calc(100% - ${this.layout_sidebarWidthPercent})`,
+                            width: this.noSidebar ? '100%' : `calc(100% - ${this.sidebarWidthPercent})`,
                             overflow: this.layoutDoc._createDocOnCR || this.layoutDoc._layout_hideScroll ? 'hidden' : this.layout_autoHeight ? 'visible' : undefined,
                         }}
                         onScroll={this.onScroll}
@@ -2160,7 +2179,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                             }}
                         />
                     </div>
-                    {this.noSidebar || !this.SidebarShown || this.layout_sidebarWidthPercent === '0%' ? null : this.sidebarCollection}
+                    {this.noSidebar || !this.SidebarShown || this.sidebarWidthPercent === '0%' ? null : this.sidebarCollection}
                     {this.noSidebar || this.Document._layout_noSidebar || this.Document._createDocOnCR || this.layoutDoc._chromeHidden || this.Document.quiz ? null : this.sidebarHandle}
                     {this.audioHandle}
                     {this.layoutDoc._layout_enableAltContentUI && !this.layoutDoc._chromeHidden ? this.overlayAlternateIcon : null}
-- 
cgit v1.2.3-70-g09d2


From b0bb28202fa77556768d1da93bf9ea879c6ba2e5 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 10 Apr 2025 12:05:55 -0400
Subject: performance fixes for text boxes to not call screenToLocal
 unnecessarily.  simplifications to template data docs

---
 src/client/views/DocumentDecorations.scss          |   2 +-
 src/client/views/DocumentDecorations.tsx           |   2 +-
 src/client/views/StyleProvider.tsx                 |  24 +++--
 src/client/views/TagsView.scss                     |   3 +
 .../views/collections/CollectionCarouselView.scss  |   2 +
 .../views/collections/CollectionCarouselView.tsx   |  14 ++-
 .../views/collections/CollectionNoteTakingView.tsx |   6 +-
 .../views/collections/CollectionStackingView.tsx   |  13 ++-
 src/client/views/collections/CollectionSubView.tsx |   7 +-
 .../views/collections/CollectionTreeView.tsx       |  14 +--
 .../collectionGrid/CollectionGridView.tsx          |  11 +--
 .../CollectionMulticolumnView.tsx                  | 106 ++++++++++-----------
 .../CollectionMultirowView.tsx                     | 106 ++++++++++-----------
 src/client/views/nodes/DocumentView.scss           |   6 ++
 src/client/views/nodes/DocumentView.tsx            |  87 ++++++++++-------
 src/client/views/nodes/FieldView.tsx               |   2 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  16 ++--
 .../formattedText/ProsemirrorExampleTransfer.ts    |   3 +-
 src/client/views/nodes/trails/PresElementBox.tsx   |   2 +-
 19 files changed, 229 insertions(+), 197 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index 28cebe23c..09a13634f 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -40,10 +40,10 @@ $resizeHandler: 8px;
     }
     .documentDecorations-tagsView {
         position: absolute;
-        height: 100%;
         width: 100%;
         pointer-events: all;
         border-radius: 50%;
+        top: 30; // offset by height of documentButtonBar so that items can be clicked without overlap interference
         color: black;
     }
 }
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 452dd60ee..0756d3d9e 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -863,7 +863,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
                             <div
                                 className="documentDecorations-tagsView"
                                 style={{
-                                    top: 30, // offset by height of documentButtonBar so that items can be clicked without overlap interference
+                                    display: DocumentView.Selected().length > 1 || !seldocview.showTags ? 'none' : undefined,
                                     transform: `translate(${-this._resizeBorderWidth + 10}px, ${2 * this._resizeBorderWidth + bounds.b - bounds.y + this._titleHeight}px) `,
                                 }}>
                                 {DocumentView.Selected().length > 1 ? <TagsView Views={DocumentView.Selected()} background={SnappingManager.userBackgroundColor ?? ''} /> : null}
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index a3067907d..e8876f96f 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -327,7 +327,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
                         ? undefined // if it's a background & has a cluster color, make the shadow spread really big
                         : fieldKey.includes('_inline')  // if doc is an inline document in a text box
                         ? `${Colors.DARK_GRAY} ${StrCast(doc.layout_boxShadow, '0vw 0vw 0.1vw')}`
-                        :doc.rootDocument !== doc.embedContainer && DocCast(doc.embedContainer)?.type === DocumentType.RTF && !isInk() // if doc is embedded in a text document (but not an inline) and this isn't a simple text template (where the layoutDoc's rootDocument is its embed container)
+                        : doc.rootDocument !== doc.embedContainer && DocCast(doc.embedContainer)?.type === DocumentType.RTF && !isInk() // if doc is embedded in a text document (but not an inline) and this isn't a simple text template (where the layoutDoc's rootDocument is its embed container)
                         ? `${Colors.DARK_GRAY} ${StrCast(doc.layout_boxShadow, '0.2vw 0.2vw 0.8vw')}`
                         : StrCast(doc.layout_boxShadow, '');
             }
@@ -342,25 +342,28 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
             if (isDocumentActive?.()) return isInk() ? 'visiblePainted' : 'all';
             return undefined; // fixes problem with tree view elements getting pointer events when the tree view is not active
         case StyleProp.Decorations: {
-            const lock = () => doc?.pointerEvents !== 'none' ? null : (
+            const showLock = doc?.pointerEvents === 'none'
+            const lock = () =>  !showLock ? null : (
                         <div className="styleProvider-lock" onClick={() => toggleLockedPosition(doc)}>
                             <FontAwesomeIcon icon='lock' size="lg" />
                         </div>
                     );
-            const paint = () => !doc?.onPaint ? null : (
+            const showPaint = doc?.onPaint;
+            const paint = () => !showPaint ? null : (
                         <div className={`styleProvider-paint${isSelected?.() ? "-selected":""}`} onClick={e => togglePaintView(e, doc, props)}>
                             <FontAwesomeIcon icon='pen' size="lg" />
                         </div>
                     );
+            const showFilterIcon =
+                StrListCast(doc?._childFilters).length || StrListCast(doc?._childFiltersByRanges).length
+                    ? 'green' // #18c718bd' //'hasFilter'
+                    : childFilters?.().filter(f => ClientUtils.IsRecursiveFilter(f) && f !== ClientUtils.noDragDocsFilter).length || childFiltersByRanges?.().length
+                    ? 'orange' // 'inheritsFilter'
+                    : undefined;
+            const showFilter = showFilterIcon && !hideFilterStatus;
             const filter = () => {
                 const dashView = untracked(() => DocumentView.getDocumentView(Doc.ActiveDashboard));
-                const showFilterIcon =
-                    StrListCast(doc?._childFilters).length || StrListCast(doc?._childFiltersByRanges).length
-                        ? 'green' // #18c718bd' //'hasFilter'
-                        : childFilters?.().filter(f => ClientUtils.IsRecursiveFilter(f) && f !== ClientUtils.noDragDocsFilter).length || childFiltersByRanges?.().length
-                        ? 'orange' // 'inheritsFilter'
-                        : undefined;
-                return !showFilterIcon || hideFilterStatus ? null : (
+                return !showFilter ? null : (
                     <div className="styleProvider-filter">
                         <Dropdown
                             type={Type.TERT}
@@ -394,6 +397,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
             };
         
             return (
+                !showPaint && !showLock && !showFilter ? null:
                 <>
                     {paint()}
                     {lock()}
diff --git a/src/client/views/TagsView.scss b/src/client/views/TagsView.scss
index d58b55ab5..a940502d4 100644
--- a/src/client/views/TagsView.scss
+++ b/src/client/views/TagsView.scss
@@ -16,6 +16,9 @@
                 min-height: unset !important;
             }
         }
+        .tagsView-editing-box {
+            pointer-events: all;
+        }
     }
 }
 
diff --git a/src/client/views/collections/CollectionCarouselView.scss b/src/client/views/collections/CollectionCarouselView.scss
index 1080aa703..962b590c8 100644
--- a/src/client/views/collections/CollectionCarouselView.scss
+++ b/src/client/views/collections/CollectionCarouselView.scss
@@ -9,6 +9,8 @@
         height: 50;
         display: inline-block;
         width: 100%;
+        bottom: 0;
+        position: absolute;
     }
     .collectionCarouselView-image {
         height: calc(100% - 50px);
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index c80ac23eb..2d3f8cb0e 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -16,7 +16,6 @@ import { FocusViewOptions } from '../nodes/FocusViewOptions';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import './CollectionCarouselView.scss';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
-import { DocData } from '../../../fields/DocSymbols';
 
 @observer
 export class CollectionCarouselView extends CollectionSubView() {
@@ -116,12 +115,14 @@ export class CollectionCarouselView extends CollectionSubView() {
                 ? false
                 : undefined; // prettier-ignore
     onPassiveWheel = (e: WheelEvent) => e.stopPropagation();
+
     renderDoc = (doc: Doc, showCaptions: boolean, overlayFunc?: (r: DocumentView | null) => void) => {
         return (
             <DocumentView
                 {...this._props}
                 ref={overlayFunc}
                 Document={doc}
+                TemplateDataDocument={doc.isTemplateDoc || doc.isTemplateForField ? this._props.TemplateDataDocument : undefined}
                 NativeWidth={returnZero}
                 NativeHeight={returnZero}
                 fitWidth={this._props.childLayoutFitWidth}
@@ -137,7 +138,6 @@ export class CollectionCarouselView extends CollectionSubView() {
                 renderDepth={this._props.renderDepth + 1}
                 LayoutTemplate={this._props.childLayoutTemplate}
                 LayoutTemplateString={this._props.childLayoutString}
-                TemplateDataDocument={doc[DocData]}
                 childFilters={this.childDocFilters}
                 focus={this.focus}
                 hideDecorations={BoolCast(this.layoutDoc.layout_hideDecorations)}
@@ -183,7 +183,15 @@ export class CollectionCarouselView extends CollectionSubView() {
     }
 
     @computed get content() {
-        const captionProps = { ...this._props, NativeScaling: returnOne, PanelWidth: this.captionWidth, fieldKey: 'caption', setHeight: undefined, setContentView: undefined };
+        const captionProps = {
+            ...this._props, //
+            NativeScaling: returnOne,
+            PanelWidth: this.captionWidth,
+            fieldKey: 'caption',
+            setHeight: undefined,
+            setContentView: undefined,
+            noSidebar: true,
+        };
         const carouselShowsCaptions = StrCast(this.layoutDoc._layout_showCaption);
         return !this.curDoc() ? null : (
             <>
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index 01695dbaf..173147f64 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -258,11 +258,9 @@ export class CollectionNoteTakingView extends CollectionSubView() {
         const noteTakingDocTransform = () => this.getDocTransform(doc, dref);
         return (
             <DocumentView
-                ref={r => {
-                    dref = r || undefined;
-                }}
+                ref={r => (dref = r || undefined)}
                 Document={doc}
-                TemplateDataDocument={dataDoc ?? (!Doc.AreProtosEqual(doc[DocData], doc) ? doc[DocData] : undefined)}
+                TemplateDataDocument={doc.isTemplateDoc || doc.isTemplateForField ? this._props.TemplateDataDocument : undefined}
                 pointerEvents={this.blockPointerEventsWhenDragging}
                 renderDepth={this._props.renderDepth + 1}
                 PanelWidth={width}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index d7cf0ffc2..4f60acb18 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -675,12 +675,12 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
     }
 
     return35 = () => 35;
+    @computed get menuBtnDoc() { return DocCast(this.layoutDoc.layout_headerButton); } // prettier-ignore
     @computed get buttonMenu() {
-        const menuDoc = DocCast(this.layoutDoc.layout_headerButton);
-        return !menuDoc ? null : (
-            <div className="buttonMenu-docBtn" style={{ width: NumCast(menuDoc._width, 30), height: NumCast(menuDoc._height, 30) }}>
+        return !this.menuBtnDoc ? null : (
+            <div className="buttonMenu-docBtn" style={{ width: NumCast(this.menuBtnDoc._width, 30), height: NumCast(this.menuBtnDoc._height, 30) }}>
                 <DocumentView
-                    Document={menuDoc}
+                    Document={this.menuBtnDoc}
                     isContentActive={this.isContentActive}
                     isDocumentActive={this.isContentActive}
                     addDocument={this._props.addDocument}
@@ -728,13 +728,12 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             SetValue: this.addGroup,
             contents: '+ ADD A GROUP',
         };
-        const buttonMenu = this.layoutDoc.layout_headerButton;
         const noviceExplainer = this.layoutDoc.layout_explainer;
         return (
             <>
-                {buttonMenu || noviceExplainer ? (
+                {this.menuBtnDoc || noviceExplainer ? (
                     <div className="documentButtonMenu">
-                        {buttonMenu ? this.buttonMenu : null}
+                        {this.menuBtnDoc ? this.buttonMenu : null}
                         {Doc.noviceMode && noviceExplainer ? <div className="documentExplanation">{StrCast(noviceExplainer)}</div> : null}
                     </div>
                 ) : null}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index a88707c6f..375c0fe53 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -603,7 +603,12 @@ export function CollectionSubView<X>() {
          */
         @computed get uiBtnScaling() { return this.maxWidgetSize / this._sideBtnWidth; } // prettier-ignore
 
-        screenXPadding = () => (this.uiBtnScaling * this._sideBtnWidth - NumCast(this.layoutDoc.xMargin)) / this._props.ScreenToLocalTransform().Scale;
+        screenXPadding = (docView?: DocumentView) => {
+            if (!docView) return 0;
+            const diff = this._props.PanelWidth() - docView.PanelWidth();
+            const xpad1 = this.uiBtnScaling * (this._sideBtnWidth - NumCast(this.layoutDoc.xMargin)) - diff / 2; // this._sideBtnWidth;
+            return xpad1 / (docView.NativeDimScaling?.() || 1);
+        };
         filteredChildDocs = () => this.childLayoutPairs.map(pair => pair.layout);
         childDocsFunc = () => this.childDocs;
         @action setFilterFunc = (func?: (doc: Doc) => boolean) => { this._filterFunc = func; }; // prettier-ignore
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 418c437d5..962fbdcd7 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -8,7 +8,7 @@ import { DocData } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { listSpec } from '../../../fields/Schema';
 import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast, toList } from '../../../fields/Types';
+import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast, toList } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
 import { emptyFunction, Utils } from '../../../Utils';
 import { Docs } from '../../documents/Documents';
@@ -200,7 +200,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     onContextMenu = (): void => {
         // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
         const layoutItems: ContextMenuProps[] = [];
-        const menuDoc = ScriptCast(Cast(this.layoutDoc.layout_headerButton, Doc, null)?.onClick)?.script.originalScript === CollectionTreeView.AddTreeFunc;
+        const menuDoc = ScriptCast(this.menuBtnDoc?.onClick)?.script.originalScript === CollectionTreeView.AddTreeFunc;
         menuDoc && layoutItems.push({ description: 'Create new folder', event: () => CollectionTreeView.addTreeFolder(this.Document), icon: 'paint-brush' });
         if (!Doc.noviceMode) {
             layoutItems.push({
@@ -347,14 +347,14 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     }
 
     return35 = () => 35;
+    @computed get menuBtnDoc() { return DocCast(this.layoutDoc.layout_headerButton); } // prettier-ignore
     @computed get buttonMenu() {
-        const menuDoc = Cast(this.layoutDoc.layout_headerButton, Doc, null);
         // To create a multibutton menu add a CollectionLinearView
-        return !menuDoc ? null : (
-            <div className="buttonMenu-docBtn" style={{ width: NumCast(menuDoc._width, 30), height: NumCast(menuDoc._height, 30) }}>
+        return !this.menuBtnDoc ? null : (
+            <div className="buttonMenu-docBtn" style={{ width: NumCast(this.menuBtnDoc._width, 30), height: NumCast(this.menuBtnDoc._height, 30) }}>
                 <DocumentView
-                    Document={menuDoc}
-                    TemplateDataDocument={menuDoc}
+                    Document={this.menuBtnDoc}
+                    TemplateDataDocument={this.menuBtnDoc}
                     isContentActive={this._props.isContentActive}
                     isDocumentActive={returnTrue}
                     addDocument={this._props.addDocument}
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index 8393c719b..a7603a45b 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -4,7 +4,7 @@ import * as React from 'react';
 import { returnFalse, returnZero, setupMoveUpEvents } from '../../../../ClientUtils';
 import { Doc, Opt } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
-import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
+import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { emptyFunction } from '../../../../Utils';
 import { Docs } from '../../../documents/Documents';
 import { DragManager } from '../../../util/DragManager';
@@ -16,7 +16,6 @@ import { DocumentView } from '../../nodes/DocumentView';
 import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView';
 import './CollectionGridView.scss';
 import Grid, { Layout } from './Grid';
-import { DocData } from '../../../../fields/DocSymbols';
 
 @observer
 export class CollectionGridView extends CollectionSubView() {
@@ -183,17 +182,17 @@ export class CollectionGridView extends CollectionSubView() {
     isChildContentActive = () => (this._props.isDocumentActive?.() && (this._props.childDocumentsActive?.() || BoolCast(this.Document.childDocumentsActive)) ? true : undefined);
     /**
      *
-     * @param layout
+     * @param childLayout
      * @param dxf the x- and y-translations of the decorations box as a transform i.e. this.lookupIndividualTransform
      * @param width
      * @param height
      * @returns the `ContentFittingDocumentView` of the node
      */
-    getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => (
+    getDisplayDoc = (childLayout: Doc, dxf: () => Transform, width: () => number, height: () => number) => (
         <DocumentView
             {...this._props}
-            Document={layout}
-            TemplateDataDocument={DocCast(layout.rootDocument)?.[DocData]}
+            Document={childLayout}
+            TemplateDataDocument={childLayout.isTemplateDoc || childLayout.isTemplateForField ? this._props.TemplateDataDocument : undefined}
             NativeWidth={returnZero}
             NativeHeight={returnZero}
             fitWidth={this._props.childLayoutFitWidth}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index f27398427..42b093cc7 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -6,7 +6,7 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import { FaChevronRight } from 'react-icons/fa';
 import { Doc, DocListCast } from '../../../../fields/Doc';
-import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
+import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { DragManager } from '../../../util/DragManager';
 import { SettingsManager } from '../../../util/SettingsManager';
 import { Transform } from '../../../util/Transform';
@@ -18,7 +18,7 @@ import ResizeBar from './MulticolumnResizer';
 import WidthLabel from './MulticolumnWidthLabel';
 import { dropActionType } from '../../../util/DropActionTypes';
 import { SnappingManager } from '../../../util/SnappingManager';
-import { DocData } from '../../../../fields/DocSymbols';
+import { computedFn } from 'mobx-utils';
 
 interface WidthSpecifier {
     magnitude: number;
@@ -198,15 +198,14 @@ export class CollectionMulticolumnView extends CollectionSubView() {
      * documents before the target.
      */
     private lookupIndividualTransform = (layout: Doc) => {
-        if (this.columnUnitLength === undefined) {
-            return Transform.Identity(); // we're still waiting on promises to resolve
-        }
-        let offset = 0;
-        for (const { layout: candidate } of this.childLayoutPairs) {
-            if (candidate === layout) {
-                return this.ScreenToLocalBoxXf().translate(-offset / (this._props.NativeDimScaling?.() || 1), 0);
+        if (this.columnUnitLength !== undefined) {
+            let offset = 0;
+            for (const { layout: candidate } of this.childLayoutPairs) {
+                if (candidate === layout) {
+                    return this.ScreenToLocalBoxXf().translate(-offset / (this._props.NativeDimScaling?.() || 1), 0);
+                }
+                offset += this.lookupPixels(candidate) + resizerWidth;
             }
-            offset += this.lookupPixels(candidate) + resizerWidth;
         }
         return Transform.Identity();
     };
@@ -262,50 +261,51 @@ export class CollectionMulticolumnView extends CollectionSubView() {
               ? true
               : undefined;
     };
-    getDisplayDoc = (childLayout: Doc) => {
-        const width = () => this.lookupPixels(childLayout);
-        const height = () => this._props.PanelHeight() - 2 * NumCast(this.layoutDoc._yMargin) - (BoolCast(this.layoutDoc.showWidthLabels) ? 20 : 0);
-        const dxf = () =>
-            this.lookupIndividualTransform(childLayout)
+    childHeight = () => this._props.PanelHeight() - 2 * NumCast(this.layoutDoc._yMargin) - (BoolCast(this.layoutDoc.showWidthLabels) ? 20 : 0);
+    childWidth = computedFn((childDoc: Doc) => () => this.lookupPixels(childDoc));
+    childXf = computedFn(
+        (childDoc: Doc) => () =>
+            this.lookupIndividualTransform(childDoc)
                 .translate(-NumCast(this.layoutDoc._xMargin), -NumCast(this.layoutDoc._yMargin))
-                .scale(this._props.NativeDimScaling?.() || 1);
-        return (
-            <DocumentView
-                Document={childLayout}
-                TemplateDataDocument={DocCast(childLayout.rootDocument)?.[DocData]}
-                styleProvider={this._props.styleProvider}
-                containerViewPath={this.childContainerViewPath}
-                LayoutTemplate={this._props.childLayoutTemplate}
-                LayoutTemplateString={this._props.childLayoutString}
-                renderDepth={this._props.renderDepth + 1}
-                PanelWidth={width}
-                PanelHeight={height}
-                rootSelected={this.rootSelected}
-                dragAction={StrCast(this.Document.childDragAction, this._props.childDragAction) as dropActionType}
-                onClickScript={this.onChildClickHandler}
-                onDoubleClickScript={this.onChildDoubleClickHandler}
-                suppressSetHeight
-                ScreenToLocalTransform={dxf}
-                isContentActive={this.isChildContentActive}
-                isDocumentActive={this._props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this._props.isDocumentActive : this.isContentActive}
-                hideResizeHandles={!!(childLayout.layout_fitWidth || this._props.childHideResizeHandles)}
-                hideDecorationTitle={this._props.childHideDecorationTitle}
-                fitContentsToBox={this._props.fitContentsToBox}
-                focus={this._props.focus}
-                childFilters={this.childDocFilters}
-                childFiltersByRanges={this.childDocRangeFilters}
-                searchFilterDocs={this.searchFilterDocs}
-                dontRegisterView={this._props.dontRegisterView}
-                addDocument={this._props.addDocument}
-                moveDocument={this._props.moveDocument}
-                removeDocument={this._props.removeDocument}
-                whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
-                addDocTab={this._props.addDocTab}
-                pinToPres={this._props.pinToPres}
-                dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as 'x' | 'y' | 'xy'}
-            />
-        );
-    };
+                .scale(this._props.NativeDimScaling?.() || 1)
+    );
+    getDisplayDoc = (childLayout: Doc) => (
+        <DocumentView
+            Document={childLayout}
+            TemplateDataDocument={childLayout.isTemplateDoc || childLayout.isTemplateForField ? this._props.TemplateDataDocument : undefined}
+            styleProvider={this._props.styleProvider}
+            containerViewPath={this.childContainerViewPath}
+            LayoutTemplate={this._props.childLayoutTemplate}
+            LayoutTemplateString={this._props.childLayoutString}
+            renderDepth={this._props.renderDepth + 1}
+            PanelWidth={this.childWidth(childLayout)}
+            PanelHeight={this.childHeight}
+            rootSelected={this.rootSelected}
+            dragAction={StrCast(this.Document.childDragAction, this._props.childDragAction) as dropActionType}
+            onClickScript={this.onChildClickHandler}
+            onDoubleClickScript={this.onChildDoubleClickHandler}
+            suppressSetHeight
+            ScreenToLocalTransform={this.childXf(childLayout)}
+            isContentActive={this.isChildContentActive}
+            isDocumentActive={this._props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this._props.isDocumentActive : this.isContentActive}
+            hideResizeHandles={!!(childLayout.layout_fitWidth || this._props.childHideResizeHandles)}
+            hideDecorationTitle={this._props.childHideDecorationTitle}
+            fitContentsToBox={this._props.fitContentsToBox}
+            focus={this._props.focus}
+            childFilters={this.childDocFilters}
+            childFiltersByRanges={this.childDocRangeFilters}
+            searchFilterDocs={this.searchFilterDocs}
+            dontRegisterView={this._props.dontRegisterView}
+            addDocument={this._props.addDocument}
+            moveDocument={this._props.moveDocument}
+            removeDocument={this._props.removeDocument}
+            whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
+            addDocTab={this._props.addDocTab}
+            pinToPres={this._props.pinToPres}
+            dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as 'x' | 'y' | 'xy'}
+        />
+    );
+
     /**
      * @returns the resolved list of rendered child documents, displayed
      * at their resolved pixel widths, each separated by a resizer.
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index f95928cf6..6bbe55f54 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -2,7 +2,7 @@ import { action, computed, makeObservable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast } from '../../../../fields/Doc';
-import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
+import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { DragManager } from '../../../util/DragManager';
 import { dropActionType } from '../../../util/DropActionTypes';
 import { Transform } from '../../../util/Transform';
@@ -11,7 +11,7 @@ import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView'
 import './CollectionMultirowView.scss';
 import HeightLabel from './MultirowHeightLabel';
 import ResizeBar from './MultirowResizer';
-import { DocData } from '../../../../fields/DocSymbols';
+import { computedFn } from 'mobx-utils';
 
 interface HeightSpecifier {
     magnitude: number;
@@ -179,16 +179,14 @@ export class CollectionMultirowView extends CollectionSubView() {
      * documents before the target.
      */
     private lookupIndividualTransform = (layout: Doc) => {
-        if (this.rowUnitLength === undefined) {
-            return Transform.Identity(); // we're still waiting on promises to resolve
-        }
-        let offset = 0;
-        // eslint-disable-next-line no-restricted-syntax
-        for (const { layout: candidate } of this.childLayoutPairs) {
-            if (candidate === layout) {
-                return this.ScreenToLocalBoxXf().translate(0, -offset / (this._props.NativeDimScaling?.() || 1));
+        if (this.rowUnitLength !== undefined) {
+            let offset = 0;
+            for (const { layout: candidate } of this.childLayoutPairs) {
+                if (candidate === layout) {
+                    return this.ScreenToLocalBoxXf().translate(0, -offset / (this._props.NativeDimScaling?.() || 1));
+                }
+                offset += this.lookupPixels(candidate) + resizerHeight;
             }
-            offset += this.lookupPixels(candidate) + resizerHeight;
         }
         return Transform.Identity(); // type coersion, this case should never be hit
     };
@@ -244,49 +242,51 @@ export class CollectionMultirowView extends CollectionSubView() {
               ? true
               : undefined;
     };
-    getDisplayDoc = (layout: Doc) => {
-        const height = () => this.lookupPixels(layout);
-        const width = () => this._props.PanelWidth() - 2 * NumCast(this.layoutDoc._xMargin) - (BoolCast(this.layoutDoc.showWidthLabels) ? 20 : 0);
-        const dxf = () =>
-            this.lookupIndividualTransform(layout)
+    childHeight = computedFn((childDoc: Doc) => () => this.lookupPixels(childDoc));
+    childWidth = () => this._props.PanelWidth() - 2 * NumCast(this.layoutDoc._xMargin) - (BoolCast(this.layoutDoc.showWidthLabels) ? 20 : 0);
+    childXf = computedFn(
+        (childDoc: Doc) => () =>
+            this.lookupIndividualTransform(childDoc)
                 .translate(-NumCast(this.layoutDoc._xMargin), -NumCast(this.layoutDoc._yMargin))
-                .scale(this._props.NativeDimScaling?.() || 1);
-        return (
-            <DocumentView
-                Document={layout}
-                TemplateDataDocument={DocCast(layout.rootDocument)?.[DocData]}
-                styleProvider={this._props.styleProvider}
-                containerViewPath={this.childContainerViewPath}
-                LayoutTemplate={this._props.childLayoutTemplate}
-                LayoutTemplateString={this._props.childLayoutString}
-                renderDepth={this._props.renderDepth + 1}
-                PanelWidth={width}
-                PanelHeight={height}
-                rootSelected={this.rootSelected}
-                dragAction={StrCast(this.Document.childDragAction, this._props.childDragAction) as dropActionType}
-                onClickScript={this.onChildClickHandler}
-                onDoubleClickScript={this.onChildDoubleClickHandler}
-                ScreenToLocalTransform={dxf}
-                isContentActive={this.isChildContentActive}
-                isDocumentActive={this._props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this._props.isDocumentActive : this.isContentActive}
-                hideResizeHandles={!!(layout.layout_fitWidth || this._props.childHideResizeHandles)}
-                hideDecorationTitle={this._props.childHideDecorationTitle}
-                fitContentsToBox={this._props.fitContentsToBox}
-                focus={this._props.focus}
-                childFilters={this.childDocFilters}
-                childFiltersByRanges={this.childDocRangeFilters}
-                searchFilterDocs={this.searchFilterDocs}
-                dontRegisterView={this._props.dontRegisterView}
-                addDocument={this._props.addDocument}
-                moveDocument={this._props.moveDocument}
-                removeDocument={this._props.removeDocument}
-                whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
-                addDocTab={this._props.addDocTab}
-                pinToPres={this._props.pinToPres}
-                dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as 'y' | 'x' | 'xy'}
-            />
-        );
-    };
+                .scale(this._props.NativeDimScaling?.() || 1)
+    );
+
+    getDisplayDoc = (childLayout: Doc) => (
+        <DocumentView
+            Document={childLayout}
+            TemplateDataDocument={childLayout.isTemplateDoc || childLayout.isTemplateForField ? this._props.TemplateDataDocument : undefined}
+            styleProvider={this._props.styleProvider}
+            containerViewPath={this.childContainerViewPath}
+            LayoutTemplate={this._props.childLayoutTemplate}
+            LayoutTemplateString={this._props.childLayoutString}
+            renderDepth={this._props.renderDepth + 1}
+            PanelWidth={this.childWidth}
+            PanelHeight={this.childHeight(childLayout)}
+            rootSelected={this.rootSelected}
+            dragAction={StrCast(this.Document.childDragAction, this._props.childDragAction) as dropActionType}
+            onClickScript={this.onChildClickHandler}
+            onDoubleClickScript={this.onChildDoubleClickHandler}
+            ScreenToLocalTransform={this.childXf(childLayout)}
+            isContentActive={this.isChildContentActive}
+            isDocumentActive={this._props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this._props.isDocumentActive : this.isContentActive}
+            hideResizeHandles={!!(childLayout.layout_fitWidth || this._props.childHideResizeHandles)}
+            hideDecorationTitle={this._props.childHideDecorationTitle}
+            fitContentsToBox={this._props.fitContentsToBox}
+            focus={this._props.focus}
+            childFilters={this.childDocFilters}
+            childFiltersByRanges={this.childDocRangeFilters}
+            searchFilterDocs={this.searchFilterDocs}
+            dontRegisterView={this._props.dontRegisterView}
+            addDocument={this._props.addDocument}
+            moveDocument={this._props.moveDocument}
+            removeDocument={this._props.removeDocument}
+            whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
+            addDocTab={this._props.addDocTab}
+            pinToPres={this._props.pinToPres}
+            dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as 'y' | 'x' | 'xy'}
+        />
+    );
+
     /**
      * @returns the resolved list of rendered child documents, displayed
      * at their resolved pixel widths, each separated by a resizer.
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index f60f33608..5ac66f2cd 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -277,6 +277,12 @@
     bottom: 0;
     pointer-events: none;
 }
+.documentView-widgetDecorations {
+    transform-origin: top right;
+    position: absolute;
+    top: 0;
+    right: 0;
+}
 
 .documentView-editorView-history {
     position: absolute;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index b03dd3944..bec5ea5c1 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -712,6 +712,54 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
 
     aiContentsWidth = () => (this.aiContentsHeight() * (this._props.NativeWidth?.() || 1)) / (this._props.NativeHeight?.() || 1);
     aiContentsHeight = () => Math.max(10, this._props.PanelHeight() - this._aiWinHeight * this.uiBtnScaling);
+    aiEditor = () => (
+        <>
+            <div
+                className="documentView-editorView-history"
+                ref={r => this.historyRef(this._oldAiWheel, (this._oldAiWheel = r))}
+                style={{
+                    transform: `scale(${this.uiBtnScaling})`,
+                    height: this.aiContentsHeight() / this.uiBtnScaling,
+                    width: ((this._props.PanelWidth() - this.aiContentsWidth()) * 0.95) / this.uiBtnScaling,
+                }}>
+                {this._componentView?.componentAIViewHistory?.() ?? null}
+            </div>
+            <div
+                className="documentView-editorView"
+                style={{
+                    background: SnappingManager.userVariantColor,
+                    width: `${100 / this.uiBtnScaling}%`, //
+                    transform: `scale(${this.uiBtnScaling})`,
+                }}
+                ref={r => this.historyRef(this._oldHistoryWheel, (this._oldHistoryWheel = r))}>
+                <div className="documentView-editorView-resizer" />
+                {this._componentView?.componentAIView?.() ?? null}
+                {this._props.DocumentView?.() ? <TagsView background={this.backgroundBoxColor} Views={[this._props.DocumentView?.()]} /> : null}
+            </div>
+        </>
+    );
+    @computed get tagsOverlay() {
+        return (
+            <div
+                className="documentView-noAiWidgets"
+                style={{
+                    width: `${100 / this.uiBtnScaling}%`, //
+                    transform: `scale(${this.uiBtnScaling})`,
+                    height: Number.isNaN(this.maxWidgetSize) ? undefined : this.TagsBtnHeight * this.uiBtnScaling,
+                }}>
+                {this._props.DocumentView?.() && !this._props.docViewPath().slice(-2)[0].ComponentView?.isUnstyledView?.() ? <TagsView background={this.backgroundBoxColor} Views={[this._props.DocumentView?.()]} /> : null}
+            </div>
+        );
+    }
+    tagsOverlayFunc = () => (this._props.DocumentView?.().showTags ? this.tagsOverlay : null);
+    @computed get widgetOverlay() {
+        return (
+            <div className="documentView-widgetDecorations" style={{ transform: `scale(${this.uiBtnScaling})` }}>
+                {this.widgetDecorations}
+            </div>
+        );
+    }
+    widgetOverlayFunc = () => (this.widgetDecorations ? this.widgetOverlay : null);
     @computed get viewBoxContents() {
         TraceMobx();
         const isInk = this.layoutDoc._layout_isSvg && !this._props.LayoutTemplateString;
@@ -742,43 +790,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
                         hideClickBehaviors={BoolCast(this.Document.hideClickBehaviors)}
                     />
                 </div>
-                {!this._props.showAIEditor ? (
-                    <div
-                        className="documentView-noAiWidgets"
-                        style={{
-                            width: `${100 / this.uiBtnScaling}%`, //
-                            transform: `scale(${this.uiBtnScaling})`,
-                            height: Number.isNaN(this.maxWidgetSize) ? undefined : this.TagsBtnHeight * this.uiBtnScaling,
-                        }}>
-                        {this._props.DocumentView?.() && !this._props.docViewPath().slice(-2)[0].ComponentView?.isUnstyledView?.() ? <TagsView background={this.backgroundBoxColor} Views={[this._props.DocumentView?.()]} /> : null}
-                    </div>
-                ) : (
-                    <>
-                        <div
-                            className="documentView-editorView-history"
-                            ref={r => this.historyRef(this._oldAiWheel, (this._oldAiWheel = r))}
-                            style={{
-                                transform: `scale(${this.uiBtnScaling})`,
-                                height: this.aiContentsHeight() / this.uiBtnScaling,
-                                width: ((this._props.PanelWidth() - this.aiContentsWidth()) * 0.95) / this.uiBtnScaling,
-                            }}>
-                            {this._componentView?.componentAIViewHistory?.() ?? null}
-                        </div>
-                        <div
-                            className="documentView-editorView"
-                            style={{
-                                background: SnappingManager.userVariantColor,
-                                width: `${100 / this.uiBtnScaling}%`, //
-                                transform: `scale(${this.uiBtnScaling})`,
-                            }}
-                            ref={r => this.historyRef(this._oldHistoryWheel, (this._oldHistoryWheel = r))}>
-                            <div className="documentView-editorView-resizer" />
-                            {this._componentView?.componentAIView?.() ?? null}
-                            {this._props.DocumentView?.() ? <TagsView background={this.backgroundBoxColor} Views={[this._props.DocumentView?.()]} /> : null}
-                        </div>
-                    </>
-                )}
-                <div style={{ transformOrigin: 'top right', transform: `scale(${this.uiBtnScaling})`, position: 'absolute', top: 0, right: 0 }}>{this.widgetDecorations ?? null}</div>
+                {this._props.showAIEditor ? this.aiEditor() : this.tagsOverlayFunc()}
+                {this.widgetOverlayFunc()}
             </>
         );
     }
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 74059bd12..ad6d93d43 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -50,7 +50,7 @@ export interface FieldViewSharedProps {
     TemplateDataDocument?: Doc;
     renderDepth: number;
     scriptContext?: unknown; // can be assigned anything and will be passed as 'scriptContext' to any OnClick script that executes on this document
-    screenXPadding?: () => number; // padding in screen space coordinates (used by text box to reflow around UI buttons in carouselView)
+    screenXPadding?: (view: DocumentView | undefined) => number; // padding in screen space coordinates (used by text box to reflow around UI buttons in carouselView)
     xPadding?: number;
     yPadding?: number;
     dontRegisterView?: boolean;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 6fced76bd..a45b8a488 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -215,7 +215,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     }
 
     getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
-        const rootDoc: Doc = Doc.isTemplateDoc(this._props.docViewPath().lastElement()?.Document) ? this.Document : DocCast(this.Document.rootDocument, this.Document);
+        const rootDoc = Doc.isTemplateDoc(this._props.docViewPath().lastElement()?.Document) ? this.Document : this.rootDoc;
         if (!pinProps && this.EditorView?.state.selection.empty) return rootDoc;
         const anchor = Docs.Create.ConfigDocument({ title: StrCast(rootDoc.title), annotationOn: rootDoc });
         this.addDocument(anchor);
@@ -308,7 +308,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
 
     leafText = (node: Node) => {
         if (node.type === this.EditorView?.state.schema.nodes.dashField) {
-            const refDoc = !node.attrs.docId ? DocCast(this.Document.rootDocument, this.Document) : (DocServer.GetCachedRefField(node.attrs.docId as string) as Doc);
+            const refDoc = !node.attrs.docId ? this.rootDoc : (DocServer.GetCachedRefField(node.attrs.docId as string) as Doc);
             const fieldKey = StrCast(node.attrs.fieldKey);
             return (
                 (node.attrs.hideKey ? '' : fieldKey + ':') + //
@@ -316,7 +316,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
             );
         }
         if (node.type === this.EditorView?.state.schema.nodes.dashDoc) {
-            const refDoc = !node.attrs.docId ? DocCast(this.Document.rootDocument, this.Document) : (DocServer.GetCachedRefField(node.attrs.docId as string) as Doc);
+            const refDoc = !node.attrs.docId ? this.rootDoc : (DocServer.GetCachedRefField(node.attrs.docId as string) as Doc);
             return refDoc[ToString]();
         }
         return '';
@@ -1522,8 +1522,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
             this._editorView.TextView = this;
         }
 
-        const selectOnLoad =
-            Doc.AreProtosEqual(this._props.TemplateDataDocument ?? DocCast(this.Document.rootDocument, this.Document), DocumentView.SelectOnLoad) && (!DocumentView.LightboxDoc() || DocumentView.LightboxContains(this.DocumentView?.()));
+        const selectOnLoad = Doc.AreProtosEqual(this._props.TemplateDataDocument ?? this.rootDoc, DocumentView.SelectOnLoad) && (!DocumentView.LightboxDoc() || DocumentView.LightboxContains(this.DocumentView?.()));
         const selLoadChar = FormattedTextBox.SelectOnLoadChar;
         if (selectOnLoad) {
             DocumentView.SetSelectOnLoad(undefined);
@@ -2102,11 +2101,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         const rounded = StrCast(this.layoutDoc._layout_borderRounding) === '100%' ? '-rounded' : '';
         setTimeout(() => !this._props.isContentActive() && FormattedTextBoxComment.textBox === this && FormattedTextBoxComment.Hide);
 
-        const scrSize = (which: number, view = this._props.docViewPath().slice(-which)[0]) => 
-                [view?._props.PanelWidth() /(view?.screenToLocalScale()??1), view?._props.PanelHeight() / (view?.screenToLocalScale()??1)]; // prettier-ignore
-        const scrMargin = [Math.max(0, (scrSize(2)[0] - scrSize(1)[0]) / 2), Math.max(0, (scrSize(2)[1] - scrSize(1)[1]) / 2)];
-        const paddingX = Math.max(NumCast(this.layoutDoc._xMargin), this._props.xPadding ?? 0, 0, ((this._props.screenXPadding?.() ?? 0) - scrMargin[0]) * this.ScreenToLocalBoxXf().Scale);
-        const paddingY = Math.max(NumCast(this.layoutDoc._yMargin), 0, ((this._props.yPadding ?? 0) - scrMargin[1]) * this.ScreenToLocalBoxXf().Scale);
+        const paddingX = Math.max(NumCast(this.layoutDoc._xMargin), 0, this._props.xPadding ?? 0, this._props.screenXPadding?.(this._props.DocumentView?.()) ?? 0);
+        const paddingY = Math.max(NumCast(this.layoutDoc._yMargin), 0, this._props.yPadding ?? 0); // prettier-ignore
         const styleFromLayout = styleFromLayoutString(this.Document, this._props, scale); // this converts any expressions in the format string to style props.  e.g., <FormattedTextBox height='{this._header_height}px' >
         return this.isLabel ? (
             <LabelBox {...this._props} />
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index eabc6455f..7ae1fc202 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -50,8 +50,7 @@ export function buildKeymap<S extends Schema<string>>(schema: S, tbox?: Formatte
 
     const canEdit = (state: EditorState) => {
         if (!tbox) return true;
-        const permissions = GetEffectiveAcl(tbox._props.TemplateDataDocument ?? tbox.Document[DocData]);
-        switch (permissions) {
+        switch (GetEffectiveAcl(tbox.dataDoc)) {
             case AclAugment:
                 {
                     // previously used hack: (state.selection as any).$cursor.nodeBefore;
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 31cd1603f..7e0375275 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -67,7 +67,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     // Since this node is being rendered with a template, this method retrieves
     // the actual slide being rendered from the auto-generated rendering template
     @computed get slideDoc() {
-        return DocCast(this.Document.rootDocument, this.Document);
+        return this.rootDoc;
     }
 
     // this is the document in the workspaces that is targeted by the slide
-- 
cgit v1.2.3-70-g09d2


From 1f294ef4a171eec72a069a9503629eaf7975d983 Mon Sep 17 00:00:00 2001
From: geireann <geireann.lindfield@gmail.com>
Date: Thu, 17 Apr 2025 13:52:58 -0400
Subject: fix for resizing docs with native width  and modify native dim set
 (fixes dailyjournal resizing).

---
 src/client/documents/DocUtils.ts                   | 32 ++--------------------
 src/client/util/CurrentUserUtils.ts                |  2 +-
 src/client/views/DocumentDecorations.tsx           |  4 +--
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 .../views/nodes/chatbot/vectorstore/Vectorstore.ts |  3 +-
 5 files changed, 9 insertions(+), 34 deletions(-)

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

diff --git a/src/client/documents/DocUtils.ts b/src/client/documents/DocUtils.ts
index 7845c80aa..7b36c3a66 100644
--- a/src/client/documents/DocUtils.ts
+++ b/src/client/documents/DocUtils.ts
@@ -711,8 +711,9 @@ export namespace DocUtils {
                       nativeWidth: 40,
                       nativeHeight: 40,
                   })
-                : defaultTextTemplate?.type === DocumentType.JOURNAL
-                  ? Docs.Create.DailyJournalDocument('', {
+                : (defaultTextTemplate?.type === DocumentType.JOURNAL ? Docs.Create.DailyJournalDocument:Docs.Create.TextDocument)(
+                    '', 
+                    {
                         annotationOn,
                         backgroundColor,
                         x,
@@ -723,33 +724,6 @@ export namespace DocUtils {
                             : {
                                   _width: width || BoolCast(Doc.UserDoc().fitBox) ? Number(StrCast(Doc.UserDoc().fontSize).replace('px', '')) * 1.5 * 6 : 200,
                                   _height: BoolCast(Doc.UserDoc().fitBox) ? Number(StrCast(Doc.UserDoc().fontSize).replace('px', '')) * 1.5 : 35,
-                                  _layout_fitWidth: true,
-                                  _layout_autoHeight: true,
-                                  backgroundColor: StrCast(Doc.UserDoc().textBackgroundColor),
-                                  borderColor: Doc.UserDoc().borderColor as string,
-                                  borderWidth: Doc.UserDoc().borderWidth as number,
-                                  text_centered: BoolCast(Doc.UserDoc().textCentered),
-                                  text_fitBox: BoolCast(Doc.UserDoc().fitBox),
-                                  text_align: StrCast(Doc.UserDoc().textAlign),
-                                  text_fontColor: StrCast(Doc.UserDoc().fontColor),
-                                  text_fontFamily: StrCast(Doc.UserDoc().fontFamily),
-                                  text_fontWeight: StrCast(Doc.UserDoc().fontWeight),
-                                  text_fontStyle: StrCast(Doc.UserDoc().fontStyle),
-                                  text_fontDecoration: StrCast(Doc.UserDoc().fontDecoration),
-                              }),
-                    })
-                  : Docs.Create.TextDocument('', {
-                        annotationOn,
-                        backgroundColor,
-                        x,
-                        y,
-                        title,
-                        ...(defaultTextTemplate
-                            ? {} // if the new doc will inherit from a template, don't set any layout fields since that would block the inheritance
-                            : {
-                                  _width: width || BoolCast(Doc.UserDoc().fitBox) ? Number(StrCast(Doc.UserDoc().fontSize).replace('px', '')) * 1.5 * 6 : 200,
-                                  _height: BoolCast(Doc.UserDoc().fitBox) ? Number(StrCast(Doc.UserDoc().fontSize).replace('px', '')) * 1.5 : 35,
-                                  _layout_fitWidth: true,
                                   _layout_autoHeight: true,
                                   backgroundColor: StrCast(Doc.UserDoc().textBackgroundColor),
                                   borderColor: Doc.UserDoc().borderColor as string,
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 1887c1716..99a67ebb2 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -406,7 +406,7 @@ pie title Minerals in my tap water
             {key: "Script",     creator: opts => Docs.Create.ScriptingDocument(null, opts), opts: { _width: 200, _height: 250, }},
             {key: "DataViz",    creator: opts => Docs.Create.DataVizDocument("", opts),     opts: { _width: 300, _height: 300, }},
             // AARAV ADD //
-            {key: "DailyJournal",creator:opts => Docs.Create.DailyJournalDocument("", opts),opts: { _width: 300, _height: 300}},
+            {key: "DailyJournal",creator:opts => Docs.Create.DailyJournalDocument("", opts),opts: { _width: 300, _height: 300, }},
             {key: "Chat",       creator: Docs.Create.ChatDocument,                          opts: { _width: 500, _height: 500, _layout_fitWidth: true, }},
             {key: "MetaNote",   creator: metaNoteTemplate,                                  opts: { _width: 300, _height: 120, _header_pointerEvents: "all", _header_height: 50, _header_fontSize: 9,_layout_autoHeightMargins: 50, _layout_autoHeight: true, treeView_HideUnrendered: true}},
             {key: "ViewSlide",  creator: slideView,                                         opts: { _width: 400, _height: 300, _xMargin: 3, _yMargin: 3,}},
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 0756d3d9e..120467e8a 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -551,7 +551,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
                 scale.x === 1 ? (scale.x = scale.y) : (scale.y = scale.x);
             }
 
-            if (['right', 'left'].includes(opts.dragHdl) && modifyNativeDim && Doc.NativeWidth(doc)) {
+            if (['right', 'left', 'bottomRight'].includes(opts.dragHdl) && modifyNativeDim && Doc.NativeWidth(doc)) {
                 const setData = Doc.NativeWidth(doc[DocData]) === doc.nativeWidth;
                 doc.nativeWidth = scale.x * Doc.NativeWidth(doc);
                 if (setData) Doc.SetNativeWidth(doc[DocData], NumCast(doc.nativeWidth));
@@ -559,7 +559,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
                     doc._nativeHeight = (initHeight / initWidth) * nwidth; // initializes the nativeHeight for a PDF
                 }
             }
-            if (['bottom', 'top'].includes(opts.dragHdl) && modifyNativeDim && Doc.NativeHeight(doc)) {
+            if (['bottom', 'top', 'bottomRight'].includes(opts.dragHdl) && modifyNativeDim && Doc.NativeHeight(doc)) {
                 const setData = Doc.NativeHeight(doc[DocData]) === doc.nativeHeight && (!doc.layout_reflowVertical || opts.ctrlKey);
                 doc._nativeHeight = scale.y * Doc.NativeHeight(doc);
                 if (setData) Doc.SetNativeHeight(doc[DocData], NumCast(doc._nativeHeight));
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 521934152..284014e54 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1208,7 +1208,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
     }
     @computed private get nativeScaling() {
         if (this.shouldNotScale) return 1;
-        const minTextScale = this.Document.type === DocumentType.RTF ? 0.1 : 0;
+        const minTextScale = [DocumentType.RTF, DocumentType.JOURNAL].includes(this.Document.type as DocumentType) ? 0.1 : 0;
         const ai = this._showAIEditor && this.nativeWidth === this.layoutDoc.width ? 95 : 0;
         const effNW = Math.max(this.effectiveNativeWidth - ai, 1);
         const effNH = Math.max(this.effectiveNativeHeight - ai, 1);
diff --git a/src/client/views/nodes/chatbot/vectorstore/Vectorstore.ts b/src/client/views/nodes/chatbot/vectorstore/Vectorstore.ts
index afd34f28d..c1915a398 100644
--- a/src/client/views/nodes/chatbot/vectorstore/Vectorstore.ts
+++ b/src/client/views/nodes/chatbot/vectorstore/Vectorstore.ts
@@ -42,7 +42,8 @@ export class Vectorstore {
     constructor(id: string, doc_ids: () => string[]) {
         const pineconeApiKey = process.env.PINECONE_API_KEY;
         if (!pineconeApiKey) {
-            throw new Error('PINECONE_API_KEY is not defined.');
+            console.log('PINECONE_API_KEY is not defined - Vectorstore will be unavailable');
+            return;
         }
 
         // Initialize Pinecone and OpenAI clients with API keys from the environment.
-- 
cgit v1.2.3-70-g09d2

