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/views/collections/CollectionTreeView.tsx | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

(limited to 'src/client/views/collections/CollectionTreeView.tsx')

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}
-- 
cgit v1.2.3-70-g09d2


From 288a0095bf7e54b0c1765ba994f5f203fd8507cf Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sun, 23 Mar 2025 20:51:21 -0400
Subject: fixed more typing errors.   fixed updateBullets to not generate error
 when part of selection is not on a list.

---
 .../views/collections/CollectionNoteTakingView.tsx |  4 +-
 .../views/collections/CollectionStackingView.tsx   |  4 +-
 .../views/collections/CollectionTreeView.tsx       |  2 +-
 src/client/views/collections/TreeView.tsx          |  4 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 12 ++---
 src/client/views/nodes/FieldView.tsx               |  2 +-
 .../formattedText/ProsemirrorExampleTransfer.ts    | 58 ++++++++++++----------
 src/client/views/nodes/formattedText/nodes_rts.ts  |  5 +-
 8 files changed, 48 insertions(+), 43 deletions(-)

(limited to 'src/client/views/collections/CollectionTreeView.tsx')

diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index 2dabd3269..01695dbaf 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -271,7 +271,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                 containerViewPath={this.childContainerViewPath}
                 fitWidth={this._props.childLayoutFitWidth}
                 isContentActive={emptyFunction}
-                onKey={this.onKeyDown}
+                onKey={this.onKey}
                 // TODO: change this from a prop to a parameter passed into a function
                 dontHideOnDrag
                 isDocumentActive={this.isContentActive}
@@ -438,7 +438,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     };
 
     @undoBatch
-    onKeyDown = (e: React.KeyboardEvent, textBox: FormattedTextBox) => {
+    onKey = (e: KeyboardEvent, textBox: FormattedTextBox) => {
         if ((e.ctrlKey || textBox.Document._createDocOnCR) && ['Enter'].includes(e.key)) {
             e.stopPropagation?.();
             const newDoc = Doc.MakeCopy(textBox.Document, true);
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 883b0bbe3..fd48a9dc1 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -310,7 +310,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         return this._props.styleProvider?.(doc, props, property);
     };
     @undoBatch
-    onKeyDown = (e: React.KeyboardEvent, textBox: FormattedTextBox) => {
+    onKey = (e: KeyboardEvent, textBox: FormattedTextBox) => {
         if (['Enter'].includes(e.key) && e.ctrlKey) {
             e.stopPropagation?.();
             const layoutFieldKey = StrCast(textBox.fieldKey);
@@ -359,7 +359,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                 containerViewPath={this.childContainerViewPath}
                 fitWidth={this.childFitWidth}
                 isContentActive={doc.onClick ? this.isChildButtonContentActive : this.isChildContentActive}
-                onKey={this.onKeyDown}
+                onKey={this.onKey}
                 DataTransition={trans}
                 isDocumentActive={this.isContentActive}
                 LayoutTemplate={this._props.childLayoutTemplate}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index b7f49ac20..1960e12bd 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 /* , textBox: FormattedTextBox */) => {
+    onKey = (e: KeyboardEvent /* , textBox: FormattedTextBox */) => {
         if (this.outlineMode && e.key === 'Enter') {
             e.stopPropagation();
             this.makeTextCollection(this.treeChildren);
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 5ac283d22..cb7da8c84 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -939,7 +939,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
         if (property.startsWith(StyleProp.Decorations)) return null;
         return this._props?.treeView?._props.styleProvider?.(doc, props, property); // properties are inherited from the CollectionTreeView, not the hierarchical parent in the treeView
     };
-    onKeyDown = (e: React.KeyboardEvent) => {
+    onKey = (e: KeyboardEvent) => {
         if (this.Document.treeView_HideHeader || (this.Document.treeView_HideHeaderIfTemplate && this.treeView._props.childLayoutTemplate?.()) || this.treeView.outlineMode) {
             switch (e.key) {
                 case 'Tab':
@@ -1138,7 +1138,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
                 ScreenToLocalTransform={this.docTransform}
                 renderDepth={this._props.renderDepth + 1}
                 onClickScript={this.onChildClick}
-                onKey={this.onKeyDown}
+                onKey={this.onKey}
                 containerViewPath={this.treeView.childContainerViewPath}
                 childFilters={returnEmptyFilter}
                 childFiltersByRanges={returnEmptyFilter}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 25cec9c0d..7fd5d71fd 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1498,7 +1498,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         return this.addDocument?.(newDoc);
     }, 'copied text note');
 
-    onKeyDown = (e: React.KeyboardEvent, textBox: FormattedTextBox) => {
+    onKey = (e: KeyboardEvent, textBox: FormattedTextBox) => {
         if ((e.metaKey || e.ctrlKey || e.altKey || textBox.Document._createDocOnCR) && ['Tab', 'Enter'].includes(e.key)) {
             e.stopPropagation?.();
             return this.createTextDocCopy(textBox, !e.altKey && e.key !== 'Tab');
@@ -1541,7 +1541,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 rootSelected={childData ? this.rootSelected : returnFalse}
                 waitForDoubleClickToClick={this._props.waitForDoubleClickToClick}
                 onClickScript={this.onChildClickHandler}
-                onKey={this.onKeyDown}
+                onKey={this.onKey}
                 onDoubleClickScript={this.onChildDoubleClickHandler}
                 bringToFront={this.bringToFront}
                 ScreenToLocalTransform={childLayout.z ? this.ScreenToLocalBoxXf : this.ScreenToContentsXf}
@@ -1839,15 +1839,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         Object.values(this._disposers).forEach(disposer => disposer?.());
     }
 
-    updateIcon = (usePanelDimensions?: boolean) => {
+    updateIcon = (/*usePanelDimensions?: boolean*/) => {
         const contentDiv = this._mainCont;
         return !contentDiv
             ? new Promise<void>(res => res())
             : UpdateIcon(
                   this.layoutDoc[Id] + '_icon_' + new Date().getTime(),
                   contentDiv,
-                  usePanelDimensions || true ? this._props.PanelWidth() : NumCast(this.layoutDoc._width),
-                  usePanelDimensions || true ? this._props.PanelHeight() : NumCast(this.layoutDoc._height),
+                  this._props.PanelWidth(), // usePanelDimensions ? this._props.PanelWidth() : NumCast(this.layoutDoc._width),
+                  this._props.PanelHeight(), // usePanelDimensions ? this._props.PanelHeight() : NumCast(this.layoutDoc._height),
                   this._props.PanelWidth(),
                   this._props.PanelHeight(),
                   0,
@@ -2314,7 +2314,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                             NativeWidth={returnZero}
                             NativeHeight={returnZero}
                             onClickScript={this.onChildClickHandler}
-                            onKey={this.onKeyDown}
+                            onKey={this.onKey}
                             onDoubleClickScript={this.onChildDoubleClickHandler}
                             childFilters={this.childDocFilters}
                             childFiltersByRanges={this.childDocRangeFilters}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index abba99c3d..f82e980f5 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -84,7 +84,7 @@ export interface FieldViewSharedProps {
     onDoubleClickScript?: () => ScriptField;
     onPointerDownScript?: () => ScriptField;
     onPointerUpScript?: () => ScriptField;
-    onKey?: (e: React.KeyboardEvent, textBox: FormattedTextBox) => boolean | undefined;
+    onKey?: (e: KeyboardEvent, textBox: FormattedTextBox) => boolean | undefined;
     fitWidth?: (doc: Doc) => boolean | undefined;
     dontCenter?: 'x' | 'y' | 'xy' | undefined;
     searchFilterDocs: () => Doc[];
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index a75efd77b..eabc6455f 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -1,12 +1,12 @@
 import { chainCommands, deleteSelection, exitCode, joinBackward, joinDown, joinUp, lift, newlineInCode, selectNodeBackward, setBlockType, splitBlockKeepMarks, toggleMark, wrapIn } from 'prosemirror-commands';
 import { redo, undo } from 'prosemirror-history';
-import { MarkType, Node, ResolvedPos, Schema } from 'prosemirror-model';
+import { MarkType, Node, Schema } from 'prosemirror-model';
 import { liftListItem, sinkListItem, splitListItem, wrapInList } from 'prosemirror-schema-list';
 import { Command, EditorState, NodeSelection, TextSelection, Transaction } from 'prosemirror-state';
 import { liftTarget } from 'prosemirror-transform';
 import { EditorView } from 'prosemirror-view';
 import { ClientUtils } from '../../../../ClientUtils';
-import { Utils } from '../../../../Utils';
+import { numberRange, Utils } from '../../../../Utils';
 import { AclAdmin, AclAugment, AclEdit, DocData } from '../../../../fields/DocSymbols';
 import { GetEffectiveAcl } from '../../../../fields/util';
 import { Docs } from '../../../documents/Documents';
@@ -19,12 +19,12 @@ const mac = typeof navigator !== 'undefined' ? /Mac/.test(navigator.platform) :
 
 export type KeyMap = { [key: string]: Command };
 
-export const updateBullets = (tx2: Transaction, schema: Schema, assignedMapStyle?: string, from?: number, to?: number) => {
+export function updateBullets(tx2: Transaction, schema: Schema, assignedMapStyle?: string, from?: number, to?: number) {
     let mapStyle = assignedMapStyle;
     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 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);
+            const resolved = tx2.doc.resolve(offset);
+            let depth = [0, ...numberRange(resolved.depth)].reduce((p, c, idx) => p + (resolved.node(idx).type === schema.nodes.ordered_list ? 1 : 0), 0);
             if (node.type === schema.nodes.ordered_list) {
                 if (depth === 0 && !assignedMapStyle) mapStyle = node.attrs.mapStyle;
                 depth++;
@@ -33,7 +33,7 @@ export const updateBullets = (tx2: Transaction, schema: Schema, assignedMapStyle
         }
     });
     return tx2;
-};
+}
 
 export function buildKeymap<S extends Schema<string>>(schema: S, tbox?: FormattedTextBox): KeyMap {
     const keys: { [key: string]: Command } = {};
@@ -43,8 +43,9 @@ export function buildKeymap<S extends Schema<string>>(schema: S, tbox?: Formatte
     }
 
     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
-        return tbox?._props.onKey?.(event, tbox);
+        // bcz: hack -- 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 event && tbox?._props.onKey?.(event as unknown as KeyboardEvent, tbox);
     }
 
     const canEdit = (state: EditorState) => {
@@ -53,8 +54,9 @@ export function buildKeymap<S extends Schema<string>>(schema: S, tbox?: Formatte
         switch (permissions) {
             case AclAugment:
                 {
-                    const prevNode = (state.selection as any).$cursor.nodeBefore;
-                    const prevUser = !prevNode ? ClientUtils.CurrentUserEmail() : prevNode.marks.lastElement()?.attrs.userid;
+                    // previously used hack: (state.selection as any).$cursor.nodeBefore;
+                    const prevNode = state.selection?.$anchor.nodeBefore;
+                    const prevUser = !prevNode ? ClientUtils.CurrentUserEmail() : Array.from(prevNode.marks).lastElement()?.attrs.userid;
                     if (prevUser !== ClientUtils.CurrentUserEmail()) {
                         return false;
                     }
@@ -208,12 +210,14 @@ export function buildKeymap<S extends Schema<string>>(schema: S, tbox?: Formatte
         return true;
     });
     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] as number, schema.nodes.paragraph, { ...resolved.parent.attrs, align: 'right' }, resolved.parent.marks);
+        const {
+            tr,
+            selection: { $from },
+        } = state;
+        if ($from?.parent.type.name === 'paragraph') {
+            tr.setNodeMarkup(state.selection.from - state.selection.$from.parentOffset - 1, schema.nodes.paragraph, { ...$from.parent.attrs, align: 'right' }, $from.parent.marks);
         } else {
-            const node = resolved.nodeAfter;
+            const node = $from.nodeAfter;
             const sm = state.storedMarks || undefined;
             if (node) {
                 tr.replaceRangeWith(state.selection.from, state.selection.from, schema.nodes.paragraph.create({ align: 'right' })).setStoredMarks([...node.marks, ...(sm || [])]);
@@ -223,12 +227,14 @@ export function buildKeymap<S extends Schema<string>>(schema: S, tbox?: Formatte
         return true;
     });
     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] as number, schema.nodes.paragraph, { ...resolved.parent.attrs, align: 'center' }, resolved.parent.marks);
+        const {
+            tr,
+            selection: { $from },
+        } = state;
+        if ($from?.parent.type.name === 'paragraph') {
+            tr.setNodeMarkup(state.selection.from - state.selection.$from.parentOffset - 1, schema.nodes.paragraph, { ...$from.parent.attrs, align: 'center' }, $from.parent.marks);
         } else {
-            const node = resolved.nodeAfter;
+            const node = $from.nodeAfter;
             const sm = state.storedMarks || undefined;
             if (node) {
                 tr.replaceRangeWith(state.selection.from, state.selection.from, schema.nodes.paragraph.create({ align: 'center' })).setStoredMarks([...node.marks, ...(sm || [])]);
@@ -238,12 +244,14 @@ export function buildKeymap<S extends Schema<string>>(schema: S, tbox?: Formatte
         return true;
     });
     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] as number, schema.nodes.paragraph, { ...resolved.parent.attrs, align: 'left' }, resolved.parent.marks);
+        const {
+            tr,
+            selection: { $from },
+        } = state;
+        if ($from?.parent.type.name === 'paragraph') {
+            tr.setNodeMarkup(state.selection.from - state.selection.$from.parentOffset - 1, schema.nodes.paragraph, { ...$from.parent.attrs, align: 'left' }, $from.parent.marks);
         } else {
-            const node = resolved.nodeAfter;
+            const node = $from.nodeAfter;
             const sm = state.storedMarks || undefined;
             if (node) {
                 tr.replaceRangeWith(state.selection.from, state.selection.from, schema.nodes.paragraph.create({ align: 'left' })).setStoredMarks([...node.marks, ...(sm || [])]);
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index 02ded3103..fe7b77e74 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -385,10 +385,6 @@ export const nodes: { [index: string]: NodeSpec } = {
                     };
                 },
             },
-            {
-                style: 'list-style-type=disc',
-                getAttrs: () => ({ mapStyle: 'bullet' }),
-            },
             {
                 tag: 'ol',
                 getAttrs: dom => {
@@ -443,6 +439,7 @@ export const nodes: { [index: string]: NodeSpec } = {
             mapStyle: { default: 'decimal' }, // "decimal", "multi", "bullet"
             visibility: { default: true },
         },
+        marks: '_',
         content: '(paragraph|audiotag)+ | ((paragraph|audiotag)+ ordered_list)',
         parseDOM: [
             {
-- 
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/collections/CollectionTreeView.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 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/collections/CollectionTreeView.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 eef391dba5ee9f1588274f8719eb4047fe844e22 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 10 Apr 2025 23:08:13 -0400
Subject: changed naming of makeDelegates to not add a number at the end.
 switched dataNote to MetaNote and cleaned up initialization of it.  fixed
 sidebar/stacking view to not grab wheel evebnts when not active.  fixed
 dashfieldview to vcenter labels.  fixed height and scrolling of templae menu.
 simplified formattedtextbox menu

---
 src/client/documents/DocUtils.ts                   |  2 +-
 src/client/documents/Documents.ts                  |  5 +-
 src/client/util/CurrentUserUtils.ts                | 83 +++++++++++-----------
 src/client/views/DocumentButtonBar.tsx             |  2 +-
 src/client/views/SidebarAnnos.tsx                  |  3 +-
 src/client/views/TemplateMenu.scss                 |  4 ++
 .../views/collections/CollectionStackingView.tsx   |  2 +-
 .../views/collections/CollectionTreeView.tsx       |  2 +-
 src/client/views/nodes/DocumentView.tsx            | 70 +++++++++---------
 .../views/nodes/formattedText/DashFieldView.scss   |  1 +
 .../views/nodes/formattedText/FormattedTextBox.tsx | 61 ----------------
 .../views/nodes/formattedText/RichTextRules.ts     |  2 +-
 src/fields/Doc.ts                                  |  6 +-
 13 files changed, 96 insertions(+), 147 deletions(-)

(limited to 'src/client/views/collections/CollectionTreeView.tsx')

diff --git a/src/client/documents/DocUtils.ts b/src/client/documents/DocUtils.ts
index f5eee84aa..0d15a91e9 100644
--- a/src/client/documents/DocUtils.ts
+++ b/src/client/documents/DocUtils.ts
@@ -899,7 +899,7 @@ export namespace DocUtils {
         const ndoc = Doc.MakeDelegateWithProto(dragFactory);
         if (ndoc && dragFactory.dragFactory_count !== undefined) {
             dragFactory.dragFactory_count = NumCast(dragFactory.dragFactory_count) + 1;
-            Doc.GetProto(ndoc).title = ndoc.title + ' ' + NumCast(dragFactory.dragFactory_count).toString();
+            Doc.GetProto(ndoc).title = ndoc.title;
         }
         return ndoc;
     }
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 0e3c614fe..d93a432d4 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -221,7 +221,6 @@ export class DocumentOptions {
     color?: STRt = new StrInfo('foreground color data doc', false);
     hidden?: BOOLt = new BoolInfo('whether the document is not rendered by its collection', false);
     backgroundColor?: STRt = new StrInfo('background color for data doc', false);
-    _override_backgroundColor?: BOOLt = new BoolInfo("whether the layout template overrides the data doc's background color");
     opacity?: NUMt = new NumInfo('document opacity', false);
     viewTransitionTime?: NUMt = new NumInfo('transition duration for view parameters', false);
     dontRegisterView?: BOOLt = new BoolInfo('are views of this document registered so that they can be found when following links, etc', false);
@@ -284,7 +283,7 @@ export class DocumentOptions {
     _layout_curPage?: NUMt = new NumInfo('current page of a PDF or other? paginated document', false);
     _layout_currentTimecode?: NUMt = new NumInfo('the current timecode of a time-based document (e.g., current time of a video)  value is in seconds', false);
     _layout_fitWidth?: BOOLt = new BoolInfo('whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)');
-    _layout_fieldKey?: STRt = new StrInfo('the field key containing the current layout definition', false);
+    layout_fieldKey?: STRt = new StrInfo('the field key containing the current layout definition', false);
     _layout_enableAltContentUI?: BOOLt = new BoolInfo('whether to show alternate content button');
     _layout_flashcardType?: STRt = new StrInfo('flashcard style to render in ComparisonBox.  currently just "flashcard".');
     _layout_showTitle?: string; // field name to display in header (:hover is an optional suffix)
@@ -678,7 +677,7 @@ export namespace Docs {
             // whatever options pertain to this specific prototype
             const options: DocumentOptions = {
                 isSystem: true,
-                _layout_fieldKey: 'layout',
+                layout_fieldKey: 'layout',
                 title,
                 type,
                 isBaseProto: true,
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 4c492cae0..1c0f11c91 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -144,14 +144,16 @@ export class CurrentUserUtils {
     static setupNoteTemplates(doc: Doc, field="template_notes") {
         const tempNotes = DocCast(doc[field]);
         const reqdTempOpts:DocumentOptions[] = [
-            { title: "Postit", _override_backgroundColor: true, backgroundColor: "yellow",    icon: "sticky-note", _layout_showTitle: "title", layout_borderRounding: "5px"},
-            { title: "Idea",   _override_backgroundColor: true, backgroundColor: "pink",      icon: "lightbulb"  , _layout_showTitle: "title"},
-            { title: "Topic",  _override_backgroundColor: true, backgroundColor: "lightblue", icon: "book-open"  , _layout_showTitle: "title"}];
+            { title: "Postit", backgroundColor: "yellow",    icon: "sticky-note", _layout_showTitle: "title", layout_borderRounding: "5px"},
+            { title: "Idea",   backgroundColor: "pink",      icon: "lightbulb"  , _layout_showTitle: "title"},
+            { title: "Topic",  backgroundColor: "lightblue", icon: "book-open"  , _layout_showTitle: "title"}];
+
+        
         const reqdNoteList = [...reqdTempOpts.map(opts => {
             const reqdOpts = {...opts, isSystem:true, width:200, layout_autoHeight: true, layout_fitWidth: true};
             const noteTemp = tempNotes ? DocListCast(tempNotes.data).find(fdoc => fdoc.title === opts.title): undefined;
             return DocUtils.AssignOpts(noteTemp, reqdOpts) ?? MakeTemplate(Docs.Create.TextDocument("",reqdOpts));
-        }), ... DocListCast(tempNotes?.data).filter(note => !reqdTempOpts.find(reqd => reqd.title === note.title))];
+        }), DocCast(doc.emptyMetaNote), ... DocListCast(tempNotes?.data).filter(note => !reqdTempOpts.find(reqd => reqd.title === note.title))];
 
         const reqdOpts:DocumentOptions = { title: "Note Layouts", _height: 75, isSystem: true };
         return DocUtils.AssignOpts(tempNotes, reqdOpts, reqdNoteList) ?? (doc[field] = Docs.Create.TreeDocument(reqdNoteList, reqdOpts));
@@ -247,7 +249,6 @@ export class CurrentUserUtils {
         title: string, toolTip: string, icon: string,  ignoreClick?: boolean, dragFactory?: Doc, 
         backgroundColor?: string, openFactoryAsDelegate?:boolean, openFactoryLocation?:string, clickFactory?: Doc, scripts?: { onClick?: string, onDragStart?: string}, funcs?: {onDragStart?:string, hidden?: string},
     }[] {
-        const standardOps = (key:string) => ({ title : "Untitled "+ key, _layout_fitWidth: false, isSystem: true, "dragFactory_count": 0, cloneFieldFilter: new List<string>(["isSystem"]) });
         const json = {
             doc: {
                 type: "doc",
@@ -272,17 +273,17 @@ export class CurrentUserUtils {
             // "    <FormattedTextBox {...props} fieldKey={'header'} dontSelectOnLoad={'true'} ignoreAutoHeight={'true'} pointerEvents='{this._header_pointerEvents||`none`}' fontSize='{this._header_fontSize}px' height='{this._header_height}px' background='{this._header_color}' />" +
             // "    <FormattedTextBox {...props} fieldKey={'text'} position='absolute' top='{this._header_height}px' height='calc(100% - {this._header_height}px)'/>" +
             // "</div>";
-        const headerBtnHgt = 10;
-        const headerTemplate = (opts:DocumentOptions) => 
-            MakeTemplate(Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), { ...opts, title: "Header Template", 
+        const metadataBtnHght = 10;
+        const metaNoteTemplate = (opts:DocumentOptions) => 
+            MakeTemplate(Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), { ...opts, title: "MetaNote", 
                 layout:`<HTMLdiv transformOrigin='top left' width='100%' height='100%'>
-                            <FormattedTextBox {...props} dontScale='true' fieldKey={'text'} height='calc(100% - ${headerBtnHgt}px - {this._header_height||0}px)'  />
+                            <FormattedTextBox {...props} dontScale='true' fieldKey={'text'} height='calc(100% - ${metadataBtnHght}px - {this._header_height||0}px)'  />
                             <FormattedTextBox {...props} dontScale='true' fieldKey={'header'} dontSelectOnLoad='true' ignoreAutoHeight='true' fontSize='{this._header_fontSize||9}px' height='{(this._header_height||0)}px' backgroundColor='{this._header_color || "lightGray"}' />
-                            <HTMLdiv fontSize='${headerBtnHgt - 1}px' height='${headerBtnHgt}px' backgroundColor='yellow'
-                                   onClick={‘(this._header_height=(this._header_height===0?50:0)) + (this._layout_autoHeightMargins=this._header_height ? this._header_height+${headerBtnHgt}:0)’} > Metadata
+                            <HTMLdiv fontSize='${metadataBtnHght - 1}px' height='${metadataBtnHght}px' backgroundColor='yellow'
+                                   onClick={‘(this._header_height=(this._header_height===0?50:0)) + (this._layout_autoHeightMargins=this._header_height ? this._header_height+${metadataBtnHght}:0)’} > Metadata
                                    </HTMLdiv>
                         </HTMLdiv>`
-                    }, "header"));
+                    }, "metaNote"));
         const slideView = (opts:DocumentOptions) => 
             MakeTemplate(Docs.Create.MultirowDocument(
                     [
@@ -405,9 +406,9 @@ 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: "Header",     creator: headerTemplate,                                    opts: { _width: 300, _height: 120, _header_pointerEvents: "all", _header_height: 50, _header_fontSize: 9,_layout_autoHeightMargins: 50, _layout_autoHeight: true, treeView_HideUnrendered: 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,}},
             {key: "Trail",      creator: Docs.Create.PresDocument,                          opts: { _width: 400, _height: 30, _type_collection: CollectionViewType.Stacking, _layout_dontCenter:'xy', dropAction: dropActionType.embed, treeView_HideTitle: true, _layout_fitWidth:true, layout_boxShadow: "0 0" }},
             {key: "Tab",        creator: opts => Docs.Create.FreeformDocument([], opts),    opts: { _width: 500, _height: 800, _layout_fitWidth: true, _freeform_backgroundGrid: true, }},
@@ -420,40 +421,42 @@ pie title Minerals in my tap water
             {key: "Plotly",     creator: plotlyView,                                        opts: { _width: 300, _height: 300, }},
                                                                                         ];
 
+        const standardOps = (key:string) => ({ title : "Untitled "+ key, _layout_fitWidth: false, isSystem: true, "dragFactory_count": 0, cloneFieldFilter: new List<string>(["isSystem"]) });
         emptyThings.forEach(
             thing => DocUtils.AssignDocField(doc, "empty"+thing.key, (opts) => thing.creator(opts), {...standardOps(thing.key), ...thing.opts}, undefined, thing.scripts, thing.funcs));
-        
+        DocCast(Doc.UserDoc().emptyMetaNote).title = "MetaNote"; // hack: metanotes are used a template, so 'untitled metaNote' is an awkward name 
+
         return [
-            { toolTip: "Tap or drag to create a note",            title: "Note", icon: "sticky-note",   dragFactory: doc.emptyNote as Doc,       clickFactory: DocCast(doc.emptyNote)},
-            { toolTip: "Tap or drag to create a flashcard",       title: "Flashcard", icon: "id-card",  dragFactory: doc.emptyFlashcard as Doc,  clickFactory: DocCast(doc.emptyFlashcard)},
-            { toolTip: "Tap or drag to create an equation",       title: "Math", icon: "calculator",    dragFactory: doc.emptyEquation as Doc,   clickFactory: DocCast(doc.emptyEquation)},
-            { toolTip: "Tap or drag to create a mermaid node",    title: "Mermaids", icon: "rocket",    dragFactory: doc.emptyMermaids as Doc,   clickFactory: DocCast(doc.emptyMermaids)}, 
-            { toolTip: "Tap or drag to create a plotly node",     title: "Plotly", icon: "rocket",      dragFactory: doc.emptyPlotly as Doc,     clickFactory: DocCast(doc.emptyMermaids)}, 
-            { toolTip: "Tap or drag to create a note board",      title: "Notes", icon: "book",         dragFactory: doc.emptyNoteboard as Doc,  clickFactory: DocCast(doc.emptyNoteboard)},
-            { toolTip: "Tap or drag to create an image",          title: "Image", icon: "image",        dragFactory: doc.emptyImage as Doc,      clickFactory: DocCast(doc.emptyImage)},
-            { toolTip: "Tap or drag to create a collection",      title: "Col", icon: "folder",         dragFactory: doc.emptyCollection as Doc, clickFactory: DocCast(doc.emptyTab)},
-            { toolTip: "Tap or drag to create a webpage",         title: "Web", icon: "globe-asia",     dragFactory: doc.emptyWebpage as Doc,    clickFactory: DocCast(doc.emptyWebpage)},
-            { toolTip: "Tap or drag to create a comparison box",  title: "Compare", icon: "columns",    dragFactory: doc.emptyComparison as Doc, clickFactory: DocCast(doc.emptyComparison)},
-            { toolTip: "Tap or drag to create a diagram",         title: "Diagram", icon: "tree",       dragFactory: doc.emptyDiagram as Doc,    clickFactory: DocCast(doc.emptyDiagram)},
-            { toolTip: "Tap or drag to create an audio recorder", title: "Audio", icon: "microphone",   dragFactory: doc.emptyAudio as Doc,      clickFactory: DocCast(doc.emptyAudio),      openFactoryLocation: OpenWhere.overlay},
-            { toolTip: "Tap or drag to create a map",             title: "Map", icon: "map-marker-alt", dragFactory: doc.emptyMap as Doc,        clickFactory: DocCast(doc.emptyMap)},
-            { toolTip: "Tap or drag to create a chat assistant",  title: "Assistant Chat", icon: "book",dragFactory: doc.emptyChat as Doc,       clickFactory: DocCast(doc.emptyChat)},
-            { toolTip: "Tap or drag to create a screen grabber",  title: "Grab", icon: "photo-video",   dragFactory: doc.emptyScreengrab as Doc, clickFactory: DocCast(doc.emptyScreengrab), openFactoryLocation: OpenWhere.overlay, funcs: { hidden: "IsNoviceMode()"}},
-            { toolTip: "Tap or drag to create a WebCam recorder", title: "WebCam", icon: "photo-video", dragFactory: doc.emptyWebCam as Doc,     clickFactory: DocCast(doc.emptyWebCam),     openFactoryLocation: OpenWhere.overlay, funcs: { hidden: "IsNoviceMode()"}},
-            { toolTip: "Tap or drag to create a button",          title: "Button", icon: "circle",      dragFactory: doc.emptyButton as Doc,     clickFactory: DocCast(doc.emptyButton)},
-            { toolTip: "Tap or drag to create a scripting box",   title: "Script", icon: "terminal",    dragFactory: doc.emptyScript as Doc,     clickFactory: DocCast(doc.emptyScript),                                             funcs: { hidden: "IsNoviceMode()"}},
-            { toolTip: "Tap or drag to create a data viz node",   title: "DataViz", icon: "chart-bar",  dragFactory: doc.emptyDataViz as Doc,    clickFactory: DocCast(doc.emptyDataViz)},
-            { toolTip: "Tap or drag to create a journal entry",   title: "Journal", icon: "book",       dragFactory: doc.emptyDailyJournal as Doc,clickFactory:DocCast(doc.emptyDataJournal), },
-            { toolTip: "Tap or drag to create a bullet slide",    title: "PPT Slide", icon: "person-chalkboard",   dragFactory: doc.emptySlide as Doc,       clickFactory: DocCast(doc.emptySlide),     openFactoryLocation: OpenWhere.overlay,funcs: { hidden: "IsNoviceMode()"}},
-            { toolTip: "Tap or drag to create a view slide",      title: "View Slide", icon: "address-card",       dragFactory: doc.emptyViewSlide as Doc,   clickFactory: DocCast(doc.emptyViewSlide), openFactoryLocation: OpenWhere.overlay,funcs: { hidden: "IsNoviceMode()"}},
-            { toolTip: "Tap or drag to create a data note",       title: "DataNote", icon: "window-maximize",      dragFactory: doc.emptyHeader as Doc,      clickFactory: DocCast(doc.emptyHeader),    openFactoryAsDelegate: true,           funcs: { hidden: "IsNoviceMode()"} },
-            { toolTip: "Toggle a Calculator REPL",                title: "replviewer",  icon: "calculator",                                      clickFactory: '<ScriptingRepl />' as unknown as Doc, openFactoryLocation: OpenWhere.overlay}, // hack: clickFactory is not a Doc but will get interpreted as a custom UI by the openDoc() onClick script
+            { toolTip: "Tap or drag to create a note",            title: "Note",        icon: "sticky-note",     dragFactory: doc.emptyNote as Doc,       clickFactory: DocCast(doc.emptyNote)},
+            { toolTip: "Tap or drag to create a flashcard",       title: "Flashcard",   icon: "id-card",         dragFactory: doc.emptyFlashcard as Doc,  clickFactory: DocCast(doc.emptyFlashcard)},
+            { toolTip: "Tap or drag to create an equation",       title: "Math",        icon: "calculator",      dragFactory: doc.emptyEquation as Doc,   clickFactory: DocCast(doc.emptyEquation)},
+            { toolTip: "Tap or drag to create a mermaid node",    title: "Mermaids",    icon: "rocket",          dragFactory: doc.emptyMermaids as Doc,   clickFactory: DocCast(doc.emptyMermaids)}, 
+            { toolTip: "Tap or drag to create a plotly node",     title: "Plotly",      icon: "rocket",          dragFactory: doc.emptyPlotly as Doc,     clickFactory: DocCast(doc.emptyMermaids)}, 
+            { toolTip: "Tap or drag to create a note board",      title: "Notes",       icon: "book",            dragFactory: doc.emptyNoteboard as Doc,  clickFactory: DocCast(doc.emptyNoteboard)},
+            { toolTip: "Tap or drag to create an image",          title: "Image",       icon: "image",           dragFactory: doc.emptyImage as Doc,      clickFactory: DocCast(doc.emptyImage)},
+            { toolTip: "Tap or drag to create a collection",      title: "Col",         icon: "folder",          dragFactory: doc.emptyCollection as Doc, clickFactory: DocCast(doc.emptyTab)},
+            { toolTip: "Tap or drag to create a webpage",         title: "Web",         icon: "globe-asia",      dragFactory: doc.emptyWebpage as Doc,    clickFactory: DocCast(doc.emptyWebpage)},
+            { toolTip: "Tap or drag to create a comparison box",  title: "Compare",     icon: "columns",         dragFactory: doc.emptyComparison as Doc, clickFactory: DocCast(doc.emptyComparison)},
+            { toolTip: "Tap or drag to create a diagram",         title: "Diagram",     icon: "tree",            dragFactory: doc.emptyDiagram as Doc,    clickFactory: DocCast(doc.emptyDiagram)},
+            { toolTip: "Tap or drag to create an audio recorder", title: "Audio",       icon: "microphone",      dragFactory: doc.emptyAudio as Doc,      clickFactory: DocCast(doc.emptyAudio),      openFactoryLocation: OpenWhere.overlay},
+            { toolTip: "Tap or drag to create a map",             title: "Map",         icon: "map-marker-alt",  dragFactory: doc.emptyMap as Doc,        clickFactory: DocCast(doc.emptyMap)},
+            { toolTip: "Tap or drag to create a chat assistant",  title: "Chat Assist", icon: "book",            dragFactory: doc.emptyChat as Doc,       clickFactory: DocCast(doc.emptyChat)},
+            { toolTip: "Tap or drag to create a screen grabber",  title: "Grab",        icon: "photo-video",     dragFactory: doc.emptyScreengrab as Doc, clickFactory: DocCast(doc.emptyScreengrab), openFactoryLocation: OpenWhere.overlay, funcs: { hidden: "IsNoviceMode()"}},
+            { toolTip: "Tap or drag to create a WebCam recorder", title: "WebCam",      icon: "photo-video",     dragFactory: doc.emptyWebCam as Doc,     clickFactory: DocCast(doc.emptyWebCam),     openFactoryLocation: OpenWhere.overlay, funcs: { hidden: "IsNoviceMode()"}},
+            { toolTip: "Tap or drag to create a button",          title: "Button",      icon: "circle",          dragFactory: doc.emptyButton as Doc,     clickFactory: DocCast(doc.emptyButton)},
+            { toolTip: "Tap or drag to create a scripting box",   title: "Script",      icon: "terminal",        dragFactory: doc.emptyScript as Doc,     clickFactory: DocCast(doc.emptyScript),                                             funcs: { hidden: "IsNoviceMode()"}},
+            { toolTip: "Tap or drag to create a data viz node",   title: "DataViz",     icon: "chart-bar",       dragFactory: doc.emptyDataViz as Doc,    clickFactory: DocCast(doc.emptyDataViz)},
+            { toolTip: "Tap or drag to create a journal entry",   title: "Journal",     icon: "book",            dragFactory:doc.emptyDailyJournal as Doc,clickFactory: DocCast(doc.emptyDataJournal), },
+            { toolTip: "Tap or drag to create a bullet slide",    title: "PPT Slide",   icon:"person-chalkboard",dragFactory: doc.emptySlide as Doc,      clickFactory: DocCast(doc.emptySlide),     openFactoryLocation: OpenWhere.overlay, funcs: { hidden: "IsNoviceMode()"}},
+            { toolTip: "Tap or drag to create a view slide",      title: "View Slide",  icon: "address-card",    dragFactory: doc.emptyViewSlide as Doc,  clickFactory: DocCast(doc.emptyViewSlide), openFactoryLocation: OpenWhere.overlay, funcs: { hidden: "IsNoviceMode()"}},
+            { toolTip: "Tap or drag to create a data note",       title: "MetaNote",    icon: "window-maximize", dragFactory: doc.emptyMetaNote as Doc,   clickFactory: DocCast(doc.emptyMetaNote),  openFactoryAsDelegate: true,            funcs: { hidden: "IsNoviceMode()"} },
+            { toolTip: "Toggle a Calculator REPL",                title: "replviewer",  icon: "calculator",                                               clickFactory: '<ScriptingRepl />' as unknown as Doc, openFactoryLocation: OpenWhere.overlay}, // hack: clickFactory is not a Doc but will get interpreted as a custom UI by the openDoc() onClick script
           ].map(tuple => (
               { openFactoryLocation: OpenWhere.addRight, 
                 scripts: { onClick:     'openDoc(copyDragFactory(this.clickFactory,this.openFactoryAsDelegate), this.openFactoryLocation)', 
                            onDragStart: '{ return copyDragFactory(this.dragFactory,this.openFactoryAsDelegate); }'}, 
                 funcs: tuple.funcs,
-                ...tuple, }))
+                ...tuple, }));
     }
 
     /// Initalizes the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 9a57a5e6c..6f2f051cd 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -512,7 +512,7 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
                 <div className="documentButtonBar-button">{this.pinButton}</div>
                 <div className="documentButtonBar-button">{this.recordButton}</div>
                 <div className="documentButtonBar-button">{this.calendarButton}</div>
-                <div className="documentButtonBar-button">{this.aiEditorButton}</div>
+                {this.view0?.HasAIEditor ? <div className="documentButtonBar-button">{this.aiEditorButton}</div> : null}
                 <div className="documentButtonBar-button">{this.keywordButton}</div>
                 {!Doc.UserDoc().documentLinksButton_fullMenu ? null : <div className="documentButtonBar-button">{this.shareButton}</div>}
                 <div className="documentButtonBar-button">{this.menuButton}</div>
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index b7e6318b1..573c28ccf 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -1,7 +1,7 @@
 import { computed, makeObservable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { ClientUtils, returnAll, returnFalse, returnOne, returnZero } from '../../ClientUtils';
+import { ClientUtils, returnFalse, returnOne, returnZero } from '../../ClientUtils';
 import { emptyFunction } from '../../Utils';
 import { Doc, DocListCast, Field, FieldResult, FieldType, StrListCast } from '../../fields/Doc';
 import { Id } from '../../fields/FieldSymbols';
@@ -253,7 +253,6 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
                         renderDepth={this._props.renderDepth + 1}
                         type_collection={CollectionViewType.Stacking}
                         fieldKey={this.sidebarKey}
-                        pointerEvents={returnAll}
                     />
                 </div>
             </div>
diff --git a/src/client/views/TemplateMenu.scss b/src/client/views/TemplateMenu.scss
index 8879fc20d..049945517 100644
--- a/src/client/views/TemplateMenu.scss
+++ b/src/client/views/TemplateMenu.scss
@@ -40,12 +40,16 @@
     height: 100%;
     width: 100%;
     max-height: 250px;
+    overflow: auto;
 
     .templateToggle,
     .chromeToggle {
         text-align: left;
         color: black;
     }
+    .collectionTreeView {
+        height: fit-content !important;
+    }
 
     input {
         margin-right: 10px;
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 4f60acb18..112510265 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -720,7 +720,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         return this._props.isContentActive() === false ? 'none' : undefined;
     }
 
-    onPassiveWheel = (e: WheelEvent) => e.stopPropagation();
+    onPassiveWheel = (e: WheelEvent) => this._props.isContentActive() && e.stopPropagation();
     render() {
         TraceMobx();
         const editableViewProps = {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 962fbdcd7..3497a62cb 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -467,7 +467,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
 
         const scale = this._props.NativeDimScaling?.() || 1;
         return (
-            <div style={{ transform: `scale(${scale})`, transformOrigin: 'top left', width: `${100 / scale}%`, height: `${100 / scale}%` }}>
+            <div className="collectionTreeView" 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
                         {...this._props}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index bec5ea5c1..cbd92c159 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -523,7 +523,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
                 appearanceItems.splice(0, 0, { description: 'Open in Lightbox', event: () => DocumentView.SetLightboxDoc(this.Document), icon: 'external-link-alt' });
             }
             appearanceItems.push({ description: 'Pin', event: () => this._props.pinToPres(this.Document, {}), icon: 'map-pin' });
-            appearanceItems.push({ description: 'AI view', event: () => this._docView?.toggleAIEditor(), icon: 'map-pin' });
+            this._componentView?.componentAIView?.() && appearanceItems.push({ description: 'AI view', event: () => this._docView?.toggleAIEditor(), icon: 'map-pin' });
 
             !Doc.noviceMode && templateDoc && appearanceItems.push({ description: 'Open Template   ', event: () => this._props.addDocTab(templateDoc, OpenWhere.addRight), icon: 'eye' });
             !appearance && appearanceItems.length && cm.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'compass' });
@@ -660,7 +660,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
         this._props
             .ScreenToLocalTransform()
             .translate(-NumCast(this.Document.borderWidth), -this.headerMargin - NumCast(this.Document.borderWidth))
-            .scale(this._props.showAIEditor ? (this._props.PanelHeight() || 1) / this.aiContentsHeight() : 1);
+            .scale(this.viewingAiEditor() ? (this._props.PanelHeight() || 1) / this.aiContentsHeight() : 1);
     onClickFunc = () => (this.disableClickScriptFunc ? undefined : this.onClickHdlr);
     setHeight = (height: number) => { !this._props.suppressSetHeight && (this.layoutDoc._height = Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), height + 2 * NumCast(this.Document.borderWidth))); } // prettier-ignore
     setContentView = action((view: ViewBoxInterface<FieldViewProps>) => { this._componentView = view; }); // prettier-ignore
@@ -712,32 +712,34 @@ 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 aiEditor() {
+        return (
+            <>
+                <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
@@ -760,6 +762,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
         );
     }
     widgetOverlayFunc = () => (this.widgetDecorations ? this.widgetOverlay : null);
+    viewingAiEditor = () => (this._props.showAIEditor && this._componentView?.componentAIView?.() !== undefined ? this.aiEditor : null);
     @computed get viewBoxContents() {
         TraceMobx();
         const isInk = this.layoutDoc._layout_isSvg && !this._props.LayoutTemplateString;
@@ -770,8 +773,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
                     className="documentView-contentsView"
                     style={{
                         pointerEvents: (isInk || noBackground ? 'none' : this.contentPointerEvents()) ?? (this._mounted ? 'all' : 'none'),
-                        width: this._props.showAIEditor ? this.aiContentsWidth() : undefined,
-                        height: this._props.showAIEditor ? this.aiContentsHeight() : this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined,
+                        width: this.viewingAiEditor() ? this.aiContentsWidth() : undefined,
+                        height: this.viewingAiEditor() ? this.aiContentsHeight() : this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined,
                     }}>
                     <DocumentContentsView
                         {...this._props}
@@ -779,8 +782,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
                         pointerEvents={this.contentPointerEvents}
                         setContentViewBox={this.setContentView}
                         childFilters={this.childFilters}
-                        PanelWidth={this._props.showAIEditor ? this.aiContentsWidth : this._props.PanelWidth}
-                        PanelHeight={this._props.showAIEditor ? this.aiContentsHeight : this.panelHeight}
+                        PanelWidth={this.viewingAiEditor() ? this.aiContentsWidth : this._props.PanelWidth}
+                        PanelHeight={this.viewingAiEditor() ? this.aiContentsHeight : this.panelHeight}
                         setHeight={this.setHeight}
                         isContentActive={this.isContentActive}
                         ScreenToLocalTransform={this.screenToLocalContent}
@@ -790,7 +793,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
                         hideClickBehaviors={BoolCast(this.Document.hideClickBehaviors)}
                     />
                 </div>
-                {this._props.showAIEditor ? this.aiEditor() : this.tagsOverlayFunc()}
+                {this.viewingAiEditor() ?? this.tagsOverlayFunc()}
                 {this.widgetOverlayFunc()}
             </>
         );
@@ -1256,6 +1259,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
     public get allLinks()       { return this._docViewInternal?._allLinks ?? []; } // prettier-ignore
     public get TagBtnHeight()   { return this._docViewInternal?.TagsBtnHeight; } // prettier-ignore
     public get UIBtnScaling()   { return this._docViewInternal?.uiBtnScaling; } // prettier-ignore
+    public get HasAIEditor()    { return !!this._docViewInternal?._componentView?.componentAIView?.(); } // prettier-ignore
 
     get LayoutFieldKey() {
         return Doc.LayoutFieldKey(this.Document, this._props.LayoutTemplateString);
diff --git a/src/client/views/nodes/formattedText/DashFieldView.scss b/src/client/views/nodes/formattedText/DashFieldView.scss
index 6db8abc7f..3734ad9cc 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.scss
+++ b/src/client/views/nodes/formattedText/DashFieldView.scss
@@ -26,6 +26,7 @@
         display: inline-block;
         font-weight: normal;
         background: rgba(0, 0, 0, 0.1);
+        align-content: center;
         cursor: default;
     }
     .dashFieldView-fieldSpan {
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index bef999e6d..fbe4fd294 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1,5 +1,4 @@
 /* eslint-disable no-use-before-define */
-import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@mui/material';
 import { Property } from 'csstype';
@@ -21,7 +20,6 @@ import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocCss, ForceServerWrite, U
 import { Id, ToString } from '../../../../fields/FieldSymbols';
 import { InkTool } from '../../../../fields/InkField';
 import { List } from '../../../../fields/List';
-import { PrefetchProxy } from '../../../../fields/Proxy';
 import { RichTextField } from '../../../../fields/RichTextField';
 import { ComputedField } from '../../../../fields/ScriptField';
 import { BoolCast, Cast, DateCast, DocCast, FieldValue, NumCast, RTFCast, ScriptCast, StrCast } from '../../../../fields/Types';
@@ -35,7 +33,6 @@ import { DocUtils } from '../../../documents/DocUtils';
 import { DictationManager } from '../../../util/DictationManager';
 import { DragManager } from '../../../util/DragManager';
 import { dropActionType } from '../../../util/DropActionTypes';
-import { MakeTemplate } from '../../../util/DropConverter';
 import { LinkManager } from '../../../util/LinkManager';
 import { RTFMarkup } from '../../../util/RTFMarkup';
 import { SnappingManager } from '../../../util/SnappingManager';
@@ -855,42 +852,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
             return;
         }
 
-        const changeItems: ContextMenuProps[] = [];
-        changeItems.push({
-            description: 'plain',
-            event: undoable(() => {
-                Doc.setNativeView(this.Document);
-                this.layoutDoc.layout_autoHeightMargins = undefined;
-            }, 'set plain view'),
-            icon: 'eye',
-        });
-        changeItems.push({
-            description: 'metadata',
-            event: undoable(() => {
-                this.dataDoc.layout_meta = Cast(Doc.UserDoc().emptyHeader, Doc, null)?.layout;
-                this.Document.layout_fieldKey = 'layout_meta';
-                setTimeout(() => {
-                    this.layoutDoc._header_height = this.layoutDoc._layout_autoHeightMargins = 50;
-                }, 50);
-            }, 'set metadata view'),
-            icon: 'eye',
-        });
-        const noteTypesDoc = Cast(Doc.UserDoc().template_notes, Doc, null);
-        DocListCast(noteTypesDoc?.data).forEach(note => {
-            const icon: IconProp = StrCast(note.icon) as IconProp;
-            changeItems.push({
-                description: StrCast(note.title),
-                event: undoable(
-                    () => {
-                        this.layoutDoc.layout_autoHeightMargins = undefined;
-                        Doc.setNativeView(this.Document);
-                        DocUtils.makeCustomViewClicked(this.Document, Docs.Create.TreeDocument, StrCast(note.title), note);
-                    },
-                    `set ${StrCast(note.title)} view}`
-                ),
-                icon: icon,
-            });
-        });
         const highlighting: ContextMenuProps[] = [];
         const noviceHighlighting = ['Audio Tags', 'My Text', 'Text from Others', 'Bold Text'];
         const expertHighlighting = [...noviceHighlighting, 'Important Items', 'Ignore Items', 'Disagree Items', 'By Recent Minute', 'By Recent Hour'];
@@ -952,20 +913,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                 icon: 'expand-arrows-alt',
             });
 
-        appearanceItems.push({ description: 'Change Style...', noexpand: true, subitems: changeItems, icon: 'external-link-alt' });
-
-        !Doc.noviceMode &&
-            appearanceItems.push({
-                description: 'Make Default Layout',
-                event: () => {
-                    if (!this.layoutDoc.isTemplateDoc) {
-                        MakeTemplate(this.Document);
-                    }
-                    Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.Document);
-                    Doc.AddDocToList(Cast(Doc.UserDoc().template_notes, Doc, null), 'data', this.Document);
-                },
-                icon: 'eye',
-            });
         !appearance && appearanceItems.length && cm.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'eye' });
 
         const options = cm.findByDescription('Options...');
@@ -988,14 +935,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                 },
                 icon: !this.Document._createDocOnCR ? 'grip-lines' : 'bars',
             });
-        !Doc.noviceMode &&
-            optionItems.push({
-                description: `${this.Document._layout_autoHeight ? 'Lock' : 'Auto'} Height`,
-                event: () => {
-                    this.layoutDoc._layout_autoHeight = !this.layoutDoc._layout_autoHeight;
-                },
-                icon: this.Document._layout_autoHeight ? 'lock' : 'unlock',
-            });
         optionItems.push({
             description: this.Document.savedAsSticker ? 'Sticker Saved!' : 'Save to Stickers',
             event: action(undoable(async () => await StickerPalette.addToPalette(this.Document), 'save to palette')),
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 77c00537b..04315e886 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -113,7 +113,7 @@ export class RichTextRules {
                 const inlineFieldKey = '$inline' + numInlines; // which field on the text document this annotation will write to
                 const inlineLayoutKey = '$layout_' + inlineFieldKey; // the field holding the layout string that will render the inline annotation
                 const textDocInline = Docs.Create.TextDocument('', {
-                    _layout_fieldKey: inlineLayoutKey,
+                    layout_fieldKey: inlineLayoutKey,
                     _width: 75,
                     _height: 35,
                     annotationOn: this.Document[DocData],
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index e48337651..2db8d3b85 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -239,7 +239,7 @@ export class Doc extends RefField {
     public static get MyPublishedDocs()       { return DocListCast(Doc.ActiveDashboard?.myPublishedDocs).concat(DocListCast(DocCast(Doc.UserDoc().myPublishedDocs)?.data)); } // prettier-ignore
     public static get MyDashboards()          { return DocCast(Doc.UserDoc().myDashboards); } // prettier-ignore
     public static get MyTemplates()           { return DocCast(Doc.UserDoc().myTemplates); } // prettier-ignore
-    public static get MyStickers()               { return DocCast(Doc.UserDoc().myStickers); } // prettier-ignore
+    public static get MyStickers()            { return DocCast(Doc.UserDoc().myStickers); } // prettier-ignore
     public static get MyLightboxDrawings()    { return DocCast(Doc.UserDoc().myLightboxDrawings); } // prettier-ignore
     public static get MyImports()             { return DocCast(Doc.UserDoc().myImports); } // prettier-ignore
     public static get MyFilesystem()          { return DocCast(Doc.UserDoc().myFilesystem); } // prettier-ignore
@@ -1062,14 +1062,14 @@ export namespace Doc {
         return ndoc;
     }
 
-    let _applyCount: number = 0;
     export function ApplyTemplate(templateDoc: Doc) {
         if (templateDoc) {
             const proto = new Doc();
+            const applyCount = NumCast(templateDoc.dragFactory_count);
             proto.author = ClientUtils.CurrentUserEmail();
             const target = Doc.MakeDelegate(proto);
             const targetKey = StrCast(templateDoc.layout_fieldKey, 'layout');
-            const applied = ApplyTemplateTo(templateDoc, target, targetKey, templateDoc.title + '(...' + _applyCount++ + ')');
+            const applied = ApplyTemplateTo(templateDoc, target, targetKey, templateDoc.title + '(...' + applyCount + ')');
             target.layout_fieldKey = targetKey; //this and line above
             applied && (Doc.GetProto(applied).type = templateDoc.type);
             return applied;
-- 
cgit v1.2.3-70-g09d2

