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/views/collections/CollectionStackingView.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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

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


From d818ef151ca65008e5c6bb5e92b709decb3026d8 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 14 Apr 2025 18:35:49 -0400
Subject: fixed how templates are expanded to avoid template sub-component
 conflicts by changing how field keys are named.   fixed various Cast
 functions to be more typesafe by including undefined as part of return type. 
 overhaul of Doc.MakeClone, MakeCopy, FindRefernces - makeClone is no longer
 async.  fixed inlined docs in text docs.

---
 src/client/documents/DocUtils.ts                   |  41 +-
 src/client/documents/DocumentTypes.ts              |   2 +-
 src/client/documents/Documents.ts                  |   8 +-
 src/client/util/CurrentUserUtils.ts                |  37 +-
 src/client/util/DictationManager.ts                |   2 +-
 src/client/util/DocumentManager.ts                 |   2 +-
 src/client/util/DragManager.ts                     |  40 +-
 src/client/util/DropConverter.ts                   |   6 +-
 src/client/util/Import & Export/ImageUtils.ts      |   2 +-
 src/client/util/LinkFollower.ts                    |  32 +-
 src/client/util/LinkManager.ts                     |  43 +-
 src/client/util/SearchUtil.ts                      |   6 +-
 src/client/util/SettingsManager.tsx                |   2 +-
 src/client/views/DocComponent.tsx                  |   8 +-
 src/client/views/DocumentButtonBar.tsx             |   8 +-
 src/client/views/FilterPanel.tsx                   |   4 +-
 src/client/views/GlobalKeyHandler.ts               |   6 +-
 src/client/views/InkStrokeProperties.ts            |   2 +-
 src/client/views/InkTranscription.tsx              |   6 +-
 src/client/views/LightboxView.tsx                  |   4 +-
 src/client/views/Main.tsx                          |   4 +-
 src/client/views/MainView.tsx                      |  42 +-
 src/client/views/PinFuncs.ts                       |   8 +-
 src/client/views/PropertiesButtons.tsx             |   2 +-
 src/client/views/PropertiesView.tsx                |   8 +-
 src/client/views/StyleProvider.tsx                 |  12 +-
 src/client/views/StyleProviderQuiz.tsx             |   2 +-
 src/client/views/TagsView.tsx                      |   6 +-
 src/client/views/animationtimeline/Timeline.tsx    |   2 +-
 .../views/collections/CollectionCarouselView.tsx   |   3 +-
 .../views/collections/CollectionDockingView.tsx    |  16 +-
 .../views/collections/CollectionNoteTakingView.tsx |  11 +-
 .../views/collections/CollectionStackingView.tsx   |  12 +-
 .../CollectionStackingViewFieldColumn.tsx          |   4 +-
 src/client/views/collections/CollectionSubView.tsx |  37 +-
 src/client/views/collections/TabDocView.tsx        |  14 +-
 src/client/views/collections/TreeView.tsx          |  20 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  31 +-
 .../collectionFreeForm/FaceCollectionBox.tsx       |   2 +-
 .../collectionFreeForm/ImageLabelBox.tsx           |   4 +-
 .../collections/collectionFreeForm/MarqueeView.tsx |   6 +-
 .../collectionLinear/CollectionLinearView.tsx      |   4 +-
 .../collectionSchema/CollectionSchemaView.tsx      |   4 +-
 .../collectionSchema/SchemaTableCell.tsx           |   2 +-
 src/client/views/global/globalScripts.ts           |  37 +-
 src/client/views/newlightbox/NewLightboxView.tsx   |   2 +-
 src/client/views/nodes/AudioBox.tsx                |   2 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |   4 +-
 src/client/views/nodes/DataVizBox/DataVizBox.tsx   |   2 +-
 src/client/views/nodes/DocumentContentsView.tsx    |   4 +-
 src/client/views/nodes/DocumentView.tsx            | 100 ++--
 src/client/views/nodes/FieldView.tsx               |   2 +-
 src/client/views/nodes/IconTagBox.tsx              |   2 +-
 src/client/views/nodes/ImageBox.tsx                |  24 +-
 src/client/views/nodes/KeyValueBox.tsx             |   8 +-
 src/client/views/nodes/KeyValuePair.tsx            |   4 +-
 src/client/views/nodes/PDFBox.tsx                  |   2 +-
 .../views/nodes/RecordingBox/RecordingBox.tsx      |   2 +-
 src/client/views/nodes/ScreenshotBox.tsx           |   2 +-
 src/client/views/nodes/ScriptingBox.tsx            |  13 +-
 src/client/views/nodes/WebBox.tsx                  |  18 +-
 .../nodes/chatbot/chatboxcomponents/ChatBox.tsx    |   2 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  55 +-
 .../views/nodes/formattedText/RichTextMenu.tsx     |   6 +-
 .../views/nodes/formattedText/RichTextRules.ts     |  19 +-
 src/client/views/nodes/trails/PresBox.tsx          | 187 +++---
 src/client/views/nodes/trails/PresElementBox.scss  | 308 ----------
 src/client/views/nodes/trails/PresElementBox.tsx   | 628 ---------------------
 src/client/views/nodes/trails/PresSlideBox.scss    | 308 ++++++++++
 src/client/views/nodes/trails/PresSlideBox.tsx     | 628 +++++++++++++++++++++
 src/client/views/nodes/trails/index.ts             |   2 +-
 src/client/views/pdf/GPTPopup/GPTPopup.tsx         |   2 +-
 src/client/views/search/FaceRecognitionHandler.tsx |   4 +-
 src/client/views/search/SearchBox.tsx              |   2 +-
 src/client/views/smartdraw/StickerPalette.tsx      |  13 +-
 src/debug/Repl.tsx                                 |   6 +-
 src/fields/Doc.ts                                  | 486 ++++++++--------
 src/fields/List.ts                                 |   6 +-
 src/fields/Proxy.ts                                |  64 +--
 src/fields/Schema.ts                               |  21 +-
 src/fields/ScriptField.ts                          |  12 +-
 src/fields/Types.ts                                |  91 +--
 src/fields/util.ts                                 |  10 +-
 83 files changed, 1799 insertions(+), 1806 deletions(-)
 delete mode 100644 src/client/views/nodes/trails/PresElementBox.scss
 delete mode 100644 src/client/views/nodes/trails/PresElementBox.tsx
 create mode 100644 src/client/views/nodes/trails/PresSlideBox.scss
 create mode 100644 src/client/views/nodes/trails/PresSlideBox.tsx

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

diff --git a/src/client/documents/DocUtils.ts b/src/client/documents/DocUtils.ts
index 5a8230847..7845c80aa 100644
--- a/src/client/documents/DocUtils.ts
+++ b/src/client/documents/DocUtils.ts
@@ -55,13 +55,13 @@ export namespace DocUtils {
             const matchLink = (val: string, anchor: Doc) => {
                 const linkedToExp = (val ?? '').split('=');
                 if (linkedToExp.length === 1) return Field.toScriptString(anchor) === val;
-                return Field.toScriptString(DocCast(anchor[linkedToExp[0]])) === linkedToExp[1];
+                return DocCast(anchor[linkedToExp[0]]) && Field.toScriptString(DocCast(anchor[linkedToExp[0]])!) === linkedToExp[1];
             };
             // prettier-ignore
             return  (value === Doc.FilterNone && !allLinks.length) || 
                     (value === Doc.FilterAny && !!allLinks.length) ||
-                    (allLinks.some(link => matchLink(value as string, DocCast(link.link_anchor_1)) || 
-                                           matchLink(value as string, DocCast(link.link_anchor_2)) ));
+                    (allLinks.some(link => (DocCast(link.link_anchor_1) && matchLink(value as string, DocCast(link.link_anchor_1)!)) || 
+                                           (DocCast(link.link_anchor_2) && matchLink(value as string, DocCast(link.link_anchor_2)!)) ));
         }
         if (typeof value === 'string') {
             value = value.replace(`,${ClientUtils.noRecursionHack}`, '');
@@ -266,7 +266,7 @@ export namespace DocUtils {
             Object.keys(funcs)
                 .filter(key => !key.endsWith('-setter'))
                 .forEach(key => {
-                    const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
+                    const cfield = ComputedField.DisableCompute(() => FieldValue(doc[key]));
                     const func = funcs[key];
                     if (ScriptCast(cfield)?.script.originalScript !== func) {
                         const setFunc = Cast(funcs[key + '-setter'], 'string', null);
@@ -375,12 +375,13 @@ export namespace DocUtils {
 
         const documentList: ContextMenuProps[] = foo
             .filter(btnDoc => !btnDoc.hidden)
-            .map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null))
+            .map(btnDoc => DocCast(btnDoc?.dragFactory))
             .filter(doc => doc && doc !== Doc.UserDoc().emptyTrail && doc.title)
+            .map(doc => doc!)
             .map(dragDoc => ({
                 description: ':' + StrCast(dragDoc.title).replace('Untitled ', ''),
                 event: undoable(() => {
-                    const newDoc = (Doc.isTemplateDoc(dragDoc) ? DocUtils.delegateDragFactory : DocUtils.copyDragFactory)(dragDoc);
+                    const newDoc = (dragDoc.isTemplateDoc ? DocUtils.delegateDragFactory : DocUtils.copyDragFactory)(dragDoc);
                     if (newDoc) {
                         newDoc._author = ClientUtils.CurrentUserEmail();
                         newDoc.x = x;
@@ -437,6 +438,7 @@ export namespace DocUtils {
             .filter(btnDoc => !btnDoc.hidden)
             .map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null))
             .filter(doc => doc && doc !== Doc.UserDoc().emptyTrail && doc !== Doc.UserDoc().emptyNote && doc.title)
+            .map(doc => doc!)
             .map(dragDoc => ({
                 description: ':' + StrCast(dragDoc.title).replace('Untitled ', ''),
                 event: undoable(() => {
@@ -494,11 +496,11 @@ export namespace DocUtils {
             .map(btnDoc => (btnDoc.dragFactory as Doc) || btnDoc)
             .filter(d => d.isTemplateDoc);
         // bcz: this is hacky -- want to have different templates be applied depending on the "type" of a document.  but type is not reliable and there could be other types of template searches so this should be generalized
-        // first try to find a template that matches the specific document type (<typeName>_<templateName>).  otherwise, fallback to a general match on <templateName>
+        // first try to find a template that matches the specific document type (<typeName><TemplateName>).  otherwise, fallback to a general match on <templateName>
         !docLayoutTemplate &&
             allTemplates.forEach(tempDoc => {
                 const templateType = StrCast(doc[templateName + '_fieldKey'] || doc.type);
-                StrCast(tempDoc.title) === templateName + '_' + templateType && (docLayoutTemplate = tempDoc);
+                StrCast(tempDoc.title) === templateName + (templateType[0].toUpperCase() + templateType.slice(1)) && (docLayoutTemplate = tempDoc);
             });
         !docLayoutTemplate &&
             allTemplates.forEach(tempDoc => {
@@ -522,18 +524,13 @@ export namespace DocUtils {
                 Doc.ApplyTemplateTo(docLayoutTemplate, doc, customName, undefined);
             }
         } else {
-            let fieldTemplate: Opt<Doc>;
-            if (doc.data instanceof RichTextField || typeof doc.data === 'string') {
-                fieldTemplate = Docs.Create.TextDocument('', options);
-            } else if (doc.data instanceof PdfField) {
-                fieldTemplate = Docs.Create.PdfDocument('http://www.msn.com', options);
-            } else if (doc.data instanceof VideoField) {
-                fieldTemplate = Docs.Create.VideoDocument('http://www.cs.brown.edu', options);
-            } else if (doc.data instanceof AudioField) {
-                fieldTemplate = Docs.Create.AudioDocument('http://www.cs.brown.edu', options);
-            } else if (doc.data instanceof ImageField) {
-                fieldTemplate = Docs.Create.ImageDocument('http://www.cs.brown.edu', options);
-            }
+            const fieldTemplate = (() => {
+                if (doc.data instanceof RichTextField || typeof doc.data === 'string') return Docs.Create.TextDocument('', options);
+                if (doc.data instanceof PdfField) return Docs.Create.PdfDocument('http://www.msn.com', options);
+                if (doc.data instanceof VideoField) return Docs.Create.VideoDocument('http://www.cs.brown.edu', options);
+                if (doc.data instanceof AudioField) return Docs.Create.AudioDocument('http://www.cs.brown.edu', options);
+                if (doc.data instanceof ImageField) return Docs.Create.ImageDocument('http://www.cs.brown.edu', options);
+            })();
             const docTemplate = creator?.(fieldTemplate ? [fieldTemplate] : [], { title: customName + '(' + doc.title + ')', isTemplateDoc: true, _width: _width + 20, _height: Math.max(100, _height + 45) });
             fieldTemplate && Doc.MakeMetadataFieldTemplate(fieldTemplate, docTemplate ? Doc.GetProto(docTemplate) : docTemplate);
             docTemplate && Doc.ApplyTemplateTo(docTemplate, doc, customName, undefined);
@@ -904,8 +901,8 @@ export namespace DocUtils {
         return ndoc;
     }
 
-    export async function Zip(doc: Doc, zipFilename = 'dashExport.zip') {
-        const { clone, map, linkMap } = await Doc.MakeClone(doc);
+    export function Zip(doc: Doc, zipFilename = 'dashExport.zip') {
+        const { clone, map, linkMap } = Doc.MakeClone(doc);
         const proms = new Set<string>();
         function replacer(key: string, value: { url: string; [key: string]: unknown }) {
             if (key && ['branchOf', 'cloneOf', 'cursors'].includes(key)) return undefined;
diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts
index dd0985182..5c6559836 100644
--- a/src/client/documents/DocumentTypes.ts
+++ b/src/client/documents/DocumentTypes.ts
@@ -36,7 +36,7 @@ export enum DocumentType {
     // special purpose wrappers that either take no data or are compositions of lower level types
     LINK = 'link',
     PRES = 'presentation',
-    PRESELEMENT = 'preselement',
+    PRESSLIDE = 'presslide',
     COMPARISON = 'comparison',
     PUSHPIN = 'pushpin',
     MAPROUTE = 'maproute',
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index d93a432d4..bf9cc5bd4 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -217,7 +217,7 @@ export class DocumentOptions {
     author?: string; // STRt = new StrInfo('creator of document'); // bcz: don't change this. Otherwise, the userDoc's field Infos will have a FieldInfo assigned to its author field which will render it unreadable
     author_date?: DATEt = new DateInfo('date the document was created', true);
     annotationOn?: DOCt = new DocInfo('document annotated by this document', false);
-    rootDocument?: DOCt = new DocInfo('document that supplies the information needed for a rendering template (eg, pres slide for PresElement)');
+    rootDocument?: DOCt = new DocInfo('document that stores the data for compound template documents.');
     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);
@@ -1139,8 +1139,8 @@ export namespace Docs {
             return InstanceFromProto(Prototypes.get(DocumentType.FONTICON), undefined, { ...(options || {}) });
         }
 
-        export function PresElementBoxDocument() {
-            return Prototypes.get(DocumentType.PRESELEMENT);
+        export function PresSlideDocument() {
+            return Prototypes.get(DocumentType.PRESSLIDE);
         }
 
         export function DataVizDocument(url: string, options?: DocumentOptions, overwriteDoc?: Doc) {
@@ -1148,7 +1148,7 @@ export namespace Docs {
         }
 
         export function AnnoPaletteDocument(options?: DocumentOptions) {
-            return InstanceFromProto(Prototypes.get(DocumentType.ANNOPALETTE), new List([Doc.MyStickers]), { ...(options || {}) });
+            return InstanceFromProto(Prototypes.get(DocumentType.ANNOPALETTE), new List([...(Doc.MyStickers ? [Doc.MyStickers] : [])]), { ...(options || {}) });
         }
 
         export function DockDocument(documents: Array<Doc>, config: string, options: DocumentOptions, id?: string) {
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 35dc5f1c7..1887c1716 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -148,12 +148,12 @@ export class CurrentUserUtils {
             { title: "Idea",   backgroundColor: "pink",      icon: "lightbulb"  , _layout_showTitle: "title"},
             { title: "Topic",  backgroundColor: "lightblue", icon: "book-open"  , _layout_showTitle: "title"}];
 
-        
+        const metanote = DocCast(doc.emptyMetaNote);
         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));
-        }), DocCast(doc.emptyMetaNote), ... DocListCast(tempNotes?.data).filter(note => !reqdTempOpts.find(reqd => reqd.title === note.title))];
+        }), ...(metanote ? [metanote]:[]), ... 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));
@@ -211,7 +211,7 @@ export class CurrentUserUtils {
         const fontBox = (opts:DocumentOptions, fieldKey:string) => Docs.Create.FontIconDocument({ layout:FontIconBox.LayoutString(fieldKey), _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, ...opts  });
         
         const makeIconTemplate = (name: DocumentType | string | undefined, templateField: string, opts:DocumentOptions) => {
-            const title = "icon" + (name ? "_" + name : "");
+            const title = "icon" + (name ? name[0].toUpperCase()+name.slice(1) : "");
             const curIcon = DocCast(templateIconsDoc[title]);
             const creator =  (() => { switch (opts.iconTemplate) {
                                         case DocumentType.IMG : return imageBox;
@@ -424,7 +424,10 @@ pie title Minerals in my tap water
         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 
+        const metanote = DocCast(Doc.UserDoc().emptyMetaNote);
+        if (metanote) {
+            metanote.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)},
@@ -490,7 +493,7 @@ pie title Minerals in my tap water
             { title: "Tools",           toolTip: "Tools",           target: this.setupToolsBtnPanel(doc, "myTools"),           icon: "wrench",      },
             { title: "Imports",         toolTip: "Imports ⌘I",      target: this.setupImportSidebar(doc, "myImports"),         icon: "upload",      },
             { title: "Closed",          toolTip: "Recently Closed", target: this.setupRecentlyClosed(doc, "myRecentlyClosed"), icon: "archive",     hidden: true }, // this doc is hidden from the Sidebar, but it's still being used in MyFilesystem which ignores the hidden field
-            { title: "Shared",          toolTip: "Shared Docs",     target: Doc.MySharedDocs,                                  icon: "users",       funcs: {badgeValue: badgeValue}},
+            { title: "Shared",          toolTip: "Shared Docs",     target: Doc.MySharedDocs??Doc.UserDoc(),                   icon: "users",       funcs: {badgeValue: badgeValue}},
             { title: "Trails",          toolTip: "Trails ⌘R",       target: Doc.UserDoc(),                                     icon: "pres-trail",  funcs: {target: getActiveDashTrails}},
             { title: "Image Grouper",   toolTip: "Image Grouper",   target: this.setupImageGrouper(doc, "myImageGrouper"),     icon: "folder-open", hidden: false },
             { title: "Faces",           toolTip: "Unique Faces",    target: this.setupFaceCollection(doc, "myFaceCollection"), icon: "face-smile",  hidden: false },
@@ -551,7 +554,6 @@ pie title Minerals in my tap water
         const creatorBtns = CurrentUserUtils.setupCreatorButtons(doc, allTools?.length ? allTools[0]:undefined);
         const userTools = allTools && allTools?.length > 1 ? allTools[1]:undefined;
         const userBtns = CurrentUserUtils.setupUserDocumentCreatorButtons(doc, userTools);
-        // doc.myUserBtns = new PrefetchProxy(userBtns);
         const reqdToolOps:DocumentOptions = {
             title: "My Tools", isSystem: true, ignoreClick: true, layout_boxShadow: "0 0",
             layout_explainer: "This is a palette of documents that can be created.", _layout_dontCenter: "y",
@@ -975,7 +977,10 @@ pie title Minerals in my tap water
         };
         
         DocUtils.AssignDocField(doc, "mySharedDocs", opts => Docs.Create.TreeDocument([], opts, sharingDocumentId + "layout", sharingDocumentId), sharedDocOpts, undefined, sharedScripts);
-        if (!Doc.GetProto(DocCast(doc.mySharedDocs)).data_dashboards) Doc.GetProto(DocCast(doc.mySharedDocs)).data_dashboards = new List<Doc>();
+        const sharedDocs = DocCast(doc.mySharedDocs);
+        if (sharedDocs) {
+            if (!Doc.GetProto(sharedDocs).data_dashboards) Doc.GetProto(sharedDocs).data_dashboards = new List<Doc>();
+        }
     }
 
     /// Import option on the left side button panel 
@@ -1000,9 +1005,9 @@ pie title Minerals in my tap water
     static updateUserDocument(docIn: Doc, sharingDocumentId: string, linkDatabaseId: string) {
         const doc = docIn;
         DocUtils.AssignDocField(doc, "globalGroupDatabase", () => Docs.Prototypes.MainGroupDocument(), {});
-        reaction(() => DateCast(DocCast(doc.globalGroupDatabase).data_modificationDate),
+        reaction(() => DateCast(DocCast(doc.globalGroupDatabase)?.data_modificationDate),
             async () => {
-                const groups = await DocListCastAsync(DocCast(doc.globalGroupDatabase).data);
+                const groups = await DocListCastAsync(DocCast(doc.globalGroupDatabase)?.data);
                 const mygroups = groups?.filter(group => JSON.parse(StrCast(group.members)).includes(ClientUtils.CurrentUserEmail())) || [];
                 SetCachedGroups(["Guest", ...(mygroups?.map(g => StrCast(g.title))??[])]);
             }, { fireImmediately: true });
@@ -1058,11 +1063,13 @@ pie title Minerals in my tap water
         
         SelectionManager.DeselectAll(); // this forces SelectionManager implementation to copy over to DocumentView's API.  This also triggers the LinkManager to be created
         
-        Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyDashboards);
-        Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MySharedDocs);
-        Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyRecentlyClosed);
+        if (Doc.MyFilesystem) {
+            Doc.MyDashboards && Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyDashboards);
+            Doc.MyDashboards && Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyDashboards);
+            Doc.MyRecentlyClosed && Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyRecentlyClosed);
+        }
 
-        Doc.GetProto(DocCast(Doc.UserDoc().emptyWebpage)).data = new WebField("https://www.wikipedia.org");
+        DocCast(Doc.UserDoc().emptyWebpage) && (Doc.GetProto(DocCast(Doc.UserDoc().emptyWebpage)!).data = new WebField("https://www.wikipedia.org"));
 
         DocServer.CacheNeedsUpdate() && setTimeout(UPDATE_SERVER_CACHE, 2500);
         setInterval(UPDATE_SERVER_CACHE, 120000);
@@ -1142,7 +1149,7 @@ pie title Minerals in my tap water
             const file = input.files?.[0];
             if (file?.type === 'application/zip' || file?.type === 'application/x-zip-compressed') {
                 const doc = await Doc.importDocument(file);
-                const list = Cast(Doc.MyImports.data, listSpec(Doc), null);
+                const list = Cast(Doc.MyImports?.data, listSpec(Doc), null);
                 doc instanceof Doc && list?.splice(0, 0, doc);
             } else if (input.files && input.files.length !== 0) {
                 const disposer = OverlayView.ShowSpinner();
@@ -1150,7 +1157,7 @@ pie title Minerals in my tap water
                 if (results.length !== input.files?.length) {
                     alert("Error uploading files - possibly due to unsupported file types");
                 }
-                const list = Cast(Doc.MyImports.data, listSpec(Doc), null);
+                const list = Cast(Doc.MyImports?.data, listSpec(Doc), null);
                 list?.splice(0, 0, ...results);
                 disposer();
             } else {
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts
index 2eef3da0e..dcef4a4fe 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -364,7 +364,7 @@ export namespace DictationManager {
                                     case 'nested collection':return Docs.Create.FreeformDocument([], {});
                                 } // prettier-ignore
                             })();
-                            created && Doc.AddDocToList(target.dataDoc, Doc.LayoutFieldKey(target.Document), created);
+                            created && Doc.AddDocToList(target.dataDoc, Doc.LayoutDataKey(target.Document), created);
                         }
                     }
                 },
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 5ce005811..ad57c2a62 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -183,7 +183,7 @@ export class DocumentManager {
 
     static _howl: Howl;
     static playAudioAnno(doc: Doc) {
-        const anno = Cast(doc[Doc.LayoutFieldKey(doc) + '_audioAnnotations'], listSpec(AudioField), null)?.lastElement();
+        const anno = Cast(doc[Doc.LayoutDataKey(doc) + '_audioAnnotations'], listSpec(AudioField), null)?.lastElement();
         if (anno) {
             this._howl?.stop();
             if (anno instanceof AudioField) {
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index e2e4c0fe4..a66e6998b 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -234,29 +234,27 @@ export namespace DragManager {
             dropDoc instanceof Doc && CreateLinkToActiveAudio(() => dropDoc);
             return dropDoc;
         };
-        const finishDrag = async (e: DragCompleteEvent) => {
+        const finishDrag = (e: DragCompleteEvent) => {
             const { docDragData } = e;
             setTimeout(() => dragData.dragEnding?.());
             onDropCompleted?.(e); // glr: optional additional function to be called - in this case with presentation trails
             if (docDragData && !docDragData.droppedDocuments.length) {
                 docDragData.dropAction = dragData.userDropAction || dragData.dropAction;
-                docDragData.droppedDocuments = (
-                    await Promise.all(
-                        dragData.draggedDocuments.map(async d =>
-                            !dragData.isDocDecorationMove && !dragData.userDropAction && ScriptCast(d.onDragStart)
-                                ? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result as Doc)
-                                : docDragData.dropAction === dropActionType.embed
-                                  ? Doc.BestEmbedding(d)
-                                  : docDragData.dropAction === dropActionType.add
-                                    ? d
-                                    : docDragData.dropAction === dropActionType.proto
-                                      ? d[DocData]
-                                      : docDragData.dropAction === dropActionType.copy
-                                        ? (await Doc.MakeClone(d)).clone
-                                        : d
-                        )
+                docDragData.droppedDocuments = dragData.draggedDocuments
+                    .map(d =>
+                        !dragData.isDocDecorationMove && !dragData.userDropAction && ScriptCast(d.onDragStart)
+                            ? addAudioTag(ScriptCast(d.onDragStart)!.script.run({ this: d }).result as Doc)
+                            : docDragData.dropAction === dropActionType.embed
+                              ? Doc.BestEmbedding(d)
+                              : docDragData.dropAction === dropActionType.add
+                                ? d
+                                : docDragData.dropAction === dropActionType.proto
+                                  ? d[DocData]
+                                  : docDragData.dropAction === dropActionType.copy
+                                    ? Doc.MakeClone(d).clone
+                                    : d
                     )
-                ).filter(d => d);
+                    .filter(d => d);
                 ![dropActionType.same, dropActionType.proto].includes(StrCast(docDragData.dropAction) as dropActionType) &&
                     docDragData.droppedDocuments
                         // .filter(drop => !drop.dragOnlyWithinContainer || ['embed', 'copy'].includes(docDragData.dropAction as any))
@@ -364,7 +362,7 @@ export namespace DragManager {
         };
     }
 
-    async function dispatchDrag(target: Element, e: PointerEvent, complete: DragCompleteEvent, pos: { x: number; y: number }, finishDrag?: (e: DragCompleteEvent) => void, options?: DragOptions, endDrag?: () => void) {
+    function dispatchDrag(target: Element, e: PointerEvent, complete: DragCompleteEvent, pos: { x: number; y: number }, finishDrag?: (e: DragCompleteEvent) => void, options?: DragOptions, endDrag?: () => void) {
         const dropArgs = {
             cancelable: true, // allows preventDefault() to be called to cancel the drop
             bubbles: true,
@@ -380,7 +378,7 @@ export namespace DragManager {
         };
         target.dispatchEvent(new CustomEvent<DropEvent>('dashPreDrop', dropArgs));
         UndoManager.StartTempBatch(); // run drag/drop in temp batch in case drop is not allowed (so we can undo any intermediate changes)
-        await finishDrag?.(complete);
+        finishDrag?.(complete);
         UndoManager.EndTempBatch(target.dispatchEvent(new CustomEvent<DropEvent>('dashOnDrop', dropArgs))); // event return val is true unless the event preventDefault() is called
         options?.dragComplete?.(complete);
         endDrag?.();
@@ -566,11 +564,11 @@ export namespace DragManager {
                 const targClassName = e.target instanceof HTMLElement && typeof e.target.className === 'string' ? e.target.className : '';
                 if (['lm_tab', 'lm_title_wrap', 'lm_tabs', 'lm_header'].includes(targClassName) && docDragData.draggedDocuments.length === 1) {
                     if (!startWindowDragTimer) {
-                        startWindowDragTimer = setTimeout(async () => {
+                        startWindowDragTimer = setTimeout(() => {
                             startWindowDragTimer = undefined;
                             docDragData.dropAction = docDragData.userDropAction || dropActionType.same;
                             AbortDrag();
-                            await finishDrag?.(new DragCompleteEvent(true, docDragData));
+                            finishDrag?.(new DragCompleteEvent(true, docDragData));
                             DragManager.StartWindowDrag?.(e, docDragData.droppedDocuments, aborted => {
                                 if (!aborted && (docDragData?.dropAction === dropActionType.move || docDragData?.dropAction === dropActionType.same)) {
                                     docDragData.removeDocument?.(docDragData?.draggedDocuments[0]);
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index 7d3f63448..b6b111930 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -3,7 +3,7 @@ import { DocData, DocLayout } from '../../fields/DocSymbols';
 import { ObjectField } from '../../fields/ObjectField';
 import { RichTextField } from '../../fields/RichTextField';
 import { ComputedField, ScriptField } from '../../fields/ScriptField';
-import { StrCast } from '../../fields/Types';
+import { DocCast, StrCast } from '../../fields/Types';
 import { ImageField } from '../../fields/URLField';
 import { Docs, DocumentOptions } from '../documents/Documents';
 import { DocumentType } from '../documents/DocumentTypes';
@@ -23,7 +23,7 @@ import { ScriptingGlobals } from './ScriptingGlobals';
  */
 function makeTemplate(doc: Doc, first: boolean = true): boolean {
     const layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc;
-    if (layoutDoc.layout instanceof Doc) {
+    if (DocCast(layoutDoc.layout)) {
         return true; // its already a template
     }
     const layout = StrCast(layoutDoc.layout).match(/fieldKey={'[^']*'}/)?.[0];
@@ -68,7 +68,7 @@ export function MakeTemplate(doc: Doc) {
  * Makes a draggable button or image that will create a template doc Instance
  */
 export function makeUserTemplateButtonOrImage(doc: Doc, image?: string) {
-    const layoutDoc = doc; // doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc;
+    const layoutDoc = doc;
     if (layoutDoc.type !== DocumentType.FONTICON) {
         !layoutDoc.isTemplateDoc && makeTemplate(layoutDoc);
     }
diff --git a/src/client/util/Import & Export/ImageUtils.ts b/src/client/util/Import & Export/ImageUtils.ts
index 9c32ca25a..eae3460b3 100644
--- a/src/client/util/Import & Export/ImageUtils.ts	
+++ b/src/client/util/Import & Export/ImageUtils.ts	
@@ -15,7 +15,7 @@ export namespace ImageUtils {
     export const AssignImgInfo = (document: Doc, data?: Upload.InspectionResults) => {
         if (data) {
             data.nativeWidth && (document._height = (NumCast(document._width) * data.nativeHeight) / data.nativeWidth);
-            const field = '$' + Doc.LayoutFieldKey(document);
+            const field = '$' + Doc.LayoutDataKey(document);
             document[`${field}_nativeWidth`] = data.nativeWidth;
             document[`${field}_nativeHeight`] = data.nativeHeight;
             document[`${field}_path`] = data.source;
diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
index 0e67dcfaa..6081c3fae 100644
--- a/src/client/util/LinkFollower.ts
+++ b/src/client/util/LinkFollower.ts
@@ -43,13 +43,13 @@ export class LinkFollower {
     };
 
     public static traverseLink(link: Opt<Doc>, sourceDoc: Doc, finished?: () => void, traverseBacklink?: boolean) {
-        const getView = (doc: Doc) => DocumentView.getFirstDocumentView(DocCast(doc.layout_unrendered ? doc.annotationOn : doc));
-        const isAnchor = (source: Doc, anchor: Doc) => Doc.AreProtosEqual(anchor, source) || Doc.AreProtosEqual(anchor.annotationOn as Doc, source);
+        const getView = (doc: Doc) => DocumentView.getFirstDocumentView(DocCast(doc.layout_unrendered ? doc.annotationOn : doc)!);
+        const isAnchor = (source?: Doc, anchor?: Doc) => Doc.AreProtosEqual(anchor, source) || Doc.AreProtosEqual(DocCast(anchor?.annotationOn), source);
         const linkDocs = link ? [link] : Doc.Links(sourceDoc);
-        const fwdLinks = linkDocs.filter(l => isAnchor(sourceDoc, l.link_anchor_1 as Doc)); // link docs where 'sourceDoc' is link_anchor_1
-        const backLinks = linkDocs.filter(l => isAnchor(sourceDoc, l.link_anchor_2 as Doc)); // link docs where 'sourceDoc' is link_anchor_2
-        const fwdLinkWithoutTargetView = fwdLinks.find(l => !getView(DocCast(l.link_anchor_2)));
-        const backLinkWithoutTargetView = backLinks.find(l => !getView(DocCast(l.link_anchor_1)));
+        const fwdLinks = linkDocs.filter(l => isAnchor(sourceDoc, DocCast(l.link_anchor_1))); // link docs where 'sourceDoc' is link_anchor_1
+        const backLinks = linkDocs.filter(l => isAnchor(sourceDoc, DocCast(l.link_anchor_2))); // link docs where 'sourceDoc' is link_anchor_2
+        const fwdLinkWithoutTargetView = fwdLinks.find(l => !DocCast(l.link_anchor_2) || !getView(DocCast(l.link_anchor_2)!));
+        const backLinkWithoutTargetView = backLinks.find(l => !DocCast(l.link_anchor_1) || !getView(DocCast(l.link_anchor_1)!));
         const linkWithoutTargetDoc = traverseBacklink === undefined ? (fwdLinkWithoutTargetView ?? backLinkWithoutTargetView) : traverseBacklink ? backLinkWithoutTargetView : fwdLinkWithoutTargetView;
         const linkDocList = linkWithoutTargetDoc && !sourceDoc.followAllLinks ? [linkWithoutTargetDoc] : traverseBacklink === undefined ? fwdLinks.concat(backLinks) : traverseBacklink ? backLinks : fwdLinks;
         const followLinks = sourceDoc.followLinkToggle || sourceDoc.followAllLinks ? linkDocList : linkDocList.slice(0, 1);
@@ -60,17 +60,17 @@ export class LinkFollower {
             return false;
         }
         followLinks.forEach(async linkDoc => {
-            const target = (
+            const target = DocCast(
                 sourceDoc === linkDoc.link_anchor_1
                     ? linkDoc.link_anchor_2
                     : sourceDoc === linkDoc.link_anchor_2
                       ? linkDoc.link_anchor_1
-                      : Doc.AreProtosEqual(sourceDoc, linkDoc.link_anchor_1 as Doc) || Doc.AreProtosEqual((linkDoc.link_anchor_1 as Doc).annotationOn as Doc, sourceDoc)
+                      : Doc.AreProtosEqual(sourceDoc, DocCast(linkDoc.link_anchor_1)) || Doc.AreProtosEqual(DocCast(DocCast(linkDoc.link_anchor_1)?.annotationOn), sourceDoc)
                         ? linkDoc.link_anchor_2
                         : linkDoc.link_anchor_1
-            ) as Doc;
-            const srcAnchor: Doc = Doc.getOppositeAnchor(linkDoc, target) ?? sourceDoc;
+            );
             if (target) {
+                const srcAnchor = Doc.getOppositeAnchor(linkDoc, target) ?? sourceDoc;
                 const doFollow = (canToggle?: boolean) => {
                     const toggleTarget = canToggle && BoolCast(sourceDoc.followLinkToggle);
                     const options: FocusViewOptions = {
@@ -102,14 +102,16 @@ export class LinkFollower {
                 if (srcAnchor.followLinkLocation === OpenWhere.inParent) {
                     const sourceDocParent = DocCast(sourceDoc.embedContainer);
                     if (target.embedContainer instanceof Doc && target.embedContainer !== sourceDocParent) {
-                        Doc.RemoveDocFromList(target.embedContainer, Doc.LayoutFieldKey(target.embedContainer), target);
+                        Doc.RemoveDocFromList(target.embedContainer, Doc.LayoutDataKey(target.embedContainer), target);
                         movedTarget = true;
                     }
-                    if (!DocListCast(sourceDocParent[Doc.LayoutFieldKey(sourceDocParent)]).includes(target)) {
-                        Doc.AddDocToList(sourceDocParent, Doc.LayoutFieldKey(sourceDocParent), target);
-                        movedTarget = true;
+                    if (sourceDocParent) {
+                        if (!DocListCast(sourceDocParent[Doc.LayoutDataKey(sourceDocParent)]).includes(target)) {
+                            Doc.AddDocToList(sourceDocParent, Doc.LayoutDataKey(sourceDocParent), target);
+                            movedTarget = true;
+                        }
+                        Doc.SetContainer(target, sourceDocParent);
                     }
-                    Doc.SetContainer(target, sourceDocParent);
                 }
                 const moveTo = [NumCast(sourceDoc.x) + NumCast(sourceDoc.followLinkXoffset), NumCast(sourceDoc.y) + NumCast(sourceDoc.followLinkYoffset)];
                 if (srcAnchor.followLinkXoffset !== undefined && moveTo[0] !== target.x) {
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 3f98ab3c4..344e2e4c0 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -136,7 +136,7 @@ export class LinkManager {
             true
         );
         FieldLoader.ServerLoadStatus.message = 'links';
-        this.addLinkDB(Doc.LinkDBDoc());
+        Doc.LinkDBDoc() && this.addLinkDB(Doc.LinkDBDoc()!);
     }
 
     public createlink_relationshipLists = () => {
@@ -148,16 +148,21 @@ export class LinkManager {
 
     public addLink(linkDoc: Doc, checkExists = false) {
         Doc.AddDocToList(Doc.UserDoc(), 'links', linkDoc);
-        if (!checkExists || !DocListCast(Doc.LinkDBDoc().data).includes(linkDoc)) {
-            Doc.AddDocToList(Doc.LinkDBDoc(), 'data', linkDoc);
-            // eslint-disable-next-line no-use-before-define
-            setTimeout(UPDATE_SERVER_CACHE, 100);
+        if (Doc.LinkDBDoc()) {
+            if (!checkExists || !DocListCast(Doc.LinkDBDoc()!.data).includes(linkDoc)) {
+                Doc.AddDocToList(Doc.LinkDBDoc()!, 'data', linkDoc);
+                // eslint-disable-next-line no-use-before-define
+                setTimeout(UPDATE_SERVER_CACHE, 100);
+            }
         }
     }
     public deleteLink(linkDoc: Doc) {
-        const ret = Doc.RemoveDocFromList(Doc.LinkDBDoc(), 'data', linkDoc);
-        linkDoc.$link_anchor_1 = linkDoc.$link_anchor_2 = undefined;
-        return ret;
+        if (Doc.LinkDBDoc()) {
+            const ret = Doc.RemoveDocFromList(Doc.LinkDBDoc()!, 'data', linkDoc);
+            linkDoc.$link_anchor_1 = linkDoc.$link_anchor_2 = undefined;
+            return ret;
+        }
+        return false;
     }
     public deleteAllLinksOnAnchor(anchor: Doc) {
         LinkManager.Instance.relatedLinker(anchor).forEach(LinkManager.Instance.deleteLink);
@@ -178,8 +183,8 @@ export class LinkManager {
         }
 
         const dirLinks = Array.from(anchor[DocData][DirectLinks]).filter(l => Doc.GetProto(anchor) === anchor[DocData] || ['1', '2'].includes(LinkManager.anchorIndex(l, anchor) as '0' | '1' | '2'));
-        const anchorRoot = DocCast(anchor.rootDocument, anchor); // template Doc fields store annotations on the topmost root of a template (not on themselves since the template layout items are only for layout)
-        const annos = DocListCast(anchorRoot[Doc.LayoutFieldKey(anchor) + '_annotations']);
+        const anchorRoot = DocCast(anchor.rootDocument, anchor)!; // template Doc fields store annotations on the topmost root of a template (not on themselves since the template layout items are only for layout)
+        const annos = DocListCast(anchorRoot[Doc.LayoutDataKey(anchor) + '_annotations']);
         return Array.from(
             annos.reduce((set, anno) => {
                 if (!processed.includes(anno)) {
@@ -242,11 +247,13 @@ export function UPDATE_SERVER_CACHE() {
         .filter(doc => doc instanceof Doc)
         .map(doc => doc as Doc);
     const references = new Set<Doc>(prototypes);
+    DocCast(Doc.UserDoc().myLinkDatabase) && references.add(DocCast(Doc.UserDoc().myLinkDatabase)!); // prevent crawling through link database here -- see below
     Doc.FindReferences(Doc.UserDoc(), references, undefined);
-    DocListCast(DocCast(Doc.UserDoc().myLinkDatabase).data).forEach(link => {
-        if (!references.has(DocCast(link.link_anchor_1)) && !references.has(DocCast(link.link_anchor_2))) {
-            Doc.RemoveDocFromList(DocCast(Doc.UserDoc().myLinkDatabase), 'data', link);
-            Doc.AddDocToList(Doc.MyRecentlyClosed, undefined, link);
+
+    DocListCast(DocCast(Doc.UserDoc().myLinkDatabase)?.data).forEach(link => {
+        if (DocCast(link.link_anchor_1) && !references.has(DocCast(link.link_anchor_1)!) && DocCast(link.link_anchor_2) && !references.has(DocCast(link.link_anchor_2)!)) {
+            DocCast(Doc.UserDoc().myLinkDatabase) && Doc.RemoveDocFromList(DocCast(Doc.UserDoc().myLinkDatabase)!, 'data', link);
+            Doc.MyRecentlyClosed && Doc.AddDocToList(Doc.MyRecentlyClosed, undefined, link);
         }
     });
     LinkManager.Instance.userLinkDBs.forEach(linkDb => Doc.FindReferences(linkDb, references, undefined));
@@ -257,10 +264,10 @@ export function UPDATE_SERVER_CACHE() {
     cacheDocumentIds = newCacheUpdate;
 
     // print out cached docs
-    //Doc.MyDockedBtns.linearView_IsOpen && console.log('Set cached docs = ');
-    // const isFiltered = filtered.filter(doc => !Doc.IsSystem(doc));
-    // const strings = isFiltered.map(doc => StrCast(doc.title) + ' ' + (Doc.IsDataProto(doc) ? '(data)' : '(embedding)'));
-    //Doc.MyDockedBtns.linearView_IsOpen && strings.sort().forEach((str, i) => console.log(i.toString() + ' ' + str));
+    Doc.MyDockedBtns?.linearView_IsOpen && console.log('Set cached docs = ');
+    const isFiltered = filtered.filter(doc => !Doc.IsSystem(doc));
+    const strings = isFiltered.map(doc => StrCast(doc.title) + ' ' + (Doc.IsDataProto(doc) ? '(data)' : '(embedding)'));
+    Doc.MyDockedBtns?.linearView_IsOpen && strings.sort().forEach((str, i) => console.log(i.toString() + ' ' + str));
 
     rp.post(ClientUtils.prepend('/setCacheDocumentIds'), {
         body: {
diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts
index 733eae5f4..e4adcaa7e 100644
--- a/src/client/util/SearchUtil.ts
+++ b/src/client/util/SearchUtil.ts
@@ -9,7 +9,7 @@ export namespace SearchUtil {
     export type HighlightingResult = { [id: string]: { [key: string]: string[] } };
 
     export function SearchCollection(collectionDoc: Opt<Doc>, queryIn: string, matchKeyNames: boolean, onlyKeys?: string[]) {
-        const blockedTypes = [DocumentType.PRESELEMENT, DocumentType.CONFIG, DocumentType.KVP, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING];
+        const blockedTypes = [DocumentType.PRESSLIDE, DocumentType.CONFIG, DocumentType.KVP, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING];
         const blockedKeys = matchKeyNames
             ? []
             : Object.entries(DocOptions)
@@ -21,7 +21,7 @@ export namespace SearchUtil {
 
         const results = new ObservableMap<Doc, string[]>();
         if (collectionDoc) {
-            const docs = DocListCast(collectionDoc[Doc.LayoutFieldKey(collectionDoc)]);
+            const docs = DocListCast(collectionDoc[Doc.LayoutDataKey(collectionDoc)]);
             // eslint-disable-next-line @typescript-eslint/ban-types
             const docIDs: String[] = [];
             SearchUtil.foreachRecursiveDoc(docs, (depth: number, doc: Doc) => {
@@ -77,7 +77,7 @@ export namespace SearchUtil {
             // eslint-disable-next-line no-loop-func
             docs.filter(d => d && !visited.includes(d)).forEach(d => {
                 visited.push(d);
-                const fieldKey = Doc.LayoutFieldKey(d);
+                const fieldKey = Doc.LayoutDataKey(d);
                 const annos = !Field.toString(Doc.LayoutField(d) as FieldType).includes('CollectionView');
                 const data = d[annos ? fieldKey + '_annotations' : fieldKey];
                 data && newarray.push(...DocListCast(data));
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 88f1f3260..9e79fd870 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -578,7 +578,7 @@ export class SettingsManager extends React.Component<object> {
                         <div className="settings-username" style={{ color: SettingsManager.userBackgroundColor }}>
                             {ClientUtils.CurrentUserEmail()}
                         </div>
-                        <Button text={Doc.GuestDashboard ? 'Exit' : 'Log Out'} type={Type.TERT} color={SettingsManager.userVariantColor} onClick={() => window.location.assign(ClientUtils.prepend('/logout'))} />
+                        <Button text={Doc.GuestDashboard ? 'Exit' : 'Log Out'} type={Type.TERT} color={SettingsManager.userVariantColor} background={SettingsManager.userColor} onClick={() => window.location.assign(ClientUtils.prepend('/logout'))} />
                     </div>
                 </div>
 
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 45c80c6f7..1ecd82dbc 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -48,7 +48,7 @@ export function DocComponent<P extends DocComponentProps>() {
          * This Doc inherits from the dataDoc, and may or may not inherit (or be) the layoutDoc.
          */
         get rootDoc() {
-            return DocCast(this.Document.rootDocument, this.Document);
+            return DocCast(this.Document.rootDocument, this.Document)!;
         }
 
         /**
@@ -64,7 +64,7 @@ export function DocComponent<P extends DocComponentProps>() {
          * This may or may not inherit from the data doc.
          */
         @computed get layoutDoc() {
-            return this._props.LayoutTemplateString ? this.Document : Doc.Layout(this.Document, this._props.LayoutTemplate?.());
+            return this._props.LayoutTemplateString ? this.Document : Doc.LayoutDoc(this.Document, this._props.LayoutTemplate?.());
         }
 
         /**
@@ -115,7 +115,7 @@ export function ViewBoxBaseComponent<P extends FieldViewProps>() {
          *       consider: Document, layoutDoc, dataDoc
          */
         get rootDoc() {
-            return DocCast(this.Document.rootDocument, this.Document);
+            return DocCast(this.Document.rootDocument, this.Document)!;
         }
         /**
          * This is the document being rendered by the React component. In the
@@ -189,7 +189,7 @@ export function ViewBoxAnnotatableComponent<P extends FieldViewProps>() {
          *       other Doc options (Document, layoutDoc, dataDoc)
          */
         @computed get rootDoc() {
-            return DocCast(this.Document.rootDocument, this.Document);
+            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.
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 6f2f051cd..8a850467a 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -303,7 +303,7 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
                             DocumentView.Selected().forEach(dv => {
                                 dv.layoutDoc._layout_showTags = !showing;
                                 if (e.shiftKey)
-                                    DocListCast(dv.Document[Doc.LayoutFieldKey(dv.Document) + '_annotations']).forEach(doc => {
+                                    DocListCast(dv.Document[Doc.LayoutDataKey(dv.Document) + '_annotations']).forEach(doc => {
                                         if (doc.face) doc.hidden = showing;
                                     });
                             });
@@ -479,13 +479,13 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
         const doc = rootView?.Document;
         if (doc) {
             const anchor = rootView.ComponentView?.getAnchor?.(true) ?? doc;
-            const trail = DocCast(anchor.presentationTrail) ?? Doc.MakeCopy(DocCast(Doc.UserDoc().emptyTrail), true);
+            const trail = DocCast(anchor.presentationTrail) ?? (DocCast(Doc.UserDoc().emptyTrail) ? Doc.MakeCopy(DocCast(Doc.UserDoc().emptyTrail)!, true) : undefined);
             if (trail !== anchor.presentationTrail) {
-                DocUtils.MakeLink(anchor, trail, { link_relationship: 'link trail' });
+                trail && DocUtils.MakeLink(anchor, trail, { link_relationship: 'link trail' });
                 anchor.presentationTrail = trail;
             }
             Doc.ActivePresentation = trail;
-            this._props.views().lastElement()?._props.addDocTab(trail, OpenWhere.replaceRight);
+            trail && this._props.views().lastElement()?._props.addDocTab(trail, OpenWhere.replaceRight);
         }
         e.stopPropagation();
     };
diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
index c3b3f9d0c..848a77004 100644
--- a/src/client/views/FilterPanel.tsx
+++ b/src/client/views/FilterPanel.tsx
@@ -182,7 +182,7 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
         return targetView?.ComponentView?.annotationKey || (targetView?.ComponentView?.fieldKey ?? 'data');
     }
     @computed get targetDocChildren() {
-        return [...DocListCast(this.Document?.[this.targetDocChildKey] || Doc.ActiveDashboard?.data), ...DocListCast(this.Document[Doc.LayoutFieldKey(this.Document) + '_sidebar'])];
+        return [...DocListCast(this.Document?.[this.targetDocChildKey] || Doc.ActiveDashboard?.data), ...DocListCast(this.Document[Doc.LayoutDataKey(this.Document) + '_sidebar'])];
     }
 
     @computed get rangeFilters() {
@@ -236,7 +236,7 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
                     if (facetVal instanceof RichTextField || typeof facetVal === 'string') rtFields++;
                     facetVal !== undefined && valueSet.add(Field.toString(facetVal as FieldType));
                     (facetVal === true || facetVal === false) && valueSet.add(Field.toString(!facetVal));
-                    const fieldKey = Doc.LayoutFieldKey(t);
+                    const fieldKey = Doc.LayoutDataKey(t);
                     const annos = !Field.toString(Doc.LayoutField(t) as FieldType).includes('CollectionView');
                     DocListCast(t[annos ? fieldKey + '_annotations' : fieldKey]).forEach(newdoc => newarray.push(newdoc));
                     annos && DocListCast(t[fieldKey + '_sidebar']).forEach(newdoc => newarray.push(newdoc));
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 94c023330..948beec74 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -239,7 +239,7 @@ export class KeyManager {
                 break;
             case 'i':
                 {
-                    const importBtn = DocListCast(Doc.MyLeftSidebarMenu.data).find(d => d.target === Doc.MyImports);
+                    const importBtn = DocListCast(Doc.MyLeftSidebarMenu?.data).find(d => d.target === Doc.MyImports);
                     if (importBtn) {
                         MainView.Instance.selectLeftSidebarButton(importBtn);
                     }
@@ -247,7 +247,7 @@ export class KeyManager {
                 break;
             case 's':
                 {
-                    const trailsBtn = DocListCast(Doc.MyLeftSidebarMenu.data).find(d => d.target === Doc.MyTrails);
+                    const trailsBtn = DocListCast(Doc.MyLeftSidebarMenu?.data).find(d => d.target === Doc.MyTrails);
                     if (trailsBtn) {
                         MainView.Instance.selectLeftSidebarButton(trailsBtn);
                     }
@@ -257,7 +257,7 @@ export class KeyManager {
                 if (DocumentView.Selected().length === 1 && DocumentView.Selected()[0].ComponentView?.search) {
                     DocumentView.Selected()[0].ComponentView?.search?.('', false, false);
                 } else {
-                    const searchBtn = DocListCast(Doc.MyLeftSidebarMenu.data).find(d => d.target === Doc.MySearcher);
+                    const searchBtn = DocListCast(Doc.MyLeftSidebarMenu?.data).find(d => d.target === Doc.MySearcher);
                     if (searchBtn) {
                         MainView.Instance.selectLeftSidebarButton(searchBtn);
                     }
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts
index 6854476e2..425f5b6bb 100644
--- a/src/client/views/InkStrokeProperties.ts
+++ b/src/client/views/InkStrokeProperties.ts
@@ -341,7 +341,7 @@ export class InkStrokeProperties {
      */
     snapControl = (inkView: DocumentView, controlIndex: number) => {
         const inkDoc = inkView.Document;
-        const ink = Cast(inkDoc[Doc.LayoutFieldKey(inkDoc)], InkField)?.inkData;
+        const ink = Cast(inkDoc[Doc.LayoutDataKey(inkDoc)], InkField)?.inkData;
 
         if (ink) {
             const screenDragPt = inkView.ComponentView?.ptToScreen?.(ink[controlIndex]);
diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx
index 1293aa9d0..2e6b477e9 100644
--- a/src/client/views/InkTranscription.tsx
+++ b/src/client/views/InkTranscription.tsx
@@ -118,9 +118,9 @@ export class InkTranscription extends React.Component {
 
         const times: number[] = [];
         validInks
-            .filter(i => Cast(i[Doc.LayoutFieldKey(i)], InkField))
+            .filter(i => Cast(i[Doc.LayoutDataKey(i)], InkField))
             .forEach(i => {
-                const d = Cast(i[Doc.LayoutFieldKey(i)], InkField, null);
+                const d = Cast(i[Doc.LayoutDataKey(i)], InkField, null);
                 const inkStroke = DocumentView.getDocumentView(i)?.ComponentView as InkingStroke;
                 strokes.push(d.inkData.map(pd => inkStroke.ptToScreen({ X: pd.X, Y: pd.Y })));
                 times.push(DateCast(i.author_date).getDate().getTime());
@@ -382,7 +382,7 @@ export class InkTranscription extends React.Component {
 
     render() {
         return (
-            <div className="ink-transcription" style={{pointerEvents: 'none'}}>
+            <div className="ink-transcription" style={{ pointerEvents: 'none' }}>
                 <div className="math-editor" ref={this.setMathRef}></div>
                 <div className="text-editor" ref={this.setTextRef}></div>
             </div>
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index de2c7cd09..0eb21b943 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -136,7 +136,7 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> {
         return this.SetLightboxDoc(
             doc,
             undefined,
-            [...DocListCast(doc[Doc.LayoutFieldKey(doc) + '_annotations']).filter(anno => anno.annotationOn !== doc), ...this._future].sort((a, b) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)),
+            [...DocListCast(doc[Doc.LayoutDataKey(doc) + '_annotations']).filter(anno => anno.annotationOn !== doc), ...this._future].sort((a, b) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)),
             layoutTemplate
         );
     };
@@ -187,7 +187,7 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> {
             saved: this._savedState,
         });
         if (this._docTarget) {
-            const fieldKey = Doc.LayoutFieldKey(this._docTarget);
+            const fieldKey = Doc.LayoutDataKey(this._docTarget);
             const contents = [...DocListCast(this._docTarget[fieldKey]), ...DocListCast(this._docTarget[fieldKey + '_annotations'])];
             const links = Doc.Links(this._docTarget)
                 .map(link => Doc.getOppositeAnchor(link, this._docTarget!)!)
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 22725a2b9..e4bbb1c0f 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -61,7 +61,7 @@ import { FootnoteView } from './nodes/formattedText/FootnoteView';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import { SummaryView } from './nodes/formattedText/SummaryView';
 import { ImportElementBox } from './nodes/importBox/ImportElementBox';
-import { PresBox, PresElementBox } from './nodes/trails';
+import { PresBox, PresSlideBox } from './nodes/trails';
 import { FaceRecognitionHandler } from './search/FaceRecognitionHandler';
 import { SearchBox } from './search/SearchBox';
 import { StickerPalette } from './smartdraw/StickerPalette';
@@ -135,7 +135,7 @@ FieldLoader.ServerLoadStatus = { requested: 0, retrieved: 0, message: 'cache' };
             AudioBox,
             RecordingBox,
             PresBox,
-            PresElementBox,
+            PresSlideBox,
             SearchBox,
             ImageLabelBox,
             FaceCollectionBox,
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index be6e2fecb..ad6bb09c7 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -93,7 +93,7 @@ export class MainView extends ObservableReactComponent<object> {
     @observable private _dashUIWidth: number = 0; // width of entire main dashboard region including left menu buttons and properties panel (but not including the dashboard selector button row)
     @observable private _dashUIHeight: number = 0; // height of entire main dashboard region including top menu buttons
     @observable private _panelContent: string = 'none';
-    @observable private _sidebarContent: Doc = Doc.MyLeftSidebarPanel;
+    @observable private _sidebarContent?: Doc = Doc.MyLeftSidebarPanel;
     @observable private _leftMenuFlyoutWidth: number = 0;
     @computed get _hideUI() {
         return SnappingManager.HideUI || (this.mainDoc && this.mainDoc._type_collection !== CollectionViewType.Docking);
@@ -173,7 +173,7 @@ export class MainView extends ObservableReactComponent<object> {
             views => views.length > 1 && document.activeElement instanceof HTMLElement && document.activeElement?.blur()
         );
         reaction(
-            () => Doc.MyDockedBtns.linearView_IsOpen,
+            () => Doc.MyDockedBtns?.linearView_IsOpen,
             open => SnappingManager.SetPrintToConsole(!!open)
         );
         const scriptTag = document.createElement('script');
@@ -199,7 +199,7 @@ export class MainView extends ObservableReactComponent<object> {
                 ele.outerHTML = '';
             }, 1000);
         }
-        this._sidebarContent.proto = undefined;
+        this._sidebarContent && (this._sidebarContent.proto = undefined);
         if (!MainView.Live) {
             DocServer.setLivePlaygroundFields([
                 'dataTransition',
@@ -636,8 +636,10 @@ export class MainView extends ObservableReactComponent<object> {
     openPresentation = (pres: Doc) => {
         if (pres.type === DocumentType.PRES) {
             CollectionDockingView.AddSplit(pres, OpenWhereMod.right, undefined, PresBox.PanelName);
-            Doc.MyTrails && (Doc.ActivePresentation = pres);
-            Doc.AddDocToList(Doc.MyTrails, 'data', pres);
+            if (Doc.MyTrails) {
+                Doc.ActivePresentation = pres;
+                Doc.AddDocToList(Doc.MyTrails, 'data', pres);
+            }
             this.closeFlyout();
         }
     };
@@ -645,16 +647,16 @@ export class MainView extends ObservableReactComponent<object> {
     @action
     createNewFolder = async () => {
         const folder = Docs.Create.TreeDocument([], { title: 'Untitled folder', _dragOnlyWithinContainer: true, isFolder: true });
-        Doc.AddDocToList(Doc.MyFilesystem, 'data', folder);
+        Doc.MyFilesystem && Doc.AddDocToList(Doc.MyFilesystem, 'data', folder);
     };
 
     waitForDoubleClick = () => (SnappingManager.ExploreMode ? 'never' : undefined);
     headerBarScreenXf = () => new Transform(-this.leftScreenOffsetOfMainDocView - this.leftMenuFlyoutWidth(), -this.headerBarDocHeight(), 1);
     mainScreenToLocalXf = () => new Transform(-this.leftScreenOffsetOfMainDocView - this.leftMenuFlyoutWidth(), -this.topOfMainDocContent, 1);
-    addHeaderDoc = (docs: Doc | Doc[]) => toList(docs).reduce((done, doc) => Doc.AddDocToList(this.headerBarDoc, 'data', doc), true);
-    removeHeaderDoc = (docs: Doc | Doc[]) => toList(docs).reduce((done, doc) => Doc.RemoveDocFromList(this.headerBarDoc, 'data', doc), true);
+    addHeaderDoc = (docs: Doc | Doc[]) => toList(docs).reduce((done, doc) => !!this.headerBarDoc && Doc.AddDocToList(this.headerBarDoc, 'data', doc), true);
+    removeHeaderDoc = (docs: Doc | Doc[]) => toList(docs).reduce((done, doc) => !!this.headerBarDoc && Doc.RemoveDocFromList(this.headerBarDoc, 'data', doc), true);
     @computed get headerBarDocView() {
-        return (
+        return !this.headerBarDoc ? null : (
             <div className="mainView-headerBar" style={{ height: this.headerBarDocHeight() }}>
                 <DocumentView
                     key="headerBarDoc"
@@ -792,16 +794,16 @@ export class MainView extends ObservableReactComponent<object> {
             <div key="flyout" className="mainView-libraryFlyout-out">
                 {this.docButtons}
             </div>
-        ) : (
+        ) : !this._sidebarContent ? null : (
             <div key="libFlyout" className="mainView-libraryFlyout" style={{ minWidth: this._leftMenuFlyoutWidth, width: this._leftMenuFlyoutWidth }}>
                 <div className="mainView-contentArea">
                     <DocumentView
-                        Document={DocCast(this._sidebarContent.proto, this._sidebarContent)}
+                        Document={DocCast(this._sidebarContent?.proto, this._sidebarContent)!}
                         addDocument={undefined}
                         addDocTab={DocumentViewInternal.addDocTabFunc}
                         pinToPres={DocumentView.PinDoc}
                         containerViewPath={returnEmptyDocViewList}
-                        styleProvider={this._sidebarContent.proto === Doc.MyDashboards || this._sidebarContent.proto === Doc.MyFilesystem || this._sidebarContent.proto === Doc.MyTrails ? DashboardStyleProvider : DefaultStyleProvider}
+                        styleProvider={this._sidebarContent?.proto === Doc.MyDashboards || this._sidebarContent.proto === Doc.MyFilesystem || this._sidebarContent.proto === Doc.MyTrails ? DashboardStyleProvider : DefaultStyleProvider}
                         removeDocument={returnFalse}
                         ScreenToLocalTransform={this.mainContainerXf}
                         PanelWidth={this.leftMenuFlyoutWidth}
@@ -821,7 +823,7 @@ export class MainView extends ObservableReactComponent<object> {
     }
 
     @computed get leftMenuPanel() {
-        return (
+        return !Doc.MyLeftSidebarMenu ? null : (
             <div key="menu" className="mainView-leftMenuPanel" style={{ background: SnappingManager.userBackgroundColor, display: DocumentView.LightboxDoc() ? 'none' : undefined }}>
                 <DocumentView
                     Document={Doc.MyLeftSidebarMenu}
@@ -861,7 +863,7 @@ export class MainView extends ObservableReactComponent<object> {
                     break;
                 default:
                     this._leftMenuFlyoutWidth = this._leftMenuFlyoutWidth || 250;
-                    this._sidebarContent.proto = DocCast(button.target);
+                    this._sidebarContent && (this._sidebarContent.proto = DocCast(button.target));
                     SnappingManager.SetLastPressedBtn(button[Id]);
             }
         }
@@ -875,8 +877,10 @@ export class MainView extends ObservableReactComponent<object> {
      */
     addHotKey = (hotKey: string) => {
         const filterIcons = DocCast(DocCast(Doc.UserDoc().myContextMenuBtns)?.Filter);
-        const menuDoc = CurrentUserUtils.setupContextMenuBtn(CurrentUserUtils.filterBtnDesc(ToTagName(hotKey), 'question'), filterIcons);
-        Doc.AddToFilterHotKeys(menuDoc);
+        if (filterIcons) {
+            const menuDoc = CurrentUserUtils.setupContextMenuBtn(CurrentUserUtils.filterBtnDesc(ToTagName(hotKey), 'question'), filterIcons);
+            Doc.AddToFilterHotKeys(menuDoc);
+        }
     };
 
     @computed get mainInnerContent() {
@@ -941,13 +945,13 @@ export class MainView extends ObservableReactComponent<object> {
     closeFlyout = action(() => {
         SnappingManager.SetLastPressedBtn('');
         this._panelContent = 'none';
-        this._sidebarContent.proto = undefined;
+        this._sidebarContent && (this._sidebarContent.proto = undefined);
         this._leftMenuFlyoutWidth = 0;
     });
 
-    remButtonDoc = (docs: Doc | Doc[]) => toList(docs).reduce((flg: boolean, doc) => flg && !doc.dragOnlyWithinContainer && Doc.RemoveDocFromList(Doc.MyDockedBtns, 'data', doc), true);
+    remButtonDoc = (docs: Doc | Doc[]) => toList(docs).reduce((flg: boolean, doc) => flg && !doc.dragOnlyWithinContainer && !!Doc.MyDockedBtns && Doc.RemoveDocFromList(Doc.MyDockedBtns!, 'data', doc), true);
     moveButtonDoc = (docs: Doc | Doc[], targetCol: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => this.remButtonDoc(docs) && addDocument(docs);
-    addButtonDoc = (docs: Doc | Doc[]) => toList(docs).reduce((flg: boolean, doc) => flg && Doc.AddDocToList(Doc.MyDockedBtns, 'data', doc), true);
+    addButtonDoc = (docs: Doc | Doc[]) => toList(docs).reduce((flg: boolean, doc) => flg && !!Doc.MyDockedBtns && Doc.AddDocToList(Doc.MyDockedBtns!, 'data', doc), true);
 
     buttonBarXf = () => {
         if (!this._docBtnRef.current) return Transform.Identity();
diff --git a/src/client/views/PinFuncs.ts b/src/client/views/PinFuncs.ts
index d756830da..805dd9a49 100644
--- a/src/client/views/PinFuncs.ts
+++ b/src/client/views/PinFuncs.ts
@@ -70,13 +70,13 @@ export function PinDocView(pinDocIn: Doc, pinProps: PinProps, targetDoc: Doc) {
             pinProps.pinData.dataview ||
             pinProps.pinData.poslayoutview ||
             pinProps?.activeFrame !== undefined;
-        const fkey = Doc.LayoutFieldKey(targetDoc);
+        const fkey = Doc.LayoutDataKey(targetDoc);
         if (pinProps.pinData.dataview) {
             pinDoc.config_usePath = targetDoc[fkey + '_usePath'];
             pinDoc.config_data = Field.Copy(targetDoc[fkey]);
         }
         if (pinProps.pinData.dataannos) {
-            const fieldKey = '$' + Doc.LayoutFieldKey(targetDoc) + +'_annotations';
+            const fieldKey = '$' + Doc.LayoutDataKey(targetDoc) + +'_annotations';
             pinDoc.config_annotations = new List<Doc>(DocListCast(targetDoc[fieldKey]).filter(doc => !doc.layout_unrendered));
         }
         if (pinProps.pinData.inkable) {
@@ -87,7 +87,7 @@ export function PinDocView(pinDocIn: Doc, pinProps: PinProps, targetDoc: Doc) {
         }
         if (pinProps.pinData.scrollable) pinDoc.config_scrollTop = targetDoc._layout_scrollTop;
         if (pinProps.pinData.clippable) {
-            const fieldKey = Doc.LayoutFieldKey(targetDoc);
+            const fieldKey = Doc.LayoutDataKey(targetDoc);
             pinDoc.config_clipWidth = targetDoc[fieldKey + '_clipWidth'];
         }
         if (pinProps.pinData.datarange) {
@@ -127,7 +127,7 @@ export function PinDocView(pinDocIn: Doc, pinProps: PinProps, targetDoc: Doc) {
         }
         if (pinProps.pinData.temporal) {
             pinDoc.config_clipStart = targetDoc._layout_currentTimecode;
-            const duration = NumCast(pinDoc[`${Doc.LayoutFieldKey(pinDoc)}_duration`], NumCast(targetDoc.config_clipStart) + 0.1);
+            const duration = NumCast(pinDoc[`${Doc.LayoutDataKey(pinDoc)}_duration`], NumCast(targetDoc.config_clipStart) + 0.1);
             pinDoc.config_clipEnd = NumCast(pinDoc.config_clipStart) + NumCast(targetDoc.clipEnd, duration);
         }
     }
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 6778a6691..66936c64c 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -447,7 +447,7 @@ export class PropertiesButtons extends React.Component {
     };
 
     render() {
-        const layoutField = this.selectedDoc?.[Doc.LayoutFieldKey(this.selectedDoc)];
+        const layoutField = this.selectedDoc?.[Doc.LayoutDataKey(this.selectedDoc)];
         const isText = DocumentView.Selected().lastElement()?.ComponentView instanceof FormattedTextBox;
         const isInk = this.selectedDoc?.layout_isSvg;
         const isImage = layoutField instanceof ImageField;
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 7fcb15afe..e7186c0e3 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -525,7 +525,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
             let permission;
             if (this.layoutDocAcls) {
                 if (target[DocAcl][userKey]) permission = HierarchyMapping.get(target[DocAcl][userKey])?.name;
-                else if (target.embedContainer) permission = StrCast(Doc.GetProto(DocCast(target.embedContainer))[userKey]);
+                else if (DocCast(target.embedContainer)) permission = StrCast(Doc.GetProto(DocCast(target.embedContainer)!)[userKey]);
                 else permission = StrCast(Doc.GetProto(target)?.[userKey]);
             } else permission = StrCast(target[userKey]);
             individualTableEntries.unshift(this.sharingItem(userEmail, showAdmin, permission!, false)); // adds each user
@@ -546,7 +546,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
                     if (this.layoutDocAcls) {
                         if (target[DocAcl][groupKey]) {
                             permission = HierarchyMapping.get(target[DocAcl][groupKey])?.name;
-                        } else if (target.embedContainer) permission = StrCast(Doc.GetProto(DocCast(target.embedContainer))[groupKey]);
+                        } else if (DocCast(target.embedContainer)) permission = StrCast(Doc.GetProto(DocCast(target.embedContainer)!)[groupKey]);
                         else permission = StrCast(Doc.GetProto(target)?.[groupKey]);
                     } else permission = StrCast(target[groupKey]);
                     groupTableEntries.unshift(this.sharingItem(StrCast(group.title), showAdmin, permission!, false));
@@ -1921,7 +1921,9 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
             const selectedItem: boolean = PresBox.Instance.selectedArray.size > 0;
             const type = [DocumentType.AUDIO, DocumentType.VID].includes(DocCast(PresBox.Instance.activeItem?.annotationOn)?.type as DocumentType)
                 ? (DocCast(PresBox.Instance.activeItem?.annotationOn)?.type as DocumentType)
-                : PresBox.targetRenderedDoc(PresBox.Instance.activeItem)?.type;
+                : PresBox.Instance.activeItem
+                  ? PresBox.targetRenderedDoc(PresBox.Instance.activeItem)?.type
+                  : undefined;
             return (
                 <div className="propertiesView" style={{ width: this._props.width }}>
                     <div className="propertiesView-sectionTitle" style={{ width: this._props.width }}>
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index e8876f96f..9110e198f 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -129,7 +129,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
     // prettier-ignore
     switch (property.split(':')[0]) {
         case StyleProp.TreeViewIcon: {
-            const img = ImageCast(doc?.icon ?? doc?.[doc ? Doc.LayoutFieldKey(doc) : ""]);
+            const img = ImageCast(doc?.icon ?? doc?.[doc ? Doc.LayoutDataKey(doc) : ""]);
             if (img) {
                 const ext = extname(img.url.href);
                 const url = doc?.icon ? img.url.href : img.url.href.replace(ext, '_s' + ext);
@@ -164,7 +164,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
             return  undefined;
         case StyleProp.DocContents:  return undefined;
         case StyleProp.WidgetColor:  return isAnnotated ? Colors.LIGHT_BLUE : 'dimgrey';
-        case StyleProp.Opacity:      return componentView?.isUnstyledView?.() ? 1 : Cast(doc?._opacity, "number", Cast(doc?.opacity, 'number', null));
+        case StyleProp.Opacity:      return componentView?.isUnstyledView?.() ? 1 : Cast(doc?._opacity, "number", Cast(doc?.opacity, 'number', null) ?? null);
         case StyleProp.FontColor:    return StrCast(doc?.[fieldKey + 'fontColor'],  isCaption ? lightOrDark(backgroundCol()) : StrCast(Doc.UserDoc().fontColor, color()));
         case StyleProp.FontSize:     return StrCast(doc?.[fieldKey + 'fontSize'],   StrCast(Doc.UserDoc().fontSize));
         case StyleProp.FontFamily:   return StrCast(doc?.[fieldKey + 'fontFamily'], StrCast(Doc.UserDoc().fontFamily));
@@ -248,13 +248,13 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
                 : 0;
         case StyleProp.BackgroundColor: {
             if (SnappingManager.LastPressedBtn === doc?.[Id]) return SnappingManager.userColor; // hack to indicate active menu panel item
-            const dataKey = doc ? Doc.LayoutFieldKey(doc) : '';
+            const dataKey = doc ? Doc.LayoutDataKey(doc) : '';
             const usePath = StrCast(doc?.[dataKey + '_usePath']);
             const alternate = usePath.includes(':hover') ? ( isHovering?.() ? '_' + usePath.replace(':hover','') : '') : usePath ? "_" +usePath:usePath;
             let docColor:Opt<string> = layoutDoc &&
                 StrCast(alternate ? layoutDoc['backgroundColor' + alternate]:undefined,
-                    doc.rootDocument 
-                        ? StrCast(layoutDoc.backgroundColor, StrCast(DocCast(doc.rootDocument).backgroundColor))  // for nested templates: use template's color, then root doc's color
+                    DocCast(doc.rootDocument)
+                        ? StrCast(layoutDoc.backgroundColor, StrCast(DocCast(doc.rootDocument)!.backgroundColor))  // for nested templates: use template's color, then root doc's color
                         : layoutDoc === doc 
                             ? StrCast(doc.backgroundColor)
                             : StrCast(StrCast(Doc.GetT(layoutDoc, 'backgroundColor', 'string', true), StrCast(doc.backgroundColor, StrCast(layoutDoc.backgroundColor)) // otherwise, use expanded template coloor, then root doc's color, then template's inherited color
@@ -262,7 +262,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
         
             // prettier-ignore
             switch (layoutDoc?.type) {
-                case DocumentType.PRESELEMENT: docColor = docColor || ""; break;
+                case DocumentType.PRESSLIDE: docColor = docColor || ""; break;
                 case DocumentType.PRES:        docColor = docColor || 'transparent'; break;
                 case DocumentType.FONTICON:    docColor = boxBackground ? undefined : docColor || SnappingManager.userBackgroundColor; break;
                 case DocumentType.RTF:         docColor = docColor || StrCast(Doc.UserDoc().textBackgroundColor, Colors.LIGHT_GRAY); break;
diff --git a/src/client/views/StyleProviderQuiz.tsx b/src/client/views/StyleProviderQuiz.tsx
index b4a1f53c5..fe76bce28 100644
--- a/src/client/views/StyleProviderQuiz.tsx
+++ b/src/client/views/StyleProviderQuiz.tsx
@@ -67,7 +67,7 @@ export namespace styleProviderQuiz {
             newCol.zIndex = 1000;
             newCol.forceActive = true;
             newCol.quiz = text;
-            newCol['$' + Doc.LayoutFieldKey(newCol) + '_transform'] = 'none';
+            newCol['$' + Doc.LayoutDataKey(newCol) + '_transform'] = 'none';
             Doc.AddDocToList(img.Document, 'quizBoxes', newCol);
             img.addDocument(newCol);
             //   img._loading = false;
diff --git a/src/client/views/TagsView.tsx b/src/client/views/TagsView.tsx
index 79c4d8af1..40d4f108d 100644
--- a/src/client/views/TagsView.tsx
+++ b/src/client/views/TagsView.tsx
@@ -21,6 +21,7 @@ import { FaceRecognitionHandler } from './search/FaceRecognitionHandler';
 import { IconTagBox } from './nodes/IconTagBox';
 import { Id } from '../../fields/FieldSymbols';
 import { StyleProp } from './StyleProp';
+import { Docs } from '../documents/Documents';
 
 /**
  * The TagsView is a metadata input/display panel shown at the bottom of a DocumentView in a freeform collection.
@@ -171,6 +172,7 @@ export class TagItem extends ObservableReactComponent<TagItemProps> {
         const newEmbeddings = TagItem.allDocsWithTag(this._props.tag).map(doc => Doc.MakeEmbedding(doc));
 
         // Create a new collection and set up configurations.
+        const emptyCol = DocCast(Doc.UserDoc().emptyCollection);
         const newCollection = ((doc: Doc) => {
             doc.$data = new List<Doc>(newEmbeddings);
             doc.$title = this._props.tag;
@@ -182,7 +184,7 @@ export class TagItem extends ObservableReactComponent<TagItemProps> {
             doc.layout_fitWidth = true;
             doc._layout_showTags = true;
             return doc;
-        })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
+        })(emptyCol ? Doc.MakeCopy(emptyCol, true) : Docs.Create.FreeformDocument([], {}));
         newEmbeddings.forEach(embed => Doc.SetContainer(embed, newCollection));
 
         // Add the collection to the tag document's list of associated smart collections.
@@ -342,7 +344,7 @@ export class TagsView extends ObservableReactComponent<TagViewProps> {
         const tagsList = new Set<string>(StrListCast(this.View.dataDoc.tags));
         const chatTagsList = new Set<string>(StrListCast(this.View.dataDoc.tags_chat));
         const facesList = new Set<string>(
-            DocListCast(this.View.dataDoc[Doc.LayoutFieldKey(this.View.Document) + '_annotations'])
+            DocListCast(this.View.dataDoc[Doc.LayoutDataKey(this.View.Document) + '_annotations'])
                 .concat(this.View.Document)
                 .filter(d => d.face)
                 .map(doc => StrCast(DocCast(doc.face)?.title))
diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx
index cd2c7df1b..814e9a7a0 100644
--- a/src/client/views/animationtimeline/Timeline.tsx
+++ b/src/client/views/animationtimeline/Timeline.tsx
@@ -92,7 +92,7 @@ export class Timeline extends ObservableReactComponent<FieldViewProps & { Doc: D
     private get children(): Doc[] {
         const annotatedDoc = [DocumentType.IMG, DocumentType.VID, DocumentType.PDF, DocumentType.MAP].includes(StrCast(this._props.Doc.type) as unknown as DocumentType);
         if (annotatedDoc) {
-            return DocListCast(this._props.Doc[Doc.LayoutFieldKey(this._props.Doc) + '_annotations']);
+            return DocListCast(this._props.Doc[Doc.LayoutDataKey(this._props.Doc) + '_annotations']);
         }
         return DocListCast(this._props.Doc[this._props.fieldKey]);
     }
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 2d3f8cb0e..975dc52fe 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -77,7 +77,8 @@ export class CollectionCarouselView extends CollectionSubView() {
     focus = (anchor: Doc, options: FocusViewOptions): Opt<number> => {
         const docs = DocListCast(this.Document[this.fieldKey]);
         if (anchor.type === DocumentType.CONFIG || docs.includes(anchor)) {
-            const newIndex = anchor.config_carousel_index ?? docs.getIndex(DocCast(anchor.annotationOn, anchor));
+            const annoOn = DocCast(anchor.annotationOn, anchor);
+            const newIndex = NumCast(anchor.config_carousel_index, (annoOn && docs.getIndex(annoOn)) ?? 0);
             options.didMove = newIndex !== this.layoutDoc._carousel_index;
             options.didMove && (this.layoutDoc._carousel_index = newIndex);
         }
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 094e61db9..ea6259a32 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -473,11 +473,11 @@ export class CollectionDockingView extends CollectionSubView() {
         }
         return undefined;
     }
-    public static async TakeSnapshot(doc: Doc | undefined, clone = false) {
+    public static TakeSnapshot(doc: Doc | undefined, clone = false) {
         if (!doc) return undefined;
         let json = StrCast(doc.dockingConfig);
         if (clone) {
-            const cloned = await Doc.MakeClone(doc);
+            const cloned = Doc.MakeClone(doc);
             Array.from(cloned.map.entries()).forEach(entry => {
                 json = json.replace(entry[0], entry[1][Id]);
             });
@@ -524,11 +524,11 @@ export class CollectionDockingView extends CollectionSubView() {
         this._flush = this._flush ?? UndoManager.StartBatch('tab movement');
         const dashDoc = tab.DashDoc;
         if (dashDoc && ![DocumentType.PRES].includes(dashDoc.type) && !tab.contentItem.config.props.keyValue) {
-            Doc.AddDocToList(Doc.MyHeaderBar, 'data', dashDoc, undefined, undefined, true);
+            Doc.MyHeaderBar && Doc.AddDocToList(Doc.MyHeaderBar, 'data', dashDoc, undefined, undefined, true);
             // if you close a tab that is not embedded somewhere else (an embedded Doc can be opened simultaneously in a tab), then add the tab to recently closed
             if (dashDoc.embedContainer === this.Document) dashDoc.embedContainer = undefined;
             if (!dashDoc.embedContainer) {
-                Doc.AddDocToList(Doc.MyRecentlyClosed, 'data', dashDoc, undefined, true, true);
+                Doc.MyRecentlyClosed && Doc.AddDocToList(Doc.MyRecentlyClosed, 'data', dashDoc, undefined, true, true);
                 Doc.RemoveEmbedding(dashDoc, dashDoc);
             }
         }
@@ -562,7 +562,7 @@ export class CollectionDockingView extends CollectionSubView() {
                     _layout_fitWidth: true,
                     title: `Untitled Tab ${NumCast(dashboard.$myPaneCount)}`,
                 });
-                Doc.AddDocToList(Doc.MyHeaderBar, 'data', docToAdd, undefined, undefined, true);
+                Doc.MyHeaderBar && Doc.AddDocToList(Doc.MyHeaderBar, 'data', docToAdd, undefined, undefined, true);
                 inheritParentAcls(this.Document, docToAdd, false);
                 CollectionDockingView.AddSplit(docToAdd, OpenWhereMod.none, stack);
             }
@@ -579,7 +579,7 @@ export class CollectionDockingView extends CollectionSubView() {
                     _freeform_backgroundGrid: true,
                     title: `Untitled Tab ${NumCast(dashboard.$myPaneCount)}`,
                 });
-                Doc.AddDocToList(Doc.MyHeaderBar, 'data', docToAdd, undefined, undefined, true);
+                Doc.MyHeaderBar && Doc.AddDocToList(Doc.MyHeaderBar, 'data', docToAdd, undefined, undefined, true);
                 inheritParentAcls(this.dataDoc, docToAdd, false);
                 CollectionDockingView.AddSplit(docToAdd, OpenWhereMod.none, stack);
             }
@@ -668,7 +668,5 @@ ScriptingGlobals.add(
 );
 // eslint-disable-next-line prefer-arrow-callback
 ScriptingGlobals.add(async function snapshotDashboard() {
-    const batch = UndoManager.StartBatch('snapshot');
-    await CollectionDockingView.TakeSnapshot(Doc.ActiveDashboard);
-    batch.end();
+    undoable(() => CollectionDockingView.TakeSnapshot(Doc.ActiveDashboard), 'snapshot dashboard');
 }, 'creates a snapshot copy of a dashboard');
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index 173147f64..7f639a11e 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -321,11 +321,10 @@ export class CollectionNoteTakingView extends CollectionSubView() {
         return Math.min(maxWidth - CollectionNoteTakingViewColumn.ColumnMargin, width < maxWidth ? width : maxWidth);
     };
 
-    // how to get the height of a document. Nothing special here.
     getDocHeight(d?: Doc) {
         if (!d || d.hidden) return 0;
-        const childLayoutDoc = Doc.Layout(d, this._props.childLayoutTemplate?.());
-        const childDataDoc = !d.isTemplateDoc && !d.isTemplateForField ? undefined : this._props.TemplateDataDocument;
+        const childLayoutDoc = Doc.LayoutDoc(d, this._props.childLayoutTemplate?.());
+        const childDataDoc = d.isTemplateDoc || d.isTemplateForField ? this._props.TemplateDataDocument : undefined;
         const maxHeight = (lim => (lim === 0 ? this._props.PanelWidth() : lim === -1 ? 10000 : lim))(NumCast(this.layoutDoc.childLimitHeight, -1));
         const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this._props.childLayoutFitWidth?.(d)) ? NumCast(d._width) : 0);
         const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this._props.childLayoutFitWidth?.(d)) ? NumCast(d._height) : 0);
@@ -566,7 +565,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
 
     @undoBatch
     remColumn = (value: SchemaHeaderField) => {
-        const colHdrData = Array.from(Cast(this.Document[this._props.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null));
+        const colHdrData = Array.from(Cast(this.Document[this._props.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null) ?? []);
         if (value) {
             const index = colHdrData.indexOf(value);
             index !== -1 && colHdrData.splice(index, 1);
@@ -585,7 +584,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
             }
             return undefined;
         });
-        const columnHeaders = Array.from(Cast(this.dataDoc[this.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null));
+        const columnHeaders = Array.from(Cast(this.dataDoc[this.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null) ?? []);
         const newColWidth = 1 / (this.numGroupColumns + 1);
         columnHeaders.push(new SchemaHeaderField(value, undefined, undefined, newColWidth));
         value && this.resizeColumns(columnHeaders);
@@ -642,7 +641,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
         leftHeader.setWidth(leftHeader.width + movementX / this.availableWidth);
         rightHeader.setWidth(rightHeader.width - movementX / this.availableWidth);
         const headers = Cast(this.dataDoc[this.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null);
-        headers.splice(headers.indexOf(leftHeader), 1, leftHeader[Copy]());
+        headers?.splice(headers.indexOf(leftHeader), 1, leftHeader[Copy]());
     };
 
     // renderedSections returns a list of all of the JSX elements used (columns and dividers). If the view
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 112510265..9155227dd 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -315,8 +315,8 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             e.stopPropagation?.();
             const layoutFieldKey = StrCast(textBox.fieldKey);
             const newDoc = Doc.MakeCopy(textBox.Document, true);
-            const dataField = textBox.Document[Doc.LayoutFieldKey(newDoc)];
-            newDoc['$' + Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined;
+            const dataField = textBox.Document[Doc.LayoutDataKey(newDoc)];
+            newDoc['$' + Doc.LayoutDataKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined;
             if (layoutFieldKey !== 'layout' && textBox.Document[layoutFieldKey] instanceof Doc) {
                 newDoc[layoutFieldKey] = textBox.Document[layoutFieldKey];
             }
@@ -340,7 +340,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
 
     isChildButtonContentActive = () => (this._props.childDocumentsActive?.() === false || this.Document.childDocumentsActive === false ? false : undefined);
     @observable docRefs = new ObservableMap<Doc, DocumentView>();
-    childFitWidth = (doc: Doc) => Cast(this.Document.childLayoutFitWidth, 'boolean', this._props.childLayoutFitWidth?.(doc) ?? Cast(doc.layout_fitWidth, 'boolean', null));
+    childFitWidth = (doc: Doc) => Cast(this.Document.childLayoutFitWidth, 'boolean', this._props.childLayoutFitWidth?.(doc) ?? Cast(doc.layout_fitWidth, 'boolean', null) ?? null);
     // this is what renders the document that you see on the screen
     // called in Children: this actually adds a document to our children list
     getDisplayDoc(doc: Doc, trans: () => string, count: number) {
@@ -408,7 +408,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
     });
     getDocWidth = computedFn((d?: Doc) => () => {
         if (!d) return 0;
-        const childLayoutDoc = Doc.Layout(d, this._props.childLayoutTemplate?.());
+        const childLayoutDoc = Doc.LayoutDoc(d, this._props.childLayoutTemplate?.());
         const maxWidth = this.columnWidth / this.numGroupColumns;
         if (!this.layoutDoc._columnsFill && !this.childFitWidth(childLayoutDoc)) {
             return Math.min(NumCast(d._width), maxWidth);
@@ -418,8 +418,8 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
     getDocTransition = computedFn((d?: Doc) => () => StrCast(d?.dataTransition));
     getDocHeight = computedFn((d?: Doc) => () => {
         if (!d || d.hidden) return 0;
-        const childLayoutDoc = Doc.Layout(d, this._props.childLayoutTemplate?.());
-        const childDataDoc = !d.isTemplateDoc && !d.isTemplateForField ? undefined : this._props.TemplateDataDocument;
+        const childLayoutDoc = Doc.LayoutDoc(d, this._props.childLayoutTemplate?.());
+        const childDataDoc = d.isTemplateDoc || d.isTemplateForField ? this._props.TemplateDataDocument : undefined;
         const maxHeight = (lim => (lim === 0 ? this._props.PanelWidth() : lim === -1 ? 10000 : lim))(NumCast(this.layoutDoc.childLimitHeight, -1));
         const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!this.childFitWidth(childLayoutDoc) ? NumCast(d._width) : 0);
         const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!this.childFitWidth(childLayoutDoc) ? NumCast(d._height) : 0);
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 66839ba7f..994669734 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -274,7 +274,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
                             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);
+                                return Doc.AddDocToList(container, Doc.LayoutDataKey(container), created);
                             }
                             return this._props.addDocument?.(created) || false;
                         }
@@ -316,7 +316,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
                 ref={this._headerRef}
                 style={{
                     marginTop: this._props.yMargin,
-                    width: this._props.columnWidth
+                    width: this._props.columnWidth,
                 }}>
                 {/* the default bucket (no key value) has a tooltip that describes what it is.
                     Further, it does not have a color and cannot be deleted. */}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 375c0fe53..bc7d6f897 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -3,7 +3,7 @@ import * as React from 'react';
 import * as rp from 'request-promise';
 import { ClientUtils, DashColor, returnFalse } from '../../../ClientUtils';
 import CursorField from '../../../fields/CursorField';
-import { Doc, DocListCast, GetDocFromUrl, GetHrefFromHTML, Opt, RTFIsFragment, StrListCast } from '../../../fields/Doc';
+import { Doc, DocListCast, expandedFieldName, GetDocFromUrl, GetHrefFromHTML, Opt, RTFIsFragment, StrListCast } from '../../../fields/Doc';
 import { AclPrivate, DocData } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
@@ -132,16 +132,23 @@ export function CollectionSubView<X>() {
 
         hasChildDocs = () => this.childLayoutPairs.map(pair => pair.layout);
         @computed get childLayoutPairs(): { layout: Doc; data: Doc }[] {
-            const { TemplateDataDocument } = this._props;
-            const validPairs = this.childDocs
-                .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
-                        !pair.layout?.hidden && pair.layout && (!pair.layout.proto || (pair.layout.proto instanceof Doc && GetEffectiveAcl(pair.layout.proto) !== AclPrivate))
-                )
-                .filter(pair => !this._filterFunc?.(pair.layout!));
-            return validPairs.map(({ data, layout }) => ({ data: data as Doc, layout: layout! })); // this mapping is a bit of a hack to coerce types
+            const lastEle = this.DocumentView?.();
+            if (!lastEle?.IsInvalid(this.Document)) {
+                const rootTemplate = lastEle && Doc.LayoutDoc(lastEle.rootDoc).isTemplateDoc && Doc.LayoutDoc(lastEle.rootDoc);
+                const templateFieldKey = rootTemplate &&
+                    [expandedFieldName(rootTemplate),
+                        ...this._props.docViewPath()
+                            .filter(dv => dv.Document.isTemplateForField)
+                            .map(dv => dv.Document.title),
+                    ].join('_'); // prettier-ignore
+                return this.childDocs
+                    .map(doc => Doc.GetLayoutDataDocPair(this.Document, !this._props.isAnnotationOverlay ? this._props.TemplateDataDocument : undefined, doc, templateFieldKey || ""))
+                    .filter(pair => // filter out any documents that have a proto that we don't have permissions to
+                        !pair.layout?.hidden && pair.layout && (!pair.layout.proto || (pair.layout.proto instanceof Doc && GetEffectiveAcl(pair.layout.proto) !== AclPrivate)))
+                    .filter(pair => !this._filterFunc?.(pair.layout!))
+                    .map(({ data, layout }) => ({ data: data!, layout: layout! })); // prettier-ignore
+            }
+            return [];
         }
         /**
          * This is the raw, stored list of children on a collection.  If you modify this list, the database will be updated
@@ -161,7 +168,7 @@ export function CollectionSubView<X>() {
         };
 
         collectionFilters = () => this._focusFilters ?? StrListCast(this.Document._childFilters);
-        collectionRangeDocFilters = () => this._focusRangeFilters ?? Cast(this.Document._childFiltersByRanges, listSpec('string'), []);
+        collectionRangeDocFilters = () => this._focusRangeFilters ?? StrListCast(this.Document._childFiltersByRanges);
         // child filters apply to the descendants of the documents in this collection
         childDocFilters = () => [...(this._props.childFilters?.().filter(f => ClientUtils.IsRecursiveFilter(f)) || []), ...this.collectionFilters()];
         // unrecursive filters apply to the documents in the collection, but no their children.  See Utils.noRecursionHack
@@ -178,7 +185,7 @@ export function CollectionSubView<X>() {
                 rawdocs = [this.dataField];
             } else if (Cast(this.dataField, listSpec(Doc), null)) {
                 // otherwise, if the collection data is a list, then use it.
-                rawdocs = Cast(this.dataField, listSpec(Doc), null);
+                rawdocs = DocListCast(this.dataField);
             } else if (this.dataField) {
                 // Finally, if it's not a doc or a list and the document is a template, we try to render the root doc.
                 // For example, if an image doc is rendered with a slide template, the template will try to render the data field as a collection.
@@ -206,7 +213,7 @@ export function CollectionSubView<X>() {
                 let notFiltered = d.z || Doc.IsSystem(d) || DocUtils.FilterDocs([d], this.unrecursiveDocFilters(), childFiltersByRanges, this.Document).length > 0;
                 if (notFiltered) {
                     notFiltered = (!searchDocs.length || searchDocs.includes(d)) && DocUtils.FilterDocs([d], childDocFilters, childFiltersByRanges, this.Document).length > 0;
-                    const fieldKey = Doc.LayoutFieldKey(d);
+                    const fieldKey = Doc.LayoutDataKey(d);
                     const isAnnotatableDoc = d[fieldKey] instanceof List && !(d[fieldKey] as List<Doc>)?.some(ele => !(ele instanceof Doc));
                     const docChildDocs = d[isAnnotatableDoc ? fieldKey + '_annotations' : fieldKey];
                     const sidebarDocs = isAnnotatableDoc && d[fieldKey + '_sidebar'];
@@ -219,7 +226,7 @@ export function CollectionSubView<X>() {
                                 newarray = [];
                                 // eslint-disable-next-line no-loop-func
                                 subDocs.forEach(t => {
-                                    const docFieldKey = Doc.LayoutFieldKey(t);
+                                    const docFieldKey = Doc.LayoutDataKey(t);
                                     const isSubDocAnnotatable = t[docFieldKey] instanceof List && !(t[docFieldKey] as List<Doc>)?.some(ele => !(ele instanceof Doc));
                                     notFiltered =
                                         notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && ((!childDocFilters.length && !childFiltersByRanges.length) || DocUtils.FilterDocs([t], childDocFilters, childFiltersByRanges, d).length));
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 568a08792..4348bc7dc 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -15,7 +15,7 @@ import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { FieldId } from '../../../fields/RefField';
 import { ComputedField } from '../../../fields/ScriptField';
-import { Cast, NumCast, StrCast, toList } from '../../../fields/Types';
+import { Cast, DocCast, NumCast, StrCast, toList } from '../../../fields/Types';
 import { DocServer } from '../../DocServer';
 import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
 import { Docs } from '../../documents/Documents';
@@ -145,7 +145,7 @@ export class TabMinimapView extends ObservableReactComponent<TabMinimapViewProps
                     select={emptyFunction}
                     isSelected={returnFalse}
                     dontRegisterView
-                    fieldKey={Doc.LayoutFieldKey(this._props.doc)}
+                    fieldKey={Doc.LayoutDataKey(this._props.doc)}
                     addDocument={returnFalse}
                     moveDocument={returnFalse}
                     removeDocument={returnFalse}
@@ -173,7 +173,7 @@ export class TabMinimapView extends ObservableReactComponent<TabMinimapViewProps
         );
     };
     render() {
-        return this._props.doc.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this._props.doc)) || this._props.doc?._type_collection !== CollectionViewType.Freeform ? null : (
+        return this._props.doc.layout !== CollectionView.LayoutString(Doc.LayoutDataKey(this._props.doc)) || this._props.doc?._type_collection !== CollectionViewType.Freeform ? null : (
             <div className="miniMap-hidden">
                 <Popup icon={<FontAwesomeIcon icon="globe-asia" size="lg" />} color={SnappingManager.userVariantColor} type={Type.TERT} onPointerDown={e => e.stopPropagation()} placement="top-end" popup={this.popup} />
             </div>
@@ -207,10 +207,10 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
         const docs = toList(docIn);
 
         const batch = UndoManager.StartBatch('Pin doc to pres trail');
-        const curPres = Doc.ActivePresentation ?? Doc.MakeCopy(Doc.UserDoc().emptyTrail as Doc, true);
+        const curPres = Doc.ActivePresentation ?? (DocCast(Doc.UserDoc().emptyTrail) ? Doc.MakeCopy(DocCast(Doc.UserDoc().emptyTrail)!, true) : Docs.Create.PresDocument({}));
 
         if (!Doc.ActivePresentation) {
-            Doc.AddDocToList(Doc.MyTrails, 'data', curPres);
+            Doc.MyTrails && Doc.AddDocToList(Doc.MyTrails, 'data', curPres);
             Doc.ActivePresentation = curPres;
         }
 
@@ -236,7 +236,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
             pinDoc.treeView_FieldKey = 'data'; // tree view will treat the 'data' field as the field where the hierarchical children are located instead of using the document's layout string field
             pinDoc.treeView_ExpandedView = 'data'; // in case the data doc has an expandedView set, this will mask that field and use the 'data' field when expanding the tree view
             pinDoc.treeView_HideHeaderIfTemplate = true; // this will force the document to render itself as the tree view header
-            const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}_duration`], null);
+            const duration = NumCast(doc[`${Doc.LayoutDataKey(pinDoc)}_duration`], null);
 
             if (pinProps.pinViewport) PinDocView(pinDoc, pinProps, anchorDoc ?? doc);
             if (!pinProps?.audioRange && duration !== undefined) {
@@ -566,7 +566,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
         return false;
     };
 
-    getCurrentFrame = () => NumCast(Cast(PresBox.Instance.activeItem.presentation_targetDoc, Doc, null)._currentFrame);
+    getCurrentFrame = () => NumCast(DocCast(PresBox.Instance.activeItem?.presentation_targetDoc)?._currentFrame);
     focusFunc = () => {
         if (!this.tab.header.parent._activeContentItem || this.tab.header.parent._activeContentItem !== this.tab.contentItem) {
             this.tab.header.parent.setActiveContentItem(this.tab.contentItem); // glr: Panning does not work when this is set - (this line is for trying to make a tab that is not topmost become topmost)
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 4dc937864..4beb75074 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -159,7 +159,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
         return this.Document[DocLayout];
     }
     @computed get fieldKey() {
-        return StrCast(this.Document._treeView_FieldKey, Doc.LayoutFieldKey(this.Document));
+        return StrCast(this.Document._treeView_FieldKey, Doc.LayoutDataKey(this.Document));
     }
     @computed get childDocs() {
         return this.childDocList(this.fieldKey);
@@ -186,7 +186,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
     moving: boolean = false;
     @undoBatch move = (doc: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => {
         if (this.Document !== target && addDoc !== returnFalse) {
-            const canAdd1 = (this._props.parentTreeView as TreeView).dropping || !(ComputedField.WithoutComputed(() => FieldValue(this._props.parentTreeView?.Document.data)) instanceof ComputedField);
+            const canAdd1 = (this._props.parentTreeView as TreeView).dropping || !(ComputedField.DisableCompute(() => FieldValue(this._props.parentTreeView?.Document.data)) instanceof ComputedField);
 
             // bcz: this should all be running in a Temp undo batch instead of hackily testing for returnFalse
             if (canAdd1 && this._props.removeDoc?.(doc) === true) {
@@ -431,9 +431,9 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
 
     localAdd = (docs: Doc | Doc[]): boolean => {
         const innerAdd = (doc: Doc): boolean => {
-            const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[this.fieldKey])) instanceof ComputedField;
+            const dataIsComputed = ComputedField.DisableCompute(() => FieldValue(this.dataDoc[this.fieldKey])) instanceof ComputedField;
             const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, this.fieldKey, doc);
-            dataIsComputed && Doc.SetContainer(doc, DocCast(this.Document.embedContainer));
+            dataIsComputed && DocCast(this.Document.embedContainer) && Doc.SetContainer(doc, DocCast(this.Document.embedContainer)!);
             return added;
         };
         return toList(docs).reduce((flg, doc) => flg && innerAdd(doc), true as boolean);
@@ -511,9 +511,9 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
                 const moveDoc = (docs: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => this.move(docs, target, addDoc);
                 const addDoc = (docs: Doc | Doc[], addBefore?: Doc, before?: boolean) => {
                     const innerAdd = (iDoc: Doc) => {
-                        const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[key])) instanceof ComputedField;
+                        const dataIsComputed = ComputedField.DisableCompute(() => FieldValue(this.dataDoc[key])) instanceof ComputedField;
                         const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, key, iDoc, addBefore, before, false, true);
-                        dataIsComputed && Doc.SetContainer(iDoc, DocCast(this.Document.embedContainer));
+                        dataIsComputed && DocCast(this.Document.embedContainer) && Doc.SetContainer(iDoc, DocCast(this.Document.embedContainer)!);
                         return added;
                     };
                     return toList(docs).reduce((flg, iDoc) => flg && innerAdd(iDoc), true as boolean);
@@ -633,7 +633,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
                         d.zIndex = i;
                     });
                 }
-                const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[key])) instanceof ComputedField;
+                const dataIsComputed = ComputedField.DisableCompute(() => FieldValue(this.dataDoc[key])) instanceof ComputedField;
                 const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false);
                 !dataIsComputed && added && Doc.SetContainer(doc, this.Document);
 
@@ -883,8 +883,8 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
         ];
     };
     childContextMenuItems = () => {
-        const customScripts = Cast(this.Document.childContextMenuScripts, listSpec(ScriptField), []);
-        const customFilters = Cast(this.Document.childContextMenuFilters, listSpec(ScriptField), []);
+        const customScripts = Cast(this.Document.childContextMenuScripts, listSpec(ScriptField), [])!;
+        const customFilters = Cast(this.Document.childContextMenuFilters, listSpec(ScriptField), [])!;
         const icons = StrListCast(this.Document.childContextMenuIcons);
         return StrListCast(this.Document.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label }));
     };
@@ -1296,7 +1296,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
 
                 const dentDoc = (editTitle: boolean, newParent: Doc, addAfter: Doc | undefined, parent: TreeView | CollectionTreeView | undefined) => {
                     if (parent instanceof TreeView && parent._props.treeView.fileSysMode && !newParent.isFolder) return;
-                    const fieldKey = Doc.LayoutFieldKey(newParent);
+                    const fieldKey = Doc.LayoutDataKey(newParent);
                     if (remove && fieldKey && Cast(newParent[fieldKey], listSpec(Doc)) !== undefined) {
                         remove(child);
                         DocumentView.SetSelectOnLoad(child);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 626538976..842293358 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -275,7 +275,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     onChildDoubleClickHandler = () => this._props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick);
     elementFunc = () => this._layoutElements;
     viewTransition = () => (this._panZoomTransition ? '' + this._panZoomTransition : undefined);
-    panZoomTransition = () => (this._panZoomTransition ? `transform ${this._panZoomTransition}ms` : Cast(this.layoutDoc._viewTransition, 'string', Cast(this.Document._viewTransition, 'string', null)));
+    panZoomTransition = () => (this._panZoomTransition ? `transform ${this._panZoomTransition}ms` : (Cast(this.layoutDoc._viewTransition, 'string', Cast(this.Document._viewTransition, 'string', null) ?? null) ?? ''));
     fitContentOnce = () => {
         const { cx, cy, scale } = this.contentBounds(); // prettier-ignore
         this.layoutDoc._freeform_panX = cx;
@@ -388,7 +388,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         if (options.easeFunc) this.setPresEaseFunc(options.easeFunc);
         if (this._lightboxDoc) return undefined;
         if (options.pointFocus) return this.focusOnPoint(options);
-        const anchorInCollection = DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]).includes(anchor);
+        const anchorInCollection = DocListCast(this.Document[this.fieldKey ?? Doc.LayoutDataKey(this.Document)]).includes(anchor);
         const anchorInChildViews = this.childLayoutPairs.map(pair => pair.layout).includes(anchor);
         if (!anchorInCollection && !anchorInChildViews) {
             return undefined;
@@ -1308,7 +1308,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         SnappingManager.TriggerUserPanned();
         if (this.layoutDoc._Transform || this.Document.treeView_OutlineMode === TreeViewType.outline) return;
         e.stopPropagation();
-        const docHeight = NumCast(this.Document[Doc.LayoutFieldKey(this.Document) + '_nativeHeight'], this.nativeHeight);
+        const docHeight = NumCast(this.Document[Doc.LayoutDataKey(this.Document) + '_nativeHeight'], this.nativeHeight);
         const scrollable = this.isAnnotationOverlay && NumCast(this.layoutDoc[this.scaleFieldKey], 1) === 1 && docHeight > this._props.PanelHeight() / this.nativeDimScaling + 1e-4;
         switch (
             !e.ctrlKey && !e.shiftKey && !e.metaKey && !e.altKey ?//
@@ -1489,12 +1489,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
      */
     createTextDocCopy = undoable((textBox: FormattedTextBox, below: boolean) => {
         const textDoc = DocCast(textBox.Document);
-        const newDoc = Doc.MakeCopy(textDoc, true);
-        newDoc['$' + Doc.LayoutFieldKey(newDoc)] = undefined; // the copy should not copy the text contents of it source, just the render style
-        newDoc.x = NumCast(textDoc.x) + (below ? 0 : NumCast(textDoc._width) + 10);
-        newDoc.y = NumCast(textDoc.y) + (below ? NumCast(textDoc._height) + 10 : 0);
-        DocumentView.SetSelectOnLoad(newDoc);
-        return this.addDocument?.(newDoc);
+        if (textDoc) {
+            const newDoc = Doc.MakeCopy(textDoc, true);
+            newDoc['$' + Doc.LayoutDataKey(newDoc)] = undefined; // the copy should not copy the text contents of it source, just the render style
+            newDoc.x = NumCast(textDoc.x) + (below ? 0 : NumCast(textDoc._width) + 10);
+            newDoc.y = NumCast(textDoc.y) + (below ? NumCast(textDoc._height) + 10 : 0);
+            DocumentView.SetSelectOnLoad(newDoc);
+            return this.addDocument?.(newDoc);
+        }
+        return false;
     }, 'copied text note');
 
     onKey = (e: KeyboardEvent, textBox: FormattedTextBox) => {
@@ -1741,7 +1744,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
         if (addAsAnnotation) {
             if (Cast(this.dataDoc[this._props.fieldKey + '_annotations'], listSpec(Doc), null) !== undefined) {
-                Cast(this.dataDoc[this._props.fieldKey + '_annotations'], listSpec(Doc), []).push(anchor);
+                Cast(this.dataDoc[this._props.fieldKey + '_annotations'], listSpec(Doc), [])?.push(anchor);
             } else {
                 this.dataDoc[this._props.fieldKey + '_annotations'] = new List<Doc>([anchor]);
             }
@@ -1985,7 +1988,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             optionItems.push({
                 description: 'Use Background Color as Default',
                 event: () => {
-                    Cast(Doc.UserDoc().emptyCollection, Doc, null).backgroundColor = StrCast(this.layoutDoc.backgroundColor);
+                    DocCast(Doc.UserDoc().emptyCollection) && (DocCast(Doc.UserDoc().emptyCollection)!.backgroundColor = StrCast(this.layoutDoc.backgroundColor));
                 },
                 icon: 'palette',
             });
@@ -2085,7 +2088,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     lightboxPanelHeight = () => Math.max(0, this._props.PanelHeight() - 30);
     lightboxScreenToLocal = () => this.ScreenToLocalBoxXf().translate(-15, -15);
     onPassiveWheel = (e: WheelEvent) => {
-        const docHeight = NumCast(this.Document[Doc.LayoutFieldKey(this.Document) + '_nativeHeight'], this.nativeHeight);
+        const docHeight = NumCast(this.Document[Doc.LayoutDataKey(this.Document) + '_nativeHeight'], this.nativeHeight);
         const scrollable = NumCast(this.layoutDoc[this.scaleFieldKey], 1) === 1 && docHeight > this._props.PanelHeight() / this.nativeDimScaling;
         this._props.isSelected() && !scrollable && e.preventDefault();
     };
@@ -2108,7 +2111,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             </div>
         );
     }
-    transitionFunc = () => (this._panZoomTransition ? `transform ${this._panZoomTransition}ms ${this._presEaseFunc}` : Cast(this.layoutDoc._viewTransition, 'string', Cast(this.Document._viewTransition, 'string', null)));
+    transitionFunc = () => (this._panZoomTransition ? `transform ${this._panZoomTransition}ms ${this._presEaseFunc}` : (Cast(this.layoutDoc._viewTransition, 'string', Cast(this.Document._viewTransition, 'string', null) ?? null) ?? ''));
     get pannableContents() {
         this.incrementalRender(); // needs to happen synchronously or freshly typed text documents will flash and miss their first characters
         return (
@@ -2385,7 +2388,7 @@ ScriptingGlobals.add(function datavizFromSchema() {
         const keys = Cast(view.layoutDoc.schema_columnKeys, listSpec('string'))?.filter(key => key !== 'text');
         if (!keys) return;
 
-        const children = DocListCast(view.Document[Doc.LayoutFieldKey(view.Document)]);
+        const children = DocListCast(view.Document[Doc.LayoutDataKey(view.Document)]);
         const csvRows = [];
         csvRows.push(keys.join(','));
         for (let i = 0; i < children.length; i++) {
diff --git a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
index c7aa410c6..72485aa86 100644
--- a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
+++ b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
@@ -187,7 +187,7 @@ export class UniqueFaceBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             ele?.addEventListener('wheel', this.onPassiveWheel, { passive: false });
                         })}>
                         {FaceRecognitionHandler.UniqueFaceImages(this.Document).map((doc, i) => {
-                            const [name, type] = ImageCastToNameType(doc[Doc.LayoutFieldKey(doc)]) ?? ['-missing-', '.png'];
+                            const [name, type] = ImageCastToNameType(doc[Doc.LayoutDataKey(doc)]) ?? ['-missing-', '.png'];
                             return (
                                 <div
                                     className="image-wrapper"
diff --git a/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx b/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx
index 7c8ccb92d..ff9fb14e7 100644
--- a/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx
+++ b/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx
@@ -165,7 +165,7 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
         const imageInfos = this._selectedImages.map(async doc => {
             if (!doc.$tags_chat) {
-                const url = ImageCastWithSuffix(doc[Doc.LayoutFieldKey(doc)], '_o') ?? '';
+                const url = ImageCastWithSuffix(doc[Doc.LayoutDataKey(doc)], '_o') ?? '';
                 return imageUrlToBase64(url).then(hrefBase64 =>
                 !hrefBase64 ? undefined : 
                     gptImageLabel(hrefBase64,'Give three labels to describe this image.').then(labels => 
@@ -310,7 +310,7 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 {this._displayImageInformation ? (
                     <div className="image-information-list">
                         {this._selectedImages.map(doc => {
-                            const [name, type] = ImageCastToNameType(doc[Doc.LayoutFieldKey(doc)]);
+                            const [name, type] = ImageCastToNameType(doc[Doc.LayoutDataKey(doc)]);
                             return (
                                 <div className="image-information" style={{ borderColor: SettingsManager.userColor }} key={Utils.GenerateGuid()}>
                                     <img
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 5fbf72f39..3cc7c0f2d 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -8,7 +8,7 @@ import { AclAdmin, AclAugment, AclEdit, DocData } from '../../../../fields/DocSy
 import { Id } from '../../../../fields/FieldSymbols';
 import { InkData, InkTool } from '../../../../fields/InkField';
 import { List } from '../../../../fields/List';
-import { Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
+import { Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { ImageField } from '../../../../fields/URLField';
 import { GetEffectiveAcl } from '../../../../fields/util';
 import { DocUtils } from '../../../documents/DocUtils';
@@ -376,7 +376,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
                   doc.$title = makeGroup ? 'grouping' : 'nested freeform';
                   doc._freeform_panX = doc._freeform_panY = 0;
                   return doc;
-              })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
+              })(DocCast(Doc.UserDoc().emptyCollection) ? Doc.MakeCopy(DocCast(Doc.UserDoc().emptyCollection)!, true) : Docs.Create.FreeformDocument([], {}));
         newCollection.isSystem = undefined;
         newCollection._width = bounds.width || 1; // if width/height are unset/0, then groups won't autoexpand to contain their children
         newCollection._height = bounds.height || 1;
@@ -437,7 +437,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
      */
     @undoBatch
     classifyImages = async () => {
-        const groupButton = DocListCast(Doc.MyLeftSidebarMenu.data).find(d => d.target === Doc.MyImageGrouper);
+        const groupButton = DocListCast(Doc.MyLeftSidebarMenu?.data).find(d => d.target === Doc.MyImageGrouper);
         if (groupButton) {
             this._selectedDocs = this.marqueeSelect(false, DocumentType.IMG);
             ImageLabelBoxData.Instance.setData(this._selectedDocs);
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index 80116dd2f..8d3947653 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -102,7 +102,7 @@ export class CollectionLinearView extends CollectionSubView() {
 
     getLinkUI = () =>
         !DocumentLinksButton.StartLink ? null : (
-            <span className="bottomPopup-background" style={{ pointerEvents: 'all' }} onPointerDown={e => e.stopPropagation()}>
+            <span key="-link-ui-" className="bottomPopup-background" style={{ pointerEvents: 'all' }} onPointerDown={e => e.stopPropagation()}>
                 <span className="bottomPopup-text">
                     Creating link from:{' '}
                     <b>
@@ -126,7 +126,7 @@ export class CollectionLinearView extends CollectionSubView() {
         );
     getCurrentlyPlayingUI = () =>
         !DocumentView.CurrentlyPlaying?.length ? null : (
-            <span className="bottomPopup-background">
+            <span key="-currently-playing-" className="bottomPopup-background">
                 <span className="bottomPopup-text">
                     Currently playing:
                     {DocumentView.CurrentlyPlaying.map((clip, i) => (
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 5803acca0..82ca96839 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -1193,7 +1193,7 @@ export class CollectionSchemaView extends CollectionSubView() {
             let notFiltered = d.z || Doc.IsSystem(d) || DocUtils.FilterDocs([d], this.unrecursiveDocFilters(), childFiltersByRanges, this.Document).length > 0;
             if (notFiltered) {
                 notFiltered = (!searchDocs.length || searchDocs.includes(d)) && DocUtils.FilterDocs([d], childDocFilters, childFiltersByRanges, this.Document).length > 0;
-                const fieldKey = Doc.LayoutFieldKey(d);
+                const fieldKey = Doc.LayoutDataKey(d);
                 const isAnnotatableDoc = d[fieldKey] instanceof List && !(d[fieldKey] as List<Doc>)?.some(ele => !(ele instanceof Doc));
                 const docChildDocs = d[isAnnotatableDoc ? fieldKey + '_annotations' : fieldKey];
                 const sidebarDocs = isAnnotatableDoc && d[fieldKey + '_sidebar'];
@@ -1206,7 +1206,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                             newarray = [];
                             // eslint-disable-next-line no-loop-func
                             subDocs.forEach(t => {
-                                const docFieldKey = Doc.LayoutFieldKey(t);
+                                const docFieldKey = Doc.LayoutDataKey(t);
                                 const isSubDocAnnotatable = t[docFieldKey] instanceof List && !(t[docFieldKey] as List<Doc>)?.some(ele => !(ele instanceof Doc));
                                 notFiltered = notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && ((!childDocFilters.length && !childFiltersByRanges.length) || DocUtils.FilterDocs([t], childDocFilters, childFiltersByRanges, d).length));
                                 DocListCast(t[isSubDocAnnotatable ? docFieldKey + '_annotations' : docFieldKey]).forEach(newdoc => newarray.push(newdoc));
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 173984dc7..8e1edc1ee 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -326,7 +326,7 @@ export class SchemaImageCell extends ObservableReactComponent<SchemaTableCellPro
         const field = Cast(this._props.Doc[this._props.fieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc
         const alts = DocListCast(this._props.Doc[this._props.fieldKey + '_alternates']); // retrieve alternate documents that may be rendered as alternate images
         const altpaths = alts
-            .map(doc => Cast(doc[Doc.LayoutFieldKey(doc)], ImageField, null)?.url)
+            .map(doc => Cast(doc[Doc.LayoutDataKey(doc)], ImageField, null)?.url)
             .filter(url => url)
             .map(url => this.choosePath(url)); // access the primary layout data of the alternate documents
         const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths;
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index 32afb3d3d..f4beb1004 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -76,7 +76,7 @@ ScriptingGlobals.add(function setBorderColor(color?: string, checkResult?: boole
             const selView = selectedViews.lastElement();
             const layoutFrameNumber = Cast(selView.containerViewPath?.().lastElement()?.Document?._currentFrame, 'number'); // frame number that container is at which determines layout frame values
             const contentFrameNumber = Cast(selView.Document?._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
-            return CollectionFreeFormDocumentView.getStringValues(selView?.Document, contentFrameNumber)[fieldKey] || defaultBorder();
+            return (contentFrameNumber !== undefined && CollectionFreeFormDocumentView.getStringValues(selView?.Document, contentFrameNumber)[fieldKey]) || defaultBorder();
         }
         setDefaultBorder(color ?? 'transparent');
         selectedViews.forEach(dv => {
@@ -87,7 +87,7 @@ ScriptingGlobals.add(function setBorderColor(color?: string, checkResult?: boole
                 obj[fieldKey] = color;
                 CollectionFreeFormDocumentView.setStringValues(contentFrameNumber, dv.Document, obj);
             } else {
-                const dataKey = Doc.LayoutFieldKey(dv.Document);
+                const dataKey = Doc.LayoutDataKey(dv.Document);
                 const alternate = (dv.layoutDoc[dataKey + '_usePath'] ? '_' + dv.layoutDoc[dataKey + '_usePath'] : '').replace(':hover', '');
                 dv.layoutDoc[fieldKey + alternate] = undefined;
                 dv.dataDoc[fieldKey + alternate] = color;
@@ -133,7 +133,7 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b
                 CollectionFreeFormDocumentView.setStringValues(contentFrameNumber, dv.Document, obj);
             } else {
                 const colorDoc = dv.isTemplateForField ? dv.layoutDoc : dv.dataDoc; // assigning to a template's compoment field should not assign to the data doc
-                const dataKey = Doc.LayoutFieldKey(colorDoc);
+                const dataKey = Doc.LayoutDataKey(colorDoc);
                 const alternate = (dv.layoutDoc[dataKey + '_usePath'] ? '_' + dv.layoutDoc[dataKey + '_usePath'] : '').replace(':hover', '');
                 colorDoc[fieldKey + alternate] = color;
             }
@@ -158,7 +158,7 @@ ScriptingGlobals.add(function setDefaultTemplate(checkResult?: boolean) {
 // eslint-disable-next-line prefer-arrow-callback
 ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boolean) {
     if (checkResult) {
-        return DocumentView.Selected().length ? StrCast(DocumentView.SelectedDocs().lastElement().layout_headingColor) : Doc.SharingDoc().headingColor;
+        return DocumentView.Selected().length ? StrCast(DocumentView.SelectedDocs().lastElement().layout_headingColor) : Doc.SharingDoc()?.headingColor;
     }
     if (DocumentView.Selected().length) {
         DocumentView.SelectedDocs().forEach(doc => {
@@ -166,8 +166,11 @@ ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boole
             doc.layout_showTitle = color === 'transparent' ? undefined : StrCast(doc.layout_showTitle, 'title');
         });
     } else {
-        Doc.SharingDoc().headingColor = undefined;
-        Doc.GetProto(Doc.SharingDoc()).headingColor = color === 'transparent' ? undefined : color;
+        const sharing = Doc.SharingDoc();
+        if (sharing) {
+            sharing.headingColor = undefined;
+            Doc.GetProto(sharing).headingColor = color === 'transparent' ? undefined : color;
+        }
         Doc.UserDoc().layout_showTitle = color === 'transparent' ? undefined : StrCast(Doc.UserDoc().layout_showTitle, 'title');
     }
     return undefined;
@@ -234,30 +237,30 @@ ScriptingGlobals.add(function showFreeform(
             setDoc: (doc: Doc) => { doc._freeform_useClusters = !doc._freeform_useClusters; },
         }],
         ['time', {
-            checkResult: (doc: Doc) => StrCast(doc?.[Doc.LayoutFieldKey(doc)+"_sort"]) === "time",
-            setDoc: (doc: Doc, dv: DocumentView) => { doc[Doc.LayoutFieldKey(doc)+"_sort"] === "time" ? doc[Doc.LayoutFieldKey(doc)+"_sort"] = '' : doc[Doc.LayoutFieldKey(doc)+"_sort"] = docSortings.Time}, // prettier-ignore
+            checkResult: (doc: Doc) => StrCast(doc?.[Doc.LayoutDataKey(doc)+"_sort"]) === "time",
+            setDoc: (doc: Doc, dv: DocumentView) => { doc[Doc.LayoutDataKey(doc)+"_sort"] === "time" ? doc[Doc.LayoutDataKey(doc)+"_sort"] = '' : doc[Doc.LayoutDataKey(doc)+"_sort"] = docSortings.Time}, // prettier-ignore
         }],
         ['docType', {
-            checkResult: (doc: Doc) => StrCast(doc?.[Doc.LayoutFieldKey(doc)+"_sort"]) === "type",
-            setDoc: (doc: Doc, dv: DocumentView) => { doc[Doc.LayoutFieldKey(doc)+"_sort"] === "type" ? doc[Doc.LayoutFieldKey(doc)+"_sort"] = '' : doc[Doc.LayoutFieldKey(doc)+"_sort"] = docSortings.Type}, // prettier-ignore
+            checkResult: (doc: Doc) => StrCast(doc?.[Doc.LayoutDataKey(doc)+"_sort"]) === "type",
+            setDoc: (doc: Doc, dv: DocumentView) => { doc[Doc.LayoutDataKey(doc)+"_sort"] === "type" ? doc[Doc.LayoutDataKey(doc)+"_sort"] = '' : doc[Doc.LayoutDataKey(doc)+"_sort"] = docSortings.Type}, // prettier-ignore
         }],
         ['color', {
-            checkResult: (doc: Doc) => StrCast(doc?.[Doc.LayoutFieldKey(doc)+"_sort"]) === "color",
-            setDoc: (doc: Doc, dv: DocumentView) => { doc?.[Doc.LayoutFieldKey(doc)+"_sort"] === "color" ? doc[Doc.LayoutFieldKey(doc)+"_sort"] = '' : doc[Doc.LayoutFieldKey(doc)+"_sort"] = docSortings.Color}, // prettier-ignore
+            checkResult: (doc: Doc) => StrCast(doc?.[Doc.LayoutDataKey(doc)+"_sort"]) === "color",
+            setDoc: (doc: Doc, dv: DocumentView) => { doc?.[Doc.LayoutDataKey(doc)+"_sort"] === "color" ? doc[Doc.LayoutDataKey(doc)+"_sort"] = '' : doc[Doc.LayoutDataKey(doc)+"_sort"] = docSortings.Color}, // prettier-ignore
         }],
         ['tag', {
-            checkResult: (doc: Doc) => StrCast(doc?.[Doc.LayoutFieldKey(doc)+"_sort"]) === "tag",
-            setDoc: (doc: Doc, dv: DocumentView) => { doc[Doc.LayoutFieldKey(doc)+"_sort"] === "tag" ? doc[Doc.LayoutFieldKey(doc)+"_sort"] = '' : doc[Doc.LayoutFieldKey(doc)+"_sort"] = docSortings.Tag}, // prettier-ignore
+            checkResult: (doc: Doc) => StrCast(doc?.[Doc.LayoutDataKey(doc)+"_sort"]) === "tag",
+            setDoc: (doc: Doc, dv: DocumentView) => { doc[Doc.LayoutDataKey(doc)+"_sort"] === "tag" ? doc[Doc.LayoutDataKey(doc)+"_sort"] = '' : doc[Doc.LayoutDataKey(doc)+"_sort"] = docSortings.Tag}, // prettier-ignore
         }],
         ['reverse', {
-            checkResult: (doc: Doc) => BoolCast(doc?.[Doc.LayoutFieldKey(doc)+"_sort_reverse"]),
-            setDoc: (doc: Doc, dv: DocumentView) => { doc[Doc.LayoutFieldKey(doc)+"_sort_reverse"] = !doc[Doc.LayoutFieldKey(doc)+"_sort_reverse"]; },
+            checkResult: (doc: Doc) => BoolCast(doc?.[Doc.LayoutDataKey(doc)+"_sort_reverse"]),
+            setDoc: (doc: Doc, dv: DocumentView) => { doc[Doc.LayoutDataKey(doc)+"_sort_reverse"] = !doc[Doc.LayoutDataKey(doc)+"_sort_reverse"]; },
         }],
         ['toggle-chat', {
             checkResult: (doc: Doc) => SnappingManager.ChatVisible,
             setDoc: (doc: Doc, dv: DocumentView) => {
                 if (SnappingManager.ChatVisible){
-                    doc[Doc.LayoutFieldKey(doc)+"_sort"] = '';
+                    doc[Doc.LayoutDataKey(doc)+"_sort"] = '';
                     SnappingManager.SetChatVisible(false);
                 } else {
                     SnappingManager.SetChatVisible(true);
diff --git a/src/client/views/newlightbox/NewLightboxView.tsx b/src/client/views/newlightbox/NewLightboxView.tsx
index b060fc0b6..87cef01d2 100644
--- a/src/client/views/newlightbox/NewLightboxView.tsx
+++ b/src/client/views/newlightbox/NewLightboxView.tsx
@@ -77,7 +77,7 @@ export class NewLightboxView extends React.Component<LightboxViewProps> {
             NewLightboxView.SetNewLightboxDoc(
                 doc,
                 undefined,
-                [...DocListCast(doc[Doc.LayoutFieldKey(doc)]), ...DocListCast(doc[Doc.LayoutFieldKey(doc) + '_annotations']).filter(anno => anno.annotationOn !== doc), ...(NewLightboxView._future ?? [])].sort(
+                [...DocListCast(doc[Doc.LayoutDataKey(doc)]), ...DocListCast(doc[Doc.LayoutDataKey(doc) + '_annotations']).filter(anno => anno.annotationOn !== doc), ...(NewLightboxView._future ?? [])].sort(
                     (a: Doc, b: Doc) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)
                 ),
                 layoutTemplate
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index e0d59cc9d..d4c512342 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -396,7 +396,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
             returnFalse,
             action(() => {
                 const newDoc = DocUtils.GetNewTextDoc('', NumCast(this.Document.x), NumCast(this.Document.y) + NumCast(this.layoutDoc._height) + 10, NumCast(this.layoutDoc._width), 2 * NumCast(this.layoutDoc._height));
-                const textField = Doc.LayoutFieldKey(newDoc);
+                const textField = Doc.LayoutDataKey(newDoc);
                 const newDocData = newDoc[DocData];
                 newDocData[`${textField}_recordingSource`] = this.dataDoc;
                 newDocData[`${textField}_recordingStart`] = ComputedField.MakeFunction(`this.${textField}_recordingSource.${this.fieldKey}_recordingStart`);
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 53a3d3631..940c4cb99 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -71,7 +71,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
         { key: 'freeform_panY' },
     ]; // fields that are configured to be animatable using animation frames
     public static animStringFields = ['backgroundColor', 'borderColor', 'color', 'fillColor']; // fields that are configured to be animatable using animation frames
-    public static animDataFields = (doc: Doc) => (Doc.LayoutFieldKey(doc) ? [Doc.LayoutFieldKey(doc)] : []); // fields that are configured to be animatable using animation frames
+    public static animDataFields = (doc: Doc) => (Doc.LayoutDataKey(doc) ? [Doc.LayoutDataKey(doc)] : []); // fields that are configured to be animatable using animation frames
     public static from(dv?: DocumentView): CollectionFreeFormDocumentView | undefined {
         return dv?._props.reactParent instanceof CollectionFreeFormDocumentView ? dv._props.reactParent : undefined;
     }
@@ -187,7 +187,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     }
     public static gotoKeyFrame(doc: Doc, newFrame: number) {
         if (doc) {
-            const childDocs = DocListCast(doc[Doc.LayoutFieldKey(doc)]);
+            const childDocs = DocListCast(doc[Doc.LayoutDataKey(doc)]);
             const currentFrame = Cast(doc._currentFrame, 'number', null);
             if (currentFrame === undefined) {
                 doc._currentFrame = 0;
diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
index 117eb05f8..c80e8ebc1 100644
--- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx
+++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
@@ -352,7 +352,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                 const getFrom = DocCast(this.layoutDoc.dataViz_asSchema);
                 if (!getFrom?.schema_columnKeys) return undefined;
                 const keys = StrListCast(getFrom?.schema_columnKeys).filter(key => key !== 'text');
-                const children = DocListCast(getFrom?.[Doc.LayoutFieldKey(getFrom)]);
+                const children = DocListCast(getFrom?.[Doc.LayoutDataKey(getFrom)]);
                 const current: { [key: string]: string }[] = [];
                 children
                     .filter(child => child)
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 6f004bed3..504c1491e 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -188,8 +188,8 @@ export class DocumentContentsView extends ObservableReactComponent<DocumentConte
             this._props.LayoutTemplate?.() ||
             (this._props.LayoutTemplateString && this._props.Document) ||
             (this._props.layoutFieldKey && StrCast(this._props.Document[this._props.layoutFieldKey]) && this._props.Document) ||
-            Doc.Layout(this._props.Document, DocCast(this._props.Document[this._props.layoutFieldKey]));
-        return Doc.expandTemplateLayout(template, this._props.Document);
+            Doc.LayoutDoc(this._props.Document, DocCast(this._props.Document[this._props.layoutFieldKey]));
+        return Doc.expandTemplateLayout(template, this._props.Document, this._props.layoutFieldKey);
     }
 
     CreateBindings(onClick: Opt<ScriptField>, onInput: Opt<ScriptField>): JsxBindings {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index d88d8c44d..521934152 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -169,9 +169,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
     }
 
     componentDidMount() {
-        runInAction(() => {
-            this._mounted = true;
-        });
+        runInAction(() => (this._mounted = true));
         this.setupHandlers();
         this._disposers.contentActive = reaction(
             () =>
@@ -183,16 +181,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
                     : Doc.ActiveTool !== InkTool.None || SnappingManager.CanEmbed || this.rootSelected() || this.Document.forceActive || this._componentView?.isAnyChildContentActive?.() || this._props.isContentActive()
                       ? true
                       : undefined,
-            active => {
-                this._isContentActive = active;
-            },
+            active => (this._isContentActive = active),
             { fireImmediately: true }
         );
         this._disposers.pointerevents = reaction(
             () => this.style(this.Document, StyleProp.PointerEvents) as Property.PointerEvents | undefined,
-            pointerevents => {
-                this._pointerEvents = pointerevents;
-            },
+            pointerevents => (this._pointerEvents = pointerevents),
             { fireImmediately: true }
         );
     }
@@ -305,7 +299,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
                 };
                 const clickFunc = this.onClickFunc?.()?.script ? () => (this.onClickFunc?.()?.script.run(scriptProps, console.log).result as Opt<{ select: boolean }>)?.select && this._props.select(false) : undefined;
                 if (!clickFunc) {
-                    // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTemplateForField implies we're clicking on part of a template instance and we want to select the whole template, not the part
+                    // onDragStart implies a button doc that we don't want to select when clicking.
                     if (this.layoutDoc.onDragStart && !(e.ctrlKey || e.button > 0)) stopPropagate = false;
                     preventDefault = false;
                 }
@@ -502,7 +496,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
         const items = this._props.styleProvider?.(this.Document, this._props, StyleProp.ContextMenuItems) as ContextMenuProps[];
         items?.forEach(item => ContextMenu.Instance.addItem(item));
 
-        const customScripts = Cast(this.Document.contextMenuScripts, listSpec(ScriptField), []);
+        const customScripts = Cast(this.Document.contextMenuScripts, listSpec(ScriptField), [])!;
         StrListCast(this.Document.contextMenuLabels).forEach((label, i) =>
             cm.addItem({ description: label, event: () => customScripts[i]?.script.run({ documentView: this, this: this.Document, scriptContext: this._props.scriptContext }), icon: 'sticky-note' })
         );
@@ -536,9 +530,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
                 zorderItems.push({
                     description: !this.layoutDoc._keepZDragged ? 'Keep ZIndex when dragged' : 'Allow ZIndex to change when dragged',
                     event: undoable(
-                        action(() => {
-                            this.layoutDoc._keepZWhenDragged = !this.layoutDoc._keepZWhenDragged;
-                        }),
+                        action(() => (this.layoutDoc._keepZWhenDragged = !this.layoutDoc._keepZWhenDragged)),
                         'set zIndex drag'
                     ),
                     icon: 'hand-point-up',
@@ -663,7 +655,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
             .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
+    setContentView = action((view: ViewBoxInterface<FieldViewProps>) => (this._componentView = view));
     isContentActive = (): boolean | undefined => this._isContentActive;
     childFilters = () => [...this._props.childFilters(), ...StrListCast(this.layoutDoc.childFilters)];
 
@@ -763,6 +755,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);
+    _contentsRef = React.createRef<DocumentContentsView>();
     @computed get viewBoxContents() {
         TraceMobx();
         const isInk = this.layoutDoc._layout_isSvg && !this._props.LayoutTemplateString;
@@ -778,6 +771,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
                     }}>
                     <DocumentContentsView
                         {...this._props}
+                        ref={this._contentsRef}
                         layoutFieldKey={StrCast(this.Document.layout_fieldKey, 'layout')}
                         pointerEvents={this.contentPointerEvents}
                         setContentViewBox={this.setContentView}
@@ -812,8 +806,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
     captionStyleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string) => this._props?.styleProvider?.(doc, props, property + ':caption');
     fieldsDropdown = (placeholder: string) => (
         <div
-            ref={r => { r && runInAction(() => (this._titleDropDownInnerWidth = DivWidth(r)));}} // prettier-ignore
-            onPointerDown={action(() => { this._changingTitleField = true; })} // prettier-ignore
+            ref={action((r:HTMLDivElement|null) => r && (this._titleDropDownInnerWidth = DivWidth(r)))} // prettier-ignore
+            onPointerDown={action(() => (this._changingTitleField = true))}
             style={{ width: 'max-content', background: SnappingManager.userBackgroundColor, color: SnappingManager.userColor, transformOrigin: 'left', transform: `scale(${this.titleHeight / 30 /* height of Dropdown */})` }}>
             <FieldsDropdown
                 Doc={this.Document}
@@ -826,7 +820,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
                     }
                     this._changingTitleField = false;
                 })}
-                menuClose={action(() => { this._changingTitleField = false; })} // prettier-ignore
+                menuClose={action(() => (this._changingTitleField = false))}
             />
         </div>
     );
@@ -842,7 +836,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
         const background = StrCast(
             this.layoutDoc.layout_headingColor,
             // StrCast(SharingManager.Instance.users.find(u => u.user.email === this.dataDoc.author)?.sharingDoc.headingColor,
-            StrCast(Doc.SharingDoc().headingColor, SnappingManager.userBackgroundColor)
+            StrCast(Doc.SharingDoc()?.headingColor, SnappingManager.userBackgroundColor)
             // )
         );
         const dropdownWidth = this._titleRef.current?._editing || this._changingTitleField ? Math.max(10, (this._titleDropDownInnerWidth * this.titleHeight) / 30) : 0;
@@ -1028,7 +1022,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
         root: Doc
     ) {
         const effectDirection = (presEffectDoc?.presentation_effectDirection ?? presEffectDoc?.followLinkAnimDirection) as PresEffectDirection;
-        const duration = Cast(presEffectDoc?.presentation_transition, 'number', Cast(presEffectDoc?.followLinkTransitionTime, 'number', null));
+        const duration = Cast(presEffectDoc?.presentation_transition, 'number', Cast(presEffectDoc?.followLinkTransitionTime, 'number', null) ?? null);
         const effectProps = {
             left: effectDirection === PresEffectDirection.Left,
             right: effectDirection === PresEffectDirection.Right,
@@ -1143,7 +1137,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
     public static GetDocImage(doc?: Doc) {
         return DocumentView.getDocumentView(doc)
             ?.ComponentView?.updateIcon?.()
-            .then(() => ImageCast(doc!.icon, ImageCast(doc![Doc.LayoutFieldKey(doc!)])));
+            .then(() => ImageCast(doc!.icon, ImageCast(doc![Doc.LayoutDataKey(doc!)])));
     }
 
     public get displayName() {  return 'DocumentView(' + (this.Document?.title??"") + ')';  } // prettier-ignore
@@ -1181,6 +1175,24 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
     @observable public TagPanelHeight = 0;
     @observable public TagPanelEditing = false;
 
+    /**
+     * Tests whether the component Doc being rendered matches the Doc that this view thinks its rendering.
+     * When switching the layout_fieldKey, component views may react before the internal DocumentView has re-rendered.
+     * If this happens, the component view may try to write invalid data, such as when expanding a template (which
+     * depends on the DocumentView being in synch with the subcomponents).
+     * @param renderDoc sub-component Doc being rendered
+     * @returns boolean whether sub-component Doc is in synch with the layoutDoc that this view thinks its rendering
+     */
+    IsInvalid = (renderDoc?: Doc): boolean => {
+        const docContents = this._docViewInternal?._contentsRef.current;
+        return !(
+            (!renderDoc ||
+                (docContents?.layoutDoc === renderDoc && //
+                    !this.docViewPath().some(dv => dv.IsInvalid()))) &&
+            docContents?._props.layoutFieldKey === this.Document.layout_fieldKey
+        );
+    };
+
     @computed get showTags() {
         return this.Document._layout_showTags || this._props.showTags;
     }
@@ -1250,7 +1262,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
         !BoolCast(this.Document.dontRegisterView, this._props.dontRegisterView) && DocumentView.removeView(this);
     }
 
-    public set IsSelected(val)  { runInAction(() => { this._selected = val; }); } // prettier-ignore
+    public set IsSelected(val)  { runInAction(() => (this._selected = val)) } // prettier-ignore
     public get IsSelected()     { return this._selected; } // prettier-ignore
     public get IsContentActive(){ return this._docViewInternal?.isContentActive(); } // prettier-ignore
     public get topMost()        { return this._props.renderDepth === 0; } // prettier-ignore
@@ -1262,7 +1274,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
     public get HasAIEditor()    { return !!this._docViewInternal?._componentView?.componentAIView?.(); } // prettier-ignore
 
     get LayoutFieldKey() {
-        return Doc.LayoutFieldKey(this.Document, this._props.LayoutTemplateString);
+        return Doc.LayoutDataKey(this.Document, this._props.LayoutTemplateString);
     }
 
     @computed get layout_fitWidth() {
@@ -1316,10 +1328,10 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
 
     public toggleNativeDimensions = () => this._docViewInternal && this.Document.type !== DocumentType.INK && Doc.toggleNativeDimensions(this.layoutDoc, this.NativeDimScaling() ?? 1, this._props.PanelWidth(), this._props.PanelHeight());
 
-    public iconify(finished?: () => void, animateTime?: number) {
+    public iconify = action((finished?: () => void, animateTime?: number) => {
         this.ComponentView?.updateIcon?.();
         const animTime = this._docViewInternal?.animateScaleTime();
-        runInAction(() => { this._docViewInternal && animateTime !== undefined && (this._docViewInternal._animateScaleTime = animateTime); }); // prettier-ignore
+        this._docViewInternal && animateTime !== undefined && (this._docViewInternal._animateScaleTime = animateTime);
         const finalFinished = action(() => {
             finished?.();
             this._docViewInternal && (this._docViewInternal._animateScaleTime = animTime);
@@ -1329,12 +1341,12 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
             this.switchViews(true, 'icon', finalFinished);
             if (layoutFieldKey && layoutFieldKey !== 'layout' && layoutFieldKey !== 'layout_icon') this.Document.deiconifyLayout = layoutFieldKey.replace('layout_', '');
         } else {
-            const deiconifyLayout = Cast(this.Document.deiconifyLayout, 'string', null);
+            const deiconifyLayout = StrCast(this.Document.deiconifyLayout);
             this.switchViews(!!deiconifyLayout, deiconifyLayout, finalFinished, true);
             this.Document.deiconifyLayout = undefined;
             this._props.bringToFront?.(this.Document);
         }
-    }
+    });
 
     public playAnnotation = () => {
         const audioAnnoState = this.Document._audioAnnoState ?? AudioAnnoState.stopped;
@@ -1349,7 +1361,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
                         autoplay: true,
                         loop: false,
                         volume: 0.5,
-                        onend: action(() => { this.Document._audioAnnoState = AudioAnnoState.stopped; }), // prettier-ignore
+                        onend: action(() => (this.Document._audioAnnoState = AudioAnnoState.stopped)),
                     });
                     this.Document._audioAnnoState = AudioAnnoState.playing;
                     break;
@@ -1405,14 +1417,10 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
                     tempDoc = view.Document;
                     MakeTemplate(tempDoc);
                     Doc.AddDocToList(Doc.UserDoc(), 'template_user', tempDoc);
-                    Doc.AddDocToList(DocListCast(Doc.MyTools.data)[1], 'data', makeUserTemplateButtonOrImage(tempDoc));
-                    tempDoc && Doc.AddDocToList(Cast(Doc.UserDoc().template_user, Doc, null), 'data', tempDoc);
+                    Doc.AddDocToList(DocListCast(Doc.MyTools?.data)[1], 'data', makeUserTemplateButtonOrImage(tempDoc));
+                    DocCast(Doc.UserDoc().template_user) && tempDoc && Doc.AddDocToList(DocCast(Doc.UserDoc().template_user)!, 'data', tempDoc);
                 } else {
-                    tempDoc = DocCast(view.Document[StrCast(view.Document.layout_fieldKey)]);
-                    if (!tempDoc) {
-                        tempDoc = view.Document;
-                        while (tempDoc && !Doc.isTemplateDoc(tempDoc)) tempDoc = DocCast(tempDoc.proto);
-                    }
+                    tempDoc = DocCast(Doc.LayoutField(view.Document));
                 }
             }
             Doc.UserDoc().defaultTextLayout = tempDoc ? new PrefetchProxy(tempDoc) : undefined;
@@ -1434,10 +1442,10 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
         if (this.Document.layout_fieldKey === 'layout_' + detailLayoutKeySuffix) this.switchViews(!!defaultLayout, defaultLayout, undefined, true);
         else this.switchViews(true, detailLayoutKeySuffix, undefined, true);
     };
-    public switchViews = (custom: boolean, view: string, finished?: () => void, useExistingLayout = false) => {
+    public switchViews = action((custom: boolean, view: string, finished?: () => void, useExistingLayout = false) => {
         const batch = UndoManager.StartBatch('switchView:' + view);
         // shrink doc first..
-        runInAction(() => { this._docViewInternal && (this._docViewInternal._animateScalingTo = 0.1); }); // prettier-ignore
+        this._docViewInternal && (this._docViewInternal._animateScalingTo = 0.1);
         setTimeout(
             action(() => {
                 if (useExistingLayout && custom && this.Document['layout_' + view]) {
@@ -1457,7 +1465,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
             }),
             Math.max(0, (this._docViewInternal?.animateScaleTime() ?? 0) - 10)
         );
-    };
+    });
     /**
      * @returns a hierarchy path through  the nested DocumentViews that display this view.  The last element of the path is this view.
      */
@@ -1510,7 +1518,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
                 ref={r => {
                     const val = r?.style.display !== 'none'; // if the outer overlay has been displayed, trigger the innner div to start it's opacity fade in transition
                     if (r && val !== this._enableHtmlOverlayTransitions) {
-                        setTimeout(action(() => { this._enableHtmlOverlayTransitions = val; })); // prettier-ignore
+                        setTimeout(action(() => (this._enableHtmlOverlayTransitions = val)));
                     }
                 }}
                 style={{ display: !this._htmlOverlayText ? 'none' : undefined }}>
@@ -1537,12 +1545,8 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
             <div
                 id={this.ViewGuid}
                 className="contentFittingDocumentView"
-                onPointerEnter={action(() => {
-                    this._isHovering = true;
-                })}
-                onPointerLeave={action(() => {
-                    this._isHovering = false;
-                })}>
+                onPointerEnter={action(() => (this._isHovering = true))} //
+                onPointerLeave={action(() => (this._isHovering = false))}>
                 {!this.Document || !this._props.PanelWidth() ? null : (
                     <div
                         className="contentFittingDocumentView-previewDoc"
@@ -1570,9 +1574,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
                             fitWidth={this.layout_fitWidthFunc}
                             ScreenToLocalTransform={this.screenToContentsTransform}
                             focus={this._props.focus || emptyFunction}
-                            ref={action((r: DocumentViewInternal | null) => {
-                                r && (this._docViewInternal = r);
-                            })}
+                            ref={action((r: DocumentViewInternal | null) => r && (this._docViewInternal = r))}
                         />
                         {this.htmlOverlay()}
                         {this.ComponentView?.infoUI?.()}
@@ -1616,7 +1618,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
             if (dv && (!containingDoc || dv.containerViewPath?.().lastElement()?.Document === containingDoc)) {
                 DocumentView.showDocumentView(dv, options).then(() => dv && Doc.linkFollowHighlight(dv.Document));
             } else {
-                const container = DocCast(containingDoc ?? doc.embedContainer ?? Doc.BestEmbedding(doc));
+                const container = DocCast(containingDoc ?? doc.embedContainer ?? Doc.BestEmbedding(doc))!;
                 const showDoc = !Doc.IsSystem(container) && !cv ? container : doc;
                 options.toggleTarget = undefined;
                 DocumentView.showDocument(showDoc, options, () => DocumentView.showDocument(doc, { ...options, openLocation: undefined })).then(() => {
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index ad6d93d43..de49f502f 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -80,7 +80,7 @@ export interface FieldViewSharedProps {
     setTitleFocus?: () => void;
     focus: FocusFuncType;
     onClickScript?: () => ScriptField | undefined;
-    onDoubleClickScript?: () => ScriptField;
+    onDoubleClickScript?: () => ScriptField | undefined;
     onPointerDownScript?: () => ScriptField;
     onPointerUpScript?: () => ScriptField;
     onKey?: (e: KeyboardEvent, textBox: FormattedTextBox) => boolean | undefined;
diff --git a/src/client/views/nodes/IconTagBox.tsx b/src/client/views/nodes/IconTagBox.tsx
index e3924eca7..0bbd6a0d3 100644
--- a/src/client/views/nodes/IconTagBox.tsx
+++ b/src/client/views/nodes/IconTagBox.tsx
@@ -108,7 +108,7 @@ export class IconTagBox extends ObservableReactComponent<IconTagProps> {
                 </Tooltip>
             )); // prettier-ignore
 
-        const audioannos = StrListCast(this.View.Document[Doc.LayoutFieldKey(this.View.Document) + '_audioAnnotations_text']);
+        const audioannos = StrListCast(this.View.Document[Doc.LayoutDataKey(this.View.Document) + '_audioAnnotations_text']);
         return !buttons.length && !audioannos.length ? null : (
             <div className="card-button-container" style={{ fontSize: '50px' }}>
                 {audioannos.length ? this.renderAudioButtons(this.View, audioannos.lastElement()) : null}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 2b9a78596..0c475b7bb 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -216,7 +216,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                 } else if (hitDropTarget(e.target as HTMLElement, this._regenerateIconRef.current)) {
                     this._regenerateLoading = true;
                     const drag = de.complete.docDragData.draggedDocuments.lastElement();
-                    const dragField = drag[Doc.LayoutFieldKey(drag)];
+                    const dragField = drag[Doc.LayoutDataKey(drag)];
                     const descText = RTFCast(dragField)?.Text || StrCast(dragField) || RTFCast(drag.text)?.Text || StrCast(drag.text) || StrCast(this.Document.title);
                     const oldPrompt = StrCast(this.Document.ai_firefly_prompt, StrCast(this.Document.title));
                     const newPrompt = (text: string) => (oldPrompt ? `${oldPrompt} ~~~ ${text}` : text);
@@ -224,7 +224,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                     added = false;
                 } else if (de.altKey || !this.dataDoc[this.fieldKey]) {
                     const layoutDoc = de.complete.docDragData?.draggedDocuments[0];
-                    const targetField = Doc.LayoutFieldKey(layoutDoc);
+                    const targetField = Doc.LayoutDataKey(layoutDoc);
                     const targetDoc = layoutDoc[DocData];
                     if (targetDoc[targetField] instanceof ImageField) {
                         added = true;
@@ -264,7 +264,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
         DocListCast(this.dataDoc[this.annotationKey]).forEach(doc => {
             doc.x = (NumCast(doc.x) / oldnativeWidth) * newnativeWidth;
             doc.y = (NumCast(doc.y) / oldnativeWidth) * newnativeWidth;
-            if (!RTFCast(doc[Doc.LayoutFieldKey(doc)])) {
+            if (!RTFCast(doc[Doc.LayoutDataKey(doc)])) {
                 doc.width = (NumCast(doc.width) / oldnativeWidth) * newnativeWidth;
                 doc.height = (NumCast(doc.height) / oldnativeWidth) * newnativeWidth;
             }
@@ -351,9 +351,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                 event: () => {
                     Networking.PostToServer('/queryFireflyImageText', {
                         file: (file => {
-                            const ext = extname(file);
-                            return file.replace(ext, (this._error ? '_o' : this._curSuffix) + ext);
-                        })(ImageCast(this.Document[Doc.LayoutFieldKey(this.Document)])?.url.href),
+                            const ext = file ? extname(file) : '';
+                            return file?.replace(ext, (this._error ? '_o' : this._curSuffix) + ext);
+                        })(ImageCast(this.Document[Doc.LayoutDataKey(this.Document)])?.url.href),
                     }).then(text => alert(text));
                 },
                 icon: 'expand-arrows-alt',
@@ -364,9 +364,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                     Networking.PostToServer('/expandImage', {
                         prompt: 'sunny skies',
                         file: (file => {
-                            const ext = extname(file);
-                            return file.replace(ext, (this._error ? '_o' : this._curSuffix) + ext);
-                        })(ImageCast(this.Document[Doc.LayoutFieldKey(this.Document)])?.url.href),
+                            const ext = file ? extname(file) : '';
+                            return file?.replace(ext, (this._error ? '_o' : this._curSuffix) + ext);
+                        })(ImageCast(this.Document[Doc.LayoutDataKey(this.Document)])?.url.href),
                     }).then(res => {
                         const info = res as Upload.ImageInformation;
                         const img = Docs.Create.ImageDocument(info.accessPaths.agnostic.client, { title: 'expand:' + this.Document.title });
@@ -510,7 +510,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                 <div
                     className="imageBox-regenerateDropTarget"
                     ref={this._regenerateIconRef}
-                    onClick={() => DocumentView.showDocument(DocCast(this.Document.ai_firefly_generatedDocs), { openLocation: OpenWhere.addRight })}
+                    onClick={() => DocCast(this.Document.ai_firefly_generatedDocs) && DocumentView.showDocument(DocCast(this.Document.ai_firefly_generatedDocs)!, { openLocation: OpenWhere.addRight })}
                     style={{
                         display: (this._props.isContentActive() && (SnappingManager.CanEmbed || this.Document.ai_firefly_generatedDocs)) || this._regenerateLoading ? 'block' : 'none',
                         transform: `scale(${this.uiBtnScaling})`,
@@ -532,7 +532,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
         const defaultUrl = new URL(ClientUtils.prepend(DefaultPath));
         const altpaths =
             alts
-                ?.map(doc => (doc instanceof Doc ? (ImageCast(doc[Doc.LayoutFieldKey(doc)])?.url ?? defaultUrl) : defaultUrl))
+                ?.map(doc => (doc instanceof Doc ? (ImageCast(doc[Doc.LayoutDataKey(doc)])?.url ?? defaultUrl) : defaultUrl))
                 .filter(url => url)
                 .map(url => this.choosePath(url)) ?? []; // acc  ess the primary layout data of the alternate documents
         const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths;
@@ -664,7 +664,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                                                 const url = firstImg.pathname;
                                                 const imgField = new ImageField(url);
                                                 this._prevImgs.length === 0 &&
-                                                    this._prevImgs.push({ prompt: StrCast(this.dataDoc.ai_firefly_prompt), seed: this.dataDoc.ai_firefly_seed as number, href: this.paths.lastElement(), pathname: field.url.pathname });
+                                                    this._prevImgs.push({ prompt: StrCast(this.dataDoc.ai_firefly_prompt), seed: this.dataDoc.ai_firefly_seed as number, href: this.paths.lastElement(), pathname: field?.url.pathname ?? '' });
                                                 this._prevImgs.unshift({ prompt: firstImg.prompt, seed: firstImg.seed, pathname: url });
                                                 this.dataDoc.ai_firefly_history = JSON.stringify(this._prevImgs);
                                                 this.dataDoc.ai_firefly_prompt = firstImg.prompt;
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index be897b3f3..706607fe1 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -96,7 +96,7 @@ export class KeyValueBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const { script, type, onDelegate } = kvpScript;
         const chooseDelegate = forceOnDelegate || onDelegate || keyIn.startsWith('_');
         const key = chooseDelegate && keyIn.startsWith('$') ? keyIn.slice(1) : keyIn;
-        const target = chooseDelegate ? doc : DocCast(doc.proto, doc);
+        const target = chooseDelegate ? doc : DocCast(doc.proto, doc)!;
         let field: FieldType | undefined;
         switch (type) {
             case 'computed': field = new ComputedField(script); break; // prettier-ignore
@@ -261,15 +261,15 @@ export class KeyValueBox extends ViewBoxBaseComponent<FieldViewProps>() {
     getFieldView = () => {
         const rows = this.rows.filter(row => row.isChecked);
         if (rows.length > 1) {
-            const parent = Docs.Create.StackingDocument([], { _layout_autoHeight: true, _width: 300, title: `field views for ${DocCast(this.Document).title}`, _chromeHidden: true });
+            const parent = Docs.Create.StackingDocument([], { _layout_autoHeight: true, _width: 300, title: `field views for ${this.Document.title}`, _chromeHidden: true });
             rows.forEach(row => {
-                const field = this.createFieldView(DocCast(this.Document), row);
+                const field = this.createFieldView(this.Document, row);
                 field && Doc.AddDocToList(parent, 'data', field);
                 row.uncheck();
             });
             return parent;
         }
-        return rows.length ? this.createFieldView(DocCast(this.Document), rows.lastElement()) : undefined;
+        return rows.length ? this.createFieldView(this.Document, rows.lastElement()) : undefined;
     };
 
     createFieldView = (templateDoc: Doc, row: KeyValuePair) => {
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index c9e0aea5a..d8f968a40 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -61,7 +61,7 @@ export class KeyValuePair extends ObservableReactComponent<KeyValuePairProps> {
     };
 
     render() {
-        let doc = this._props.keyName.startsWith('_') ? this._props.doc[DocLayout] : this._props.doc;
+        let doc: Doc | undefined = this._props.keyName.startsWith('_') ? this._props.doc[DocLayout] : this._props.doc;
         const layoutField = doc !== this._props.doc;
         const key = layoutField ? this._props.keyName.replace(/^_/, '') : this._props.keyName;
         let protoCount = 0;
@@ -85,7 +85,7 @@ export class KeyValuePair extends ObservableReactComponent<KeyValuePairProps> {
                             style={hover}
                             className="keyValuePair-td-key-delete"
                             onClick={undoable(() => {
-                                delete (Object.keys(doc).indexOf(key) !== -1 ? doc : DocCast(this._props.doc.proto))[key];
+                                doc && delete (Object.keys(doc).indexOf(key) !== -1 ? doc : DocCast(this._props.doc.proto)!)[key];
                             }, 'set key value')}>
                             X
                         </button>
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 36d260fb9..83c44c80f 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -210,7 +210,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
         this._disposers.scroll = reaction(
             () => this.layoutDoc.layout_scrollTop,
             () => {
-                if (!(ComputedField.WithoutComputed(() => FieldValue(this.Document[this.SidebarKey + '_panY'])) instanceof ComputedField)) {
+                if (!(ComputedField.DisableCompute(() => FieldValue(this.Document[this.SidebarKey + '_panY'])) instanceof ComputedField)) {
                     this.Document[this.SidebarKey + '_panY'] = ComputedField.MakeFunction('this.layout_scrollTop');
                 }
                 this.layoutDoc[this.SidebarKey + '_freeform_scale'] = 1;
diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
index 53783e8a3..25c708da8 100644
--- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
@@ -98,7 +98,7 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
                   });
         screengrabber.overlayX = 70; // was -400
         screengrabber.overlayY = 590; // was 0
-        screengrabber['$' + Doc.LayoutFieldKey(screengrabber) + '_trackScreen'] = true;
+        screengrabber['$' + Doc.LayoutDataKey(screengrabber) + '_trackScreen'] = true;
         Doc.AddToMyOverlay(screengrabber); // just adds doc to overlay
         DocumentView.addViewRenderedCb(screengrabber, docView => {
             RecordingBox.screengrabber = docView.ComponentView as RecordingBox;
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 999f9c1cd..4f02d68d6 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -284,7 +284,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
     setupDictation = () => {
         if (this.dataDoc[this.fieldKey + '_dictation']) return;
         const dictationText = DocUtils.GetNewTextDoc('dictation', NumCast(this.Document.x), NumCast(this.Document.y) + NumCast(this.layoutDoc._height) + 10, NumCast(this.layoutDoc._width), 2 * NumCast(this.layoutDoc._height));
-        const textField = Doc.LayoutFieldKey(dictationText);
+        const textField = Doc.LayoutDataKey(dictationText);
         dictationText._layout_autoHeight = false;
         const dictationTextProto = dictationText[DocData];
         dictationTextProto[`${textField}_recordingSource`] = this.dataDoc;
diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx
index 8da422039..38a43e8d4 100644
--- a/src/client/views/nodes/ScriptingBox.tsx
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -1,14 +1,12 @@
-/* eslint-disable react/button-has-type */
 import { action, computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import ResizeObserver from 'resize-observer-polyfill';
 import { returnAlways, returnEmptyString } from '../../../ClientUtils';
-import { Doc } from '../../../fields/Doc';
+import { Doc, StrListCast } from '../../../fields/Doc';
 import { List } from '../../../fields/List';
-import { listSpec } from '../../../fields/Schema';
 import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { Docs } from '../../documents/Documents';
@@ -26,10 +24,8 @@ import './ScriptingBox.scss';
 import * as ts from 'typescript';
 import { FieldType } from '../../../fields/ObjectField';
 
-// eslint-disable-next-line @typescript-eslint/no-var-requires
 const getCaretCoordinates = require('textarea-caret');
 
-// eslint-disable-next-line @typescript-eslint/no-var-requires
 const ReactTextareaAutocomplete = require('@webscopeio/react-textarea-autocomplete').default;
 
 @observer
@@ -105,7 +101,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
         this.dataDoc[this.fieldKey + '-functionDescription'] = value;
     }
     @computed({ keepAlive: true }) get compileParams() {
-        return Cast(this.dataDoc[this.fieldKey + '-params'], listSpec('string'), []);
+        return StrListCast(this.dataDoc[this.fieldKey + '-params']);
     }
     set compileParams(value) {
         this.dataDoc[this.fieldKey + '-params'] = new List<string>(value);
@@ -426,7 +422,6 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
                             onChange={e => this.viewChanged(e, parameter)}
                             value={typeof this.dataDoc[parameter] === 'string' ? 'S' + StrCast(this.dataDoc[parameter]) : typeof this.dataDoc[parameter] === 'number' ? 'N' + NumCast(this.dataDoc[parameter]) : 'B' + BoolCast(this.dataDoc[parameter])}>
                             {types.map((type, i) => (
-                                // eslint-disable-next-line react/no-array-index-key
                                 <option key={i} className="scriptingBox-viewOption" value={(typeof type === 'string' ? 'S' : typeof type === 'number' ? 'N' : 'B') + type}>
                                     {' '}
                                     {type.toString()}{' '}
@@ -680,7 +675,6 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
         const definedParameters = !this.compileParams.length ? null : (
             <div className="scriptingBox-plist" style={{ width: '30%' }}>
                 {this.compileParams.map((parameter, i) => (
-                    // eslint-disable-next-line react/no-array-index-key
                     <div key={i} className="scriptingBox-pborder" onKeyDown={e => e.key === 'Enter' && this._overlayDisposer?.()}>
                         <EditableView
                             display="block"
@@ -760,7 +754,6 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
                 {!this.compileParams.length || !this.paramsNames ? null : (
                     <div className="scriptingBox-plist">
                         {this.paramsNames.map((parameter: string, i: number) => (
-                            // eslint-disable-next-line react/no-array-index-key
                             <div key={i} className="scriptingBox-pborder" onKeyDown={e => e.key === 'Enter' && this._overlayDisposer?.()}>
                                 <div className="scriptingBox-wrapper" style={{ maxHeight: '40px' }}>
                                     <div className="scriptingBox-paramNames"> {`${parameter}:${this.paramsTypes[i]} = `} </div>
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index bad4f5b13..5603786f0 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -6,7 +6,7 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import * as WebRequest from 'web-request';
 import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, ClientUtils, DivHeight, getWordAtPoint, lightOrDark, returnFalse, returnOne, returnZero, setupMoveUpEvents, smoothScroll } from '../../../ClientUtils';
-import { Doc, DocListCast, Field, FieldType, Opt } from '../../../fields/Doc';
+import { Doc, DocListCast, Field, FieldType, Opt, StrListCast } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
 import { HtmlField } from '../../../fields/HtmlField';
 import { InkTool } from '../../../fields/InkField';
@@ -645,8 +645,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     };
 
     forward = (checkAvailable?: boolean) => {
-        const future = Cast(this.dataDoc[this.fieldKey + '_future'], listSpec('string'), []);
-        const history = Cast(this.dataDoc[this.fieldKey + '_history'], listSpec('string'), []);
+        const future = StrListCast(this.dataDoc[this.fieldKey + '_future']);
+        const history = StrListCast(this.dataDoc[this.fieldKey + '_history']);
         if (checkAvailable) return future.length;
         runInAction(() => {
             if (future.length) {
@@ -672,23 +672,19 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     };
 
     back = (checkAvailable?: boolean) => {
-        const future = Cast(this.dataDoc[this.fieldKey + '_future'], listSpec('string'));
-        const history = Cast(this.dataDoc[this.fieldKey + '_history'], listSpec('string'), []);
+        const future = StrListCast(this.dataDoc[this.fieldKey + '_future']);
+        const history = StrListCast(this.dataDoc[this.fieldKey + '_history']);
         if (checkAvailable) return history.length;
         runInAction(() => {
             if (history.length) {
                 const curUrl = this._url;
-                if (future === undefined) this.dataDoc[this.fieldKey + '_future'] = new List<string>([this._url]);
+                if (!future.length) this.dataDoc[this.fieldKey + '_future'] = new List<string>([this._url]);
                 else this.dataDoc[this.fieldKey + '_future'] = new List<string>([...future, this._url]);
                 this.dataDoc[this.fieldKey] = new WebField(new URL(history.pop()!));
                 this._scrollHeight = 0;
                 if (this._webUrl === this._url) {
                     this._webUrl = curUrl;
-                    setTimeout(
-                        action(() => {
-                            this._webUrl = this._url;
-                        })
-                    );
+                    setTimeout(action(() => (this._webUrl = this._url)));
                 } else {
                     this._webUrl = this._url;
                 }
diff --git a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx
index b023b1de6..528bcd05a 100644
--- a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx
+++ b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx
@@ -733,7 +733,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                     const x2 = parseFloat(values[2]) * Doc.NativeWidth(doc);
                     const y2 = parseFloat(values[3]) * Doc.NativeHeight(doc) + foundChunk.startPage * Doc.NativeHeight(doc);
 
-                    const annotationKey = '$' + Doc.LayoutFieldKey(doc) + '_annotations';
+                    const annotationKey = '$' + Doc.LayoutDataKey(doc) + '_annotations';
 
                     const existingDoc = DocListCast(doc[annotationKey]).find(d => d.citation_id === citation.citation_id);
                     const highlightDoc = existingDoc ?? this.createImageCitationHighlight(x1, y1, x2, y2, citation, annotationKey, doc);
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index cfb6dfa12..dc23a695d 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -214,9 +214,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     }
 
     getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
-        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 });
+        if (!pinProps && this.EditorView?.state.selection.empty) return this.rootDoc;
+        const anchor = Docs.Create.ConfigDocument({ title: StrCast(this.rootDoc?.title), annotationOn: this.rootDoc });
         this.addDocument(anchor);
         this._finishingLink = true;
         this.makeLinkAnchor(anchor, OpenWhere.addRight, undefined, 'Anchored Selection', false, addAsAnnotation);
@@ -245,7 +244,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                     anchor.followLinkAudio = true;
                     let stopFunc: () => void = emptyFunction;
                     target.$mediaState = mediaState.Recording;
-                    DictationManager.recordAudioAnnotation(target, Doc.LayoutFieldKey(target), stop => { stopFunc = stop }); // prettier-ignore
+                    DictationManager.recordAudioAnnotation(target, Doc.LayoutDataKey(target), stop => { stopFunc = stop }); // prettier-ignore
 
                     const reactionDisposer = reaction(
                         () => target.mediaState,
@@ -427,8 +426,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         const oldAutoLinks = Doc.Links(this.Document).filter(
             link => 
                 ((!Doc.isTemplateForField(this.Document) &&
-                    (!Doc.isTemplateForField(DocCast(link.link_anchor_1)) || !Doc.AreProtosEqual(DocCast(link.link_anchor_1), this.Document)) &&
-                    (!Doc.isTemplateForField(DocCast(link.link_anchor_2)) || !Doc.AreProtosEqual(DocCast(link.link_anchor_2), this.Document))) ||
+                    ((DocCast(link.link_anchor_1) && !Doc.isTemplateForField(DocCast(link.link_anchor_1)!)) || !Doc.AreProtosEqual(DocCast(link.link_anchor_1), this.Document)) &&
+                    ((DocCast(link.link_anchor_2) && !Doc.isTemplateForField(DocCast(link.link_anchor_2)!)) || !Doc.AreProtosEqual(DocCast(link.link_anchor_2), this.Document))) ||
                  (Doc.isTemplateForField(this.Document) && (link.link_anchor_1 === this.Document || link.link_anchor_2 === this.Document))) &&
                 link.link_relationship === LinkManager.AutoKeywords
         ); // prettier-ignore
@@ -450,18 +449,18 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     updateTitle = () => {
         const title = StrCast(this.dataDoc.title, Cast(this.dataDoc.title, RichTextField, null)?.Text);
         if (
-            !this._props.dontRegisterView && // (this.Document.isTemplateForField === "text" || !this.Document.isTemplateForField) && // only update the title if the data document's data field is changing
+            !this._props.dontRegisterView && // only update the title if the data document's data field is changing
             title.startsWith('-') &&
             this.EditorView &&
             !this.dataDoc.title_custom &&
-            (Doc.LayoutFieldKey(this.Document) === this.fieldKey || this.fieldKey === 'text')
+            (Doc.LayoutDataKey(this.Document) === this.fieldKey || this.fieldKey === 'text')
         ) {
             let node = this.EditorView.state.doc;
             while (node.firstChild && node.firstChild.type.name !== 'text') node = node.firstChild;
             const str = node.textContent;
             const prefix = '-';
 
-            const cfield = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc.title));
+            const cfield = ComputedField.DisableCompute(() => FieldValue(this.dataDoc.title));
             if (!(cfield instanceof ComputedField)) {
                 this.dataDoc.title = (prefix + str.substring(0, Math.min(40, str.length)) + (str.length > 40 ? '...' : '')).trim();
             }
@@ -588,7 +587,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         }
         const dragData = de.complete.docDragData;
         if (dragData) {
-            const dataDoc = Doc.IsDelegateField(DocCast(this.layoutDoc.proto), this.fieldKey) ? DocCast(this.layoutDoc.proto) : this.dataDoc;
+            const layoutProto = DocCast(this.layoutDoc.proto);
+            const dataDoc = layoutProto && Doc.IsDelegateField(layoutProto, this.fieldKey) ? layoutProto : this.dataDoc;
             const effectiveAcl = GetEffectiveAcl(dataDoc);
             const draggedDoc = dragData.droppedDocuments.lastElement();
             let added: Opt<boolean>;
@@ -596,7 +596,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
             if ([AclEdit, AclAdmin, AclSelfEdit].includes(effectiveAcl) && !dragData.draggedDocuments.includes(this.Document)) {
                 // replace text contents when dragging with Alt
                 if (de.altKey) {
-                    const fieldKey = Doc.LayoutFieldKey(draggedDoc);
+                    const fieldKey = Doc.LayoutDataKey(draggedDoc);
                     if (draggedDoc[fieldKey] instanceof RichTextField && !Doc.AreProtosEqual(draggedDoc, this.Document)) {
                         Doc.GetProto(this.dataDoc)[this.fieldKey] = Field.Copy(draggedDoc[fieldKey]);
                     }
@@ -1003,16 +1003,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                 const link = CreateLinkToActiveAudio(textanchorFunc, false).lastElement();
                 if (link) {
                     link.$isDictation = true;
-                    const audioanchor = Cast(link.link_anchor_2, Doc, null);
-                    const textanchor = Cast(link.link_anchor_1, Doc, null);
+                    const audioanchor = DocCast(link.link_anchor_2);
+                    const textanchor = DocCast(link.link_anchor_1);
                     if (audioanchor) {
                         audioanchor.backgroundColor = 'tan';
                         const audiotag = this.EditorView.state.schema.nodes.audiotag.create({
                             timeCode: NumCast(audioanchor._timecodeToShow),
                             audioId: audioanchor[Id],
-                            textId: textanchor[Id],
+                            textId: textanchor?.[Id] ?? '',
                         });
-                        textanchor.$title = 'dictation:' + audiotag.attrs.timeCode;
+                        textanchor && (textanchor.$title = 'dictation:' + audiotag.attrs.timeCode);
                         const tr = this.EditorView.state.tr.insert(this.EditorView.state.doc.content.size, audiotag);
                         const tr2 = tr.setSelection(TextSelection.create(tr.doc, tr.doc.content.size));
                         this.EditorView.dispatch(tr.setSelection(TextSelection.create(tr2.doc, tr2.doc.content.size)));
@@ -1147,11 +1147,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
 
     // if the scroll height has changed and we're in layout_autoHeight mode, then we need to update the textHeight component of the doc.
     // Since we also monitor all component height changes, this will update the document's height.
-    resetNativeHeight = (scrollHeight: number) => {
-        const nh = this.layoutDoc.isTemplateForField ? 0 : NumCast(this.layoutDoc._nativeHeight);
+    resetNativeHeight = action((scrollHeight: number) => {
         this.layoutDoc['_' + this.fieldKey + '_height'] = scrollHeight;
-        if (nh) this.layoutDoc._nativeHeight = scrollHeight;
-    };
+        if (!this.layoutDoc.isTemplateForField) this.layoutDoc._nativeHeight = scrollHeight;
+    });
 
     addPlugin = (plugin: Plugin) => {
         const editorView = this.EditorView;
@@ -1216,8 +1215,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                 const dataData = this.dataDoc[this.fieldKey];
                 const layoutData = Doc.AreProtosEqual(this.layoutDoc, this.dataDoc) ? undefined : this.layoutDoc[this.fieldKey];
                 const dataTime = dataData ? (DateCast(this.dataDoc[this.fieldKey + '_modificationDate'])?.date.getTime() ?? 0) : 0;
-                const layoutTime = layoutData && this.dataDoc[this.fieldKey + '_autoUpdate'] ? (DateCast(DocCast(this.layoutDoc)[this.fieldKey + '_modificationDate'])?.date.getTime() ?? 0) : 0;
-                const protoTime = protoData && this.dataDoc[this.fieldKey + '_autoUpdate'] ? (DateCast(DocCast(this.dataDoc.proto)[this.fieldKey + '_modificationDate'])?.date.getTime() ?? 0) : 0;
+                const layoutTime = layoutData && this.dataDoc[this.fieldKey + '_autoUpdate'] ? (DateCast(DocCast(this.layoutDoc)?.[this.fieldKey + '_modificationDate'])?.date.getTime() ?? 0) : 0;
+                const protoTime = protoData && this.dataDoc[this.fieldKey + '_autoUpdate'] ? (DateCast(DocCast(this.dataDoc.proto)?.[this.fieldKey + '_modificationDate'])?.date.getTime() ?? 0) : 0;
                 const recentData = dataTime >= layoutTime ? (protoTime >= dataTime ? protoData : dataData) : layoutTime >= protoTime ? layoutData : protoData;
                 const whichData = recentData ?? (this.layoutDoc.isTemplateDoc ? layoutData : protoData) ?? protoData;
                 return !whichData ? undefined : { data: RTFCast(whichData), str: Field.toString(DocCast(whichData) ?? StrCast(whichData)) };
@@ -1313,7 +1312,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
             // } catch (err) {
             //     console.log('Drop failed', err);
             // }
-            this.addDocument?.(DocCast(this.Document.image));
+            DocCast(this.Document.image) && this.addDocument?.(DocCast(this.Document.image)!);
         }
 
         //if (this.Document.image) this.addDocument?.(DocCast(this.Document.image));
@@ -1426,8 +1425,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         const rtfField = Cast((!curText && this.layoutDoc[this.fieldKey]) || this.dataDoc[fieldKey], RichTextField);
         if (this.ProseRef) {
             this.EditorView?.destroy();
+            const edState = () => {
+                try {
+                    return rtfField?.Data ? EditorState.fromJSON(config, JSON.parse(rtfField.Data)) : EditorState.create(config);
+                } catch {
+                    return EditorState.create(config);
+                }
+            };
             this._editorView = new EditorView(this.ProseRef, {
-                state: rtfField?.Data ? EditorState.fromJSON(config, JSON.parse(rtfField.Data)) : EditorState.create(config),
+                state: edState(),
                 handleScrollToSelection: this.scrollToSelection,
                 dispatchTransaction: this.dispatchTransaction,
                 nodeViews: FormattedTextBox._nodeViews(this),
@@ -1439,7 +1445,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
             (this.EditorView as unknown as { scrollToSelection: unknown }).scrollToSelection = this.scrollToSelection;
             const { state } = this._editorView;
             if (!rtfField) {
-                const dataDoc = Doc.IsDelegateField(DocCast(this.layoutDoc.proto), this.fieldKey) ? DocCast(this.layoutDoc.proto) : this.dataDoc;
+                const layoutProto = DocCast(this.layoutDoc.proto);
+                const dataDoc = layoutProto && Doc.IsDelegateField(layoutProto, this.fieldKey) ? layoutProto : this.dataDoc;
                 const startupText = Field.toString(dataDoc[fieldKey] as FieldType);
                 const textAlign = StrCast(this.dataDoc[this.fieldKey + '_align'], StrCast(Doc.UserDoc().textAlign)) || 'left';
                 if (textAlign !== 'left') {
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 4fa1fb13b..d31a58030 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -148,11 +148,11 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
         this._activeAlignment = this.getActiveAlignment();
         this._activeFitBox = BoolCast(refDoc[refField + 'fitBox'], BoolCast(Doc.UserDoc().fitBox));
         this._activeFontFamily = !activeFamilies.length
-            ? StrCast(this.TextView?.Document._text_fontFamily, StrCast(this.dataDoc?.[Doc.LayoutFieldKey(this.dataDoc) + '_fontFamily'], refVal('fontFamily', 'Arial')))
+            ? StrCast(this.TextView?.Document._text_fontFamily, StrCast(this.dataDoc?.[Doc.LayoutDataKey(this.dataDoc) + '_fontFamily'], refVal('fontFamily', 'Arial')))
             : activeFamilies.length === 1
               ? String(activeFamilies[0])
               : 'various';
-        this._activeFontSize = !activeSizes.length ? StrCast(this.TextView?.Document.fontSize, StrCast(this.dataDoc?.[Doc.LayoutFieldKey(this.dataDoc) + '_fontSize'], refVal('fontSize', '10px'))) : activeSizes[0];
+        this._activeFontSize = !activeSizes.length ? StrCast(this.TextView?.Document.fontSize, StrCast(this.dataDoc?.[Doc.LayoutDataKey(this.dataDoc) + '_fontSize'], refVal('fontSize', '10px'))) : activeSizes[0];
         this._activeFontColor = !activeColors.length ? StrCast(this.TextView?.Document.fontColor, refVal('fontColor', 'black')) : activeColors.length > 0 ? String(activeColors[0]) : '...';
         this._activeHighlightColor = !activeHighlights.length ? '' : activeHighlights.length > 0 ? String(activeHighlights[0]) : '...';
 
@@ -382,7 +382,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
                     });
             }
         } else if (this.dataDoc) {
-            this.dataDoc[`${Doc.LayoutFieldKey(this.dataDoc)}_${fontField}`] = value;
+            this.dataDoc[`${Doc.LayoutDataKey(this.dataDoc)}_${fontField}`] = value;
             this.updateMenu(undefined, undefined, undefined, this.dataDoc);
         } else {
             Doc.UserDoc()[fontField] = value;
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 14ea911b1..26ccf6931 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -108,25 +108,23 @@ export class RichTextRules {
             new InputRule(/>>$/, (state, match, start, end) => {
                 const numInlines = NumCast(this.Document.$inlineTextCount);
                 this.Document.$inlineTextCount = numInlines + 1;
-                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 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,
                     _width: 75,
                     _height: 35,
-                    annotationOn: this.Document[DocData],
                     _layout_fitWidth: true,
                     _layout_autoHeight: true,
-                    text_fontSize: '9px',
-                    title: 'inline comment',
                 });
+                textDocInline.layout_fieldKey = inlineLayoutKey;
+                textDocInline.annotationOn = this.Document[DocData];
                 textDocInline.title = inlineFieldKey; // give the annotation its own title
                 textDocInline.title_custom = true; // And make sure that it's 'custom' so that editing text doesn't change the title of the containing doc
-                textDocInline.isTemplateForField = inlineFieldKey; // this is needed in case the containing text doc is converted to a template at some point
+                //textDocInline.isTemplateForField = inlineFieldKey; // this is needed in case the containing text doc is converted to a template at some point
                 textDocInline.isDataDoc = true;
                 textDocInline.proto = this.Document[DocData]; // make the annotation inherit from the outer text doc so that it can resolve any nested field references, e.g., [[field]]
-                this.Document[inlineLayoutKey] = FormattedTextBox.LayoutString(inlineFieldKey); // create a layout string for the layout key that will render the annotation text
-                this.Document[inlineFieldKey] = ''; // set a default value for the annotation
+                this.Document['$' + inlineLayoutKey] = FormattedTextBox.LayoutString(inlineFieldKey); // create a layout string for the layout key that will render the annotation text
+                this.Document['$' + inlineFieldKey] = ''; // set a default value for the annotation
                 const node = state.doc.resolve(start).nodeAfter;
                 const newNode = schema.nodes.dashComment.create({ docId: textDocInline[Id], reflow: true });
                 const dashDoc = schema.nodes.dashDoc.create({ width: 75, height: 35, title: 'dashDoc', docId: textDocInline[Id], float: 'right' });
@@ -287,7 +285,8 @@ export class RichTextRules {
                             editor.dispatch(estate.tr.setSelection(new TextSelection(estate.doc.resolve(start), estate.doc.resolve(end - prefixLength))));
                         }
 
-                        DocUtils.MakeLink(this.TextBox.getAnchor(true), target, { link_relationship: 'portal to:portal from' });
+                        const tanchor = this.TextBox.getAnchor(true);
+                        tanchor && DocUtils.MakeLink(tanchor, target, { link_relationship: 'portal to:portal from' });
 
                         const teditor = this.TextBox.EditorView;
                         if (teditor && selection) {
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 23155ebf3..11f35b8ef 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -149,7 +149,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     setIsLoading =              action((input?: boolean) => { this._isLoading = !!input; }); // prettier-ignore
     setShowAIGalleryVisibilty = action((visible: boolean) => { this._showAIGallery = visible; }); // prettier-ignore
     setBezierControlPoints = action((newPoints: { p1: number[]; p2: number[] }) => {
-        this.setEaseFunc(this.activeItem, `cubic-bezier(${newPoints.p1[0]}, ${newPoints.p1[1]}, ${newPoints.p2[0]}, ${newPoints.p2[1]})`);
+        this.activeItem && this.setEaseFunc(this.activeItem, `cubic-bezier(${newPoints.p1[0]}, ${newPoints.p1[1]}, ${newPoints.p2[0]}, ${newPoints.p2[1]})`);
     });
 
     @computed get showEaseFunctions() {
@@ -158,7 +158,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @computed
     get currCPoints() {
-        return EaseFuncToPoints(this.activeItem.presentation_easeFunc ? StrCast(this.activeItem.presentation_easeFunc) : 'ease');
+        return EaseFuncToPoints(this.activeItem?.presentation_easeFunc ? StrCast(this.activeItem.presentation_easeFunc) : 'ease');
     }
 
     @computed
@@ -175,7 +175,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return DocListCast(this.Document[this.presFieldKey]);
     }
     @computed get tagDocs() {
-        return this.childDocs.map(doc => Cast(doc.presentation_targetDoc, Doc, null));
+        return this.childDocs.map(doc => DocCast(doc.presentation_targetDoc)!).filter(doc => doc);
     }
     @computed get itemIndex() {
         return NumCast(this.Document._itemIndex);
@@ -187,11 +187,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return DocCast(this.activeItem?.presentation_targetDoc);
     }
     public static targetRenderedDoc = (doc: Doc) => {
-        const targetDoc = Cast(doc?.presentation_targetDoc, Doc, null);
+        const targetDoc = DocCast(doc?.presentation_targetDoc);
         return targetDoc?.layout_unrendered ? DocCast(targetDoc.annotationOn) : targetDoc;
     };
     @computed get scrollable() {
-        if ([DocumentType.PDF, DocumentType.WEB, DocumentType.RTF].includes(this.targetDoc.type as DocumentType) || this.targetDoc._type_collection === CollectionViewType.Stacking) return true;
+        if ([DocumentType.PDF, DocumentType.WEB, DocumentType.RTF].includes(this.targetDoc?.type as DocumentType) || this.targetDoc?._type_collection === CollectionViewType.Stacking) return true;
         return false;
     }
     @computed get selectedDocumentView() {
@@ -273,8 +273,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     };
 
     stopTempMedia = (targetDocField: FieldResult) => {
-        const targetDoc = DocCast(DocCast(targetDocField).annotationOn) ?? DocCast(targetDocField);
-        if ([DocumentType.VID, DocumentType.AUDIO].includes(targetDoc.type as DocumentType)) {
+        const targetDoc = DocCast(DocCast(targetDocField)?.annotationOn) ?? DocCast(targetDocField);
+        if ([DocumentType.VID, DocumentType.AUDIO].includes(targetDoc?.type as DocumentType)) {
             const targMedia = DocumentView.getDocumentView(targetDoc);
             targMedia?.ComponentView?.Pause?.();
         }
@@ -295,13 +295,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         this.setIsLoading(true);
         const slideDefaults: { [key: string]: FieldResult } = { presentation_transition: 500, config_zoom: 1 };
         const currSlideProperties = gptSlideProperties.reduce(
-            (prev, key) => { prev[key] = Field.toString(this.activeItem[key]) ?? prev[key]; return prev; }, 
+            (prev, key) => { prev[key] = Field.toString(this.activeItem?.[key]) ?? prev[key]; return prev; }, 
             slideDefaults); // prettier-ignore
 
         gptTrailSlideCustomization(input, JSON.stringify(currSlideProperties))
             .then(res =>
                 (Object.entries(JSON.parse(res)) as string[][]).forEach(([key, val]) => {
-                    this.activeItem[key] = (+val).toString() === val ? +val : (val ?? this.activeItem[key]);
+                    this.activeItem && (this.activeItem[key] = (+val).toString() === val ? +val : (val ?? this.activeItem[key]));
                 })
             )
             .catch(e => console.error(e))
@@ -325,7 +325,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         const serial = nextSelected + 1 < this.childDocs.length && NumCast(this.childDocs[nextSelected + 1].presentation_groupWithUp) > 1;
                         if (serial) {
                             this.gotoDocument(nextSelected, this.activeItem, true, async () => {
-                                const waitTime = NumCast(this.activeItem.presentation_duration);
+                                const waitTime = NumCast(this.activeItem?.presentation_duration);
                                 await new Promise<void>(res => {
                                     setTimeout(res, Math.max(0, waitTime));
                                 });
@@ -346,7 +346,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     progressivizedItems = (doc: Doc) => {
         const targetList = PresBox.targetRenderedDoc(doc);
         if (doc.presentation_indexed !== undefined && targetList) {
-            const listItems = (Cast(targetList[Doc.LayoutFieldKey(targetList)], listSpec(Doc), null)?.filter(d => d instanceof Doc) as Doc[]) ?? DocListCast(targetList[Doc.LayoutFieldKey(targetList) + '_annotations']);
+            const listItems = (Cast(targetList[Doc.LayoutDataKey(targetList)], listSpec(Doc), null)?.filter(d => d instanceof Doc) as Doc[]) ?? DocListCast(targetList[Doc.LayoutDataKey(targetList) + '_annotations']);
             return listItems.filter(ldoc => !ldoc.layout_unrendered);
         }
         return undefined;
@@ -364,7 +364,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     next = () => {
         const progressiveReveal = (first: boolean) => {
             const presIndexed = Cast(this.activeItem?.presentation_indexed, 'number', null);
-            if (presIndexed !== undefined) {
+            if (presIndexed !== undefined && this.activeItem) {
                 const listItems = this.progressivizedItems(this.activeItem);
                 const listItemDoc = listItems?.[presIndexed];
                 if (listItems && listItemDoc) {
@@ -395,7 +395,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (this.childDocs[this.itemIndex + 1] !== undefined) {
             // Case 1: No more frames in current doc and next slide is defined, therefore move to next slide
             const slides = DocListCast(this.Document[StrCast(this.presFieldKey, 'data')]);
-            const curLast = this.selectedArray.size ? Math.max(...Array.from(this.selectedArray).map(d => slides.indexOf(DocCast(d)))) : this.itemIndex;
+            const curLast = this.selectedArray.size ? Math.max(...Array.from(this.selectedArray).map(d => slides.indexOf(DocCast(d) ?? d))) : this.itemIndex;
 
             // before moving onto next slide, run the subroutines :)
             const currentDoc = this.childDocs[this.itemIndex];
@@ -422,7 +422,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const { activeItem } = this;
         let prevSelected = this.itemIndex;
         // Functionality for group with up
-        let didZoom = activeItem.presentation_movement;
+        let didZoom = activeItem?.presentation_movement;
         for (; prevSelected > 0 && this.childDocs[Math.max(0, prevSelected - 1)].presentation_groupWithUp; prevSelected--) {
             didZoom = didZoom === 'none' ? this.childDocs[prevSelected].presentation_movement : didZoom;
         }
@@ -452,7 +452,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 this.stopTempMedia(from.presentation_targetDoc);
             }
             // If next slide is audio / video 'Play automatically' then the next slide should be played
-            if (this.layoutDoc.presentation_status !== PresStatus.Edit && (this.targetDoc.type === DocumentType.AUDIO || this.targetDoc.type === DocumentType.VID) && this.activeItem.presentation_mediaStart === 'auto') {
+            if (this.layoutDoc.presentation_status !== PresStatus.Edit && (this.targetDoc?.type === DocumentType.AUDIO || this.targetDoc?.type === DocumentType.VID) && this.activeItem?.presentation_mediaStart === 'auto') {
                 this.startTempMedia(this.targetDoc, this.activeItem);
             }
             if (!group) this.clearSelectedArray();
@@ -516,8 +516,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const activeFrame = activeItem.config_activeFrame ?? activeItem.config_currentFrame;
         if (activeFrame !== undefined) {
             const frameTime = NumCast(activeItem.presentation_transition, 500);
-            const acontext = activeItem.config_activeFrame !== undefined ? DocCast(DocCast(activeItem.presentation_targetDoc).embedContainer) : DocCast(activeItem.presentation_targetDoc);
-            const context = DocCast(acontext)?.annotationOn ? DocCast(DocCast(acontext).annotationOn) : acontext;
+            const acontext = activeItem.config_activeFrame !== undefined ? DocCast(DocCast(activeItem?.presentation_targetDoc)?.embedContainer) : DocCast(activeItem.presentation_targetDoc);
+            const context = DocCast(acontext)?.annotationOn ? DocCast(DocCast(acontext)?.annotationOn) : acontext;
             if (context) {
                 const ffview = CollectionFreeFormView.from(DocumentView.getFirstDocumentView(context));
                 if (ffview?.childDocs) {
@@ -528,7 +528,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
         if ((pinDataTypes?.dataview && activeItem.config_data !== undefined) || (!pinDataTypes && activeItem.config_data !== undefined)) {
             bestTarget._dataTransition = `all ${transTime}ms`;
-            const fkey = Doc.LayoutFieldKey(bestTarget);
+            const fkey = Doc.LayoutDataKey(bestTarget);
             const setData = bestTargetView?.ComponentView?.setData;
             if (setData) setData(activeItem.config_data);
             else {
@@ -553,7 +553,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             }
         }
         if (pinDataTypes?.clippable || (!pinDataTypes && activeItem.config_clipWidth !== undefined)) {
-            const fkey = '_' + Doc.LayoutFieldKey(bestTarget);
+            const fkey = '_' + Doc.LayoutDataKey(bestTarget);
             if (bestTarget[fkey + '_clipWidth'] !== activeItem.config_clipWidth) {
                 bestTarget[fkey + '_clipWidth'] = activeItem.config_clipWidth;
                 changed = true;
@@ -655,7 +655,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             }
         }
         if (pinDataTypes?.dataannos || (!pinDataTypes && activeItem.config_annotations !== undefined)) {
-            const fkey = Doc.LayoutFieldKey(bestTarget);
+            const fkey = Doc.LayoutDataKey(bestTarget);
             const oldItems = DocListCast(bestTarget[fkey + '_annotations']).filter(doc => doc.layout_unrendered);
             const newItems = DocListCast(activeItem.config_annotations).map(doc => {
                 doc.hidden = false;
@@ -672,7 +672,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
         if (pinDataTypes?.poslayoutview || (!pinDataTypes && activeItem.config_pinLayoutData !== undefined)) {
             changed = true;
-            const layoutField = Doc.LayoutFieldKey(bestTarget);
+            const layoutField = Doc.LayoutDataKey(bestTarget);
             const transitioned = new Set<Doc>();
             StrListCast(activeItem.config_pinLayoutData)
                 .map(str => JSON.parse(str) as { id: string; x: number; y: number; back: string; fill: string; w: number; h: number; data: string; text: string })
@@ -740,7 +740,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const { activeItem, targetDoc } = this;
         const finished = (options: FocusViewOptions) => {
             afterNav?.(options);
-            targetDoc[Animation] = undefined;
+            targetDoc && (targetDoc[Animation] = undefined);
         };
         const selViewCache = Array.from(this.selectedArray);
         const dragViewCache = Array.from(this._dragArray);
@@ -756,7 +756,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             }
             finished(options);
         });
-        PresBox.NavigateToTarget(targetDoc, activeItem, resetSelection);
+        targetDoc && activeItem && PresBox.NavigateToTarget(targetDoc, activeItem, resetSelection);
     };
 
     static NavigateToTarget(targetDoc: Doc, activeItem: Doc, finished?: (options: FocusViewOptions) => void) {
@@ -806,42 +806,44 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     doHideBeforeAfter = () => {
         this.childDocs.forEach((doc, index) => {
-            const curDoc = Cast(doc, Doc, null);
-            const tagDoc = PresBox.targetRenderedDoc(curDoc);
-            const itemIndexes = this.getAllIndexes(this.tagDocs, curDoc);
-            let opacity = index === this.itemIndex ? 1 : undefined;
-            if (curDoc.presentation_hide) {
-                if (index !== this.itemIndex) {
-                    opacity = 1;
+            const curDoc = DocCast(doc);
+            if (curDoc) {
+                const tagDoc = PresBox.targetRenderedDoc(curDoc) ?? curDoc;
+                const itemIndexes = this.getAllIndexes(this.tagDocs, curDoc);
+                let opacity = index === this.itemIndex ? 1 : undefined;
+                if (curDoc.presentation_hide) {
+                    if (index !== this.itemIndex) {
+                        opacity = 1;
+                    }
                 }
-            }
-            const hidingIndBef = itemIndexes.find(item => item >= this.itemIndex) ?? itemIndexes.slice().reverse().lastElement();
-            if (curDoc.presentation_hideBefore && index === hidingIndBef) {
-                if (index > this.itemIndex) {
-                    opacity = 0;
-                } else if (index === this.itemIndex || !curDoc.presentation_hideAfter) {
-                    opacity = 1;
+                const hidingIndBef = itemIndexes.find(item => item >= this.itemIndex) ?? itemIndexes.slice().reverse().lastElement();
+                if (curDoc.presentation_hideBefore && index === hidingIndBef) {
+                    if (index > this.itemIndex) {
+                        opacity = 0;
+                    } else if (index === this.itemIndex || !curDoc.presentation_hideAfter) {
+                        opacity = 1;
+                    }
                 }
-            }
-            const hidingIndAft =
-                itemIndexes
-                    .slice()
-                    .reverse()
-                    .find(item => item <= this.itemIndex) ?? itemIndexes.lastElement();
-            if (curDoc.presentation_hideAfter && index === hidingIndAft) {
-                if (index < this.itemIndex) {
-                    opacity = 0;
-                } else if (index === this.itemIndex || !curDoc.presentation_hideBefore) {
-                    opacity = 1;
+                const hidingIndAft =
+                    itemIndexes
+                        .slice()
+                        .reverse()
+                        .find(item => item <= this.itemIndex) ?? itemIndexes.lastElement();
+                if (curDoc.presentation_hideAfter && index === hidingIndAft) {
+                    if (index < this.itemIndex) {
+                        opacity = 0;
+                    } else if (index === this.itemIndex || !curDoc.presentation_hideBefore) {
+                        opacity = 1;
+                    }
                 }
-            }
-            const hidingInd = itemIndexes.find(item => item === this.itemIndex);
-            if (curDoc.presentation_hide && index === hidingInd) {
-                if (index === this.itemIndex) {
-                    opacity = 0;
+                const hidingInd = itemIndexes.find(item => item === this.itemIndex);
+                if (curDoc.presentation_hide && index === hidingInd) {
+                    if (index === this.itemIndex) {
+                        opacity = 0;
+                    }
                 }
+                opacity !== undefined && (tagDoc.opacity = opacity === 1 ? undefined : opacity);
             }
-            opacity !== undefined && (tagDoc.opacity = opacity === 1 ? undefined : opacity);
         });
     };
 
@@ -940,7 +942,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     initializePresState = (startIndex: number) => {
         this.childDocs.forEach((doc, index) => {
-            const tagDoc = PresBox.targetRenderedDoc(doc);
+            const tagDoc = PresBox.targetRenderedDoc(doc) ?? doc;
             if (doc.presentation_hideBefore && index > startIndex) tagDoc.opacity = 0;
             if (doc.presentation_hideAfter && index < startIndex) tagDoc.opacity = 0;
             if (doc.presentation_indexed !== undefined && index >= startIndex) {
@@ -969,7 +971,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this.layoutDoc.presentation_status = PresStatus.Autoplay;
             this.initializePresState(startIndex);
             const func = () => {
-                const delay = NumCast(this.activeItem.presentation_duration, this.activeItem.type === DocumentType.SCRIPTING ? 0 : 2500) + NumCast(this.activeItem.presentation_transition);
+                const delay = NumCast(this.activeItem?.presentation_duration, this.activeItem?.type === DocumentType.SCRIPTING ? 0 : 2500) + NumCast(this.activeItem?.presentation_transition);
                 this._presTimer = setTimeout(() => {
                     if (this.next() === false) this.layoutDoc.presentation_status = this._exitTrail?.() ?? PresStatus.Manual;
                     this.layoutDoc.presentation_status === PresStatus.Autoplay && func();
@@ -1060,7 +1062,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return !results.some(r => !r);
     };
 
-    childLayoutTemplate = () => Docs.Create.PresElementBoxDocument();
+    childLayoutTemplate = () => Docs.Create.PresSlideDocument();
     removeDocument = (doc: Doc | Doc[]) =>
         !toList(doc)
             .map(d => Doc.RemoveDocFromList(this.Document, this.fieldKey, d))
@@ -1077,13 +1079,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
      */
     @computed get listOfSelected() {
         return Array.from(this.selectedArray).map((doc, index) => {
-            const curDoc = Cast(doc, Doc, null);
+            const curDoc = DocCast(doc);
+            if (!curDoc) return null;
             const tagDoc = Cast(curDoc.presentation_targetDoc, Doc, null);
             if (curDoc && curDoc === this.activeItem)
                 return (
                     <div key={doc[Id]} className="selectedList-items">
                         <b>
-                            {index + 1}. {StrCast(curDoc.title)})
+                            {index + 1}. {StrCast(curDoc.title)}
                         </b>
                     </div>
                 );
@@ -1277,7 +1280,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const presCollection = collection;
         const dv = DocumentView.getDocumentView(presCollection);
         this.childDocs.forEach((doc, index) => {
-            const tagDoc = PresBox.targetRenderedDoc(doc);
+            const tagDoc = PresBox.targetRenderedDoc(doc) ?? doc;
             const srcContext = Cast(tagDoc.embedContainer, Doc, null);
             const labelCreator = (top: number, left: number, edge: number, fontSize: number) => (
                 <div className="pathOrder" key={tagDoc.id + 'pres' + index} style={{ top, left, width: edge, height: edge, fontSize }} onClick={() => this.selectElement(doc)}>
@@ -1526,17 +1529,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     // Applies the slide transiiton settings to all docs in the array
     @undoBatch
     applyTo = (array: Doc[]) => {
-        this.updateMovement(this.activeItem.presentation_movement as PresMovement, true);
-        this.updateEffect(this.activeItem.presentation_effect as PresEffect, false, true);
-        this.updateEffect(this.activeItem.presBulletEffect as PresEffect, true, true);
-        this.updateEffectDirection(this.activeItem.presentation_effectDirection as PresEffectDirection, true);
-        const { presentation_transition: pt, presentation_duration: pd, presentation_hideBefore: ph, presentation_hideAfter: pa } = this.activeItem;
-        array.forEach(curDoc => {
-            curDoc.presentation_transition = pt;
-            curDoc.presentation_duration = pd;
-            curDoc.presentation_hideBefore = ph;
-            curDoc.presentation_hideAfter = pa;
-        });
+        if (this.activeItem) {
+            this.updateMovement(this.activeItem.presentation_movement as PresMovement, true);
+            this.updateEffect(this.activeItem.presentation_effect as PresEffect, false, true);
+            this.updateEffect(this.activeItem.presBulletEffect as PresEffect, true, true);
+            this.updateEffectDirection(this.activeItem.presentation_effectDirection as PresEffectDirection, true);
+            const { presentation_transition: pt, presentation_duration: pd, presentation_hideBefore: ph, presentation_hideAfter: pa } = this.activeItem;
+            array.forEach(curDoc => {
+                curDoc.presentation_transition = pt;
+                curDoc.presentation_duration = pd;
+                curDoc.presentation_hideBefore = ph;
+                curDoc.presentation_hideAfter = pa;
+            });
+        }
     };
 
     @computed get visibilityDurationDropdown() {
@@ -1673,15 +1678,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 onClick={() => {
                                     activeItem.presentation_indexed = activeItem.presentation_indexed === undefined ? 0 : undefined;
                                     activeItem.presentation_hideBefore = activeItem.presentation_indexed !== undefined;
-                                    const tagDoc = PresBox.targetRenderedDoc(this.activeItem);
+                                    const tagDoc = PresBox.targetRenderedDoc(activeItem) ?? activeItem;
                                     const type = DocCast(tagDoc?.annotationOn)?.type ?? tagDoc.type;
                                     activeItem.presentation_indexedStart = type === DocumentType.COL ? 1 : 0;
                                     // a progressivized slide doesn't have sub-slides, but rather iterates over the data list of the target being progressivized.
                                     // to avoid creating a new slide to correspond to each of the target's data list, we create a computedField to refernce the target's data list.
-                                    let dataField = Doc.LayoutFieldKey(tagDoc);
+                                    let dataField = Doc.LayoutDataKey(tagDoc);
                                     if (Cast(tagDoc[dataField], listSpec(Doc), null)?.filter(d => d instanceof Doc) === undefined) dataField += '_annotations';
 
-                                    if (DocCast(activeItem.presentation_targetDoc).annotationOn) activeItem.data = ComputedField.MakeFunction(`this.presentation_targetDoc.annotationOn?.["${dataField}"]`);
+                                    if (DocCast(activeItem.presentation_targetDoc)?.annotationOn) activeItem.data = ComputedField.MakeFunction(`this.presentation_targetDoc.annotationOn?.["${dataField}"]`);
                                     else activeItem.data = ComputedField.MakeFunction(`this.presentation_targetDoc?.["${dataField}"]`);
                                 }}>
                                 Enable
@@ -1797,12 +1802,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 onClick={() => {
                                     this.updateEffect(elem.effect, false);
                                     this.updateEffectDirection(elem.direction);
-                                    this.updateEffectTiming(this.activeItem, {
-                                        type: SpringType.CUSTOM,
-                                        stiffness: elem.stiffness,
-                                        damping: elem.damping,
-                                        mass: elem.mass,
-                                    });
+                                    this.activeItem &&
+                                        this.updateEffectTiming(this.activeItem, {
+                                            type: SpringType.CUSTOM,
+                                            stiffness: elem.stiffness,
+                                            damping: elem.damping,
+                                            mass: elem.mass,
+                                        });
                                 }}>
                                 <SlideEffect dir={elem.direction} presEffect={elem.effect} springSettings={elem} infinite>
                                     <div className="presBox-effect-demo-box" style={{ backgroundColor: springPreviewColors[i] }} />
@@ -1947,8 +1953,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                     formLabelPlacement="left"
                                     closeOnSelect
                                     items={easeItems}
-                                    selectedVal={this.activeItem.presentation_easeFunc ? (StrCast(this.activeItem.presentation_easeFunc).startsWith('cubic') ? 'custom' : StrCast(this.activeItem.presentation_easeFunc)) : 'ease'}
-                                    setSelectedVal={val => typeof val === 'string' && this.setEaseFunc(this.activeItem, val !== 'custom' ? val : TIMING_DEFAULT_MAPPINGS.ease)}
+                                    selectedVal={this.activeItem?.presentation_easeFunc ? (StrCast(this.activeItem.presentation_easeFunc).startsWith('cubic') ? 'custom' : StrCast(this.activeItem.presentation_easeFunc)) : 'ease'}
+                                    setSelectedVal={val => typeof val === 'string' && this.activeItem && this.setEaseFunc(this.activeItem, val !== 'custom' ? val : TIMING_DEFAULT_MAPPINGS.ease)}
                                     dropdownType={DropdownType.SELECT}
                                     type={Type.TERT}
                                 />
@@ -2145,9 +2151,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @computed get mediaOptionsDropdown() {
         const { activeItem } = this;
         if (activeItem && this.targetDoc) {
-            const renderTarget = PresBox.targetRenderedDoc(this.activeItem);
+            const renderTarget = PresBox.targetRenderedDoc(this.activeItem ?? this.targetDoc) ?? this.targetDoc;
             const clipStart = NumCast(renderTarget.clipStart);
-            const clipEnd = NumCast(renderTarget.clipEnd, clipStart + NumCast(renderTarget[Doc.LayoutFieldKey(renderTarget) + '_duration']));
+            const clipEnd = NumCast(renderTarget.clipEnd, clipStart + NumCast(renderTarget[Doc.LayoutDataKey(renderTarget) + '_duration']));
             const configClipEnd = NumCast(activeItem.config_clipEnd) < NumCast(activeItem.config_clipStart) ? clipEnd - clipStart : NumCast(activeItem.config_clipEnd);
             return (
                 <div className="presBox-ribbon" onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
@@ -2699,7 +2705,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const presEnd =
             !this.layoutDoc.presLoop &&
             this.itemIndex === this.childDocs.length - 1 &&
-            (this.activeItem.presentation_indexed === undefined || NumCast(this.activeItem.presentation_indexed) === (this.progressivizedItems(this.activeItem)?.length ?? 0));
+            (this.activeItem?.presentation_indexed === undefined || NumCast(this.activeItem.presentation_indexed) === (this.progressivizedItems(this.activeItem)?.length ?? 0));
         const presStart: boolean = !this.layoutDoc.presLoop && this.itemIndex === 0;
         const inOverlay = Doc.IsInMyOverlay(this.Document);
         // Case 1: There are still other frames and should go through all frames before going to next slide
@@ -2892,7 +2898,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const presEnd =
             !this.layoutDoc.presLoop &&
             this.itemIndex === this.childDocs.length - 1 &&
-            (this.activeItem.presentation_indexed === undefined || NumCast(this.activeItem.presentation_indexed) === (this.progressivizedItems(this.activeItem)?.length ?? 0));
+            (this.activeItem?.presentation_indexed === undefined || NumCast(this.activeItem.presentation_indexed) === (this.progressivizedItems(this.activeItem)?.length ?? 0));
         const presStart = !this.layoutDoc.presLoop && this.itemIndex === 0;
         return this._props.addDocTab === returnFalse ? ( // bcz: hack!! - addDocTab === returnFalse only when this is being rendered by the OverlayView which means the doc is a mini player
             <div
@@ -2984,16 +2990,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             />
                         ) : null}
                     </div>
-                    {/* {
-                        // if the document type is a presentation, then the collection stacking view has a "+ new slide" button at the bottom of the stack
-                        <Tooltip title={<div className="dash-tooltip">{'Click on document to pin to presentaiton or make a marquee selection to pin your desired view'}</div>}>
-                            <button className="add-slide-button" onPointerDown={this.startMarqueeCreateSlide}>
-                                + NEW SLIDE
-                            </button>
-                        </Tooltip>
-                    } */}
                 </div>
-                {/* presbox chatbox */}
                 {this._chatActive && <div className="presBox-chatbox" />}
             </div>
         );
diff --git a/src/client/views/nodes/trails/PresElementBox.scss b/src/client/views/nodes/trails/PresElementBox.scss
deleted file mode 100644
index 9ac2b5a94..000000000
--- a/src/client/views/nodes/trails/PresElementBox.scss
+++ /dev/null
@@ -1,308 +0,0 @@
-$light-blue: #aeddf8;
-$dark-blue: #5b9fdd;
-$light-background: #ececec;
-$slide-background: #d5dce2;
-$slide-active: #5b9fdd;
-
-.testingv2 {
-    background-color: red;
-}
-
-.presItem-container {
-    cursor: grab;
-    display: flex;
-    grid-template-columns: 20px auto;
-    font-family: Roboto;
-    letter-spacing: normal;
-    position: relative;
-    pointer-events: all;
-    width: 100%;
-    height: 100%;
-    font-weight: 400;
-    -webkit-touch-callout: none;
-    -webkit-user-select: none;
-    -khtml-user-select: none;
-    -moz-user-select: none;
-    -ms-user-select: none;
-    user-select: none;
-    align-items: center;
-
-    //     .presItem-number {
-    //         margin-top: 3.5px;
-    //         font-size: 12px;
-    //         font-weight: 700;
-    //         text-align: center;
-    //         justify-self: center;
-    //         align-self: flex-start;
-    //         position: relative;
-    //         display: inline-block;
-    //         overflow: hidden;
-    //     }
-}
-
-.presItem-slide {
-    position: relative;
-    height: 100%;
-    width: 100%;
-    border-bottom: 0.5px solid grey;
-    display: flex;
-    align-items: center;
-
-    .presItem-number {
-        cursor: pointer;
-        &:hover {
-            background-color: $light-blue;
-        }
-    }
-    .presItem-name {
-        display: flex;
-        min-width: 20px;
-        z-index: 300;
-        top: 2px;
-        align-self: center;
-        font-size: 11px;
-        font-family: Roboto;
-        font-weight: 500;
-        position: relative;
-        padding-left: 10px;
-        padding-right: 10px;
-        letter-spacing: normal;
-        width: max-content;
-        text-overflow: ellipsis;
-        overflow: hidden;
-        white-space: pre;
-    }
-
-    .presItem-docName {
-        min-width: 20px;
-        z-index: 300;
-        align-self: center;
-        font-size: 9px;
-        font-family: Roboto;
-        font-weight: 300;
-        position: relative;
-        padding-left: 10px;
-        padding-right: 10px;
-        letter-spacing: normal;
-        width: max-content;
-        text-overflow: ellipsis;
-        overflow: hidden;
-        white-space: pre;
-        grid-row: 2;
-        grid-column: 1/6;
-    }
-
-    .presItem-time {
-        align-self: center;
-        position: relative;
-        padding-right: 10px;
-        top: 1px;
-        font-size: 10;
-        font-weight: 300;
-        font-family: Roboto;
-        z-index: 300;
-        letter-spacing: normal;
-    }
-
-    .presItem-embedded {
-        overflow: hidden;
-        grid-row: 3;
-        grid-column: 1/8;
-        position: relative;
-        display: inline-block;
-    }
-
-    .presItem-embeddedMask {
-        width: 100%;
-        height: 100%;
-        position: absolute;
-        border-radius: 3px;
-        top: 0;
-        left: 0;
-        z-index: 1;
-        overflow: hidden;
-    }
-
-    .presItem-slideButtons {
-        display: flex;
-        position: absolute;
-        width: max-content;
-        justify-self: right;
-        justify-content: flex-end;
-
-        .slideButton {
-            cursor: pointer;
-            position: relative;
-            border-radius: 100px;
-            z-index: 300;
-            width: 18px;
-            height: 18px;
-            display: flex;
-            font-size: 12px;
-            justify-self: center;
-            align-self: center;
-            background-color: rgba(0, 0, 0, 0.5);
-            color: white;
-            justify-content: center;
-            align-items: center;
-            transition: 0.2s;
-            margin-right: 3px;
-        }
-
-        .slideButton:hover {
-            background-color: rgba(0, 0, 0, 1);
-            transform: scale(1.2);
-        }
-    }
-}
-
-// .presItem-slide:hover {
-//     .presItem-slideButtons {
-//         display: flex;
-//         grid-column: 7;
-//         grid-row: 1/3;
-//         width: max-content;
-//         justify-self: right;
-//         justify-content: flex-end;
-
-//         .slideButton {
-//             cursor: pointer;
-//             position: relative;
-//             border-radius: 100px;
-//             z-index: 300;
-//             width: 18px;
-//             height: 18px;
-//             display: flex;
-//             font-size: 12px;
-//             justify-self: center;
-//             align-self: center;
-//             background-color: rgba(0, 0, 0, 0.5);
-//             color: white;
-//             justify-content: center;
-//             align-items: center;
-//             transition: 0.2s;
-//             margin-right: 3px;
-//         }
-
-//         .slideButton:hover {
-//             background-color: rgba(0, 0, 0, 1);
-//             transform: scale(1.2);
-//         }
-//     }
-// }
-
-.presItem-slide.active {
-    //box-shadow: 0 0 0px 2.5px $dark-blue;
-    border: $dark-blue solid 2.5px;
-}
-
-.presItem-slide.group {
-    border-radius: 5px;
-}
-
-.presItem-slide.activeGroup {
-    border-radius: 5px;
-    box-shadow: 0 0 0px 2.5px $dark-blue;
-}
-
-.presItem-groupSlideContainer {
-    position: absolute;
-    /* grid-row: 3; */
-    /* grid-column: 1/8; */
-    top: 28;
-    display: block;
-    background: #92adb9;
-    width: 100%;
-    height: 10px;
-    border-radius: 0px 0px 5px 5px;
-    height: calc(100% - 28px);
-    display: grid;
-    grid-template-rows: auto auto auto;
-    grid-template-columns: 100%;
-    justify-items: left;
-    align-items: center;
-}
-
-.presItem-groupSlide {
-    position: relative;
-    background-color: #d5dce2;
-    border-radius: 5px;
-    height: calc(100% - 7px);
-    width: calc(100% - 20px);
-    left: 15px;
-    /* height: 20px; */
-    /* width: calc(100% - 19px); */
-    display: flex;
-    grid-template-rows: 16px 10px auto;
-    grid-template-columns: max-content max-content max-content max-content auto;
-}
-
-.presItem-multiDrag {
-    font-family: Roboto;
-    font-weight: 600;
-    color: white;
-    text-align: center;
-    justify-content: center;
-    align-content: center;
-    width: 100px;
-    height: 30px;
-    position: absolute;
-    background-color: $dark-blue;
-    z-index: 4000;
-    border-radius: 10px;
-    box-shadow: black 0.4vw 0.4vw 0.8vw;
-    line-height: 30px;
-}
-
-.presItem-miniSlide {
-    font-weight: 700;
-    font-size: 12;
-    grid-column: 1/8;
-    align-self: center;
-    justify-self: center;
-    background-color: #d5dce2;
-    width: 26px;
-    text-align: center;
-    height: 26px;
-    line-height: 28px;
-    border-radius: 100%;
-}
-
-.presItem-miniSlide.active {
-    box-shadow: 0 0 0px 1.5px $dark-blue;
-}
-
-.expandButton {
-    cursor: pointer;
-    position: absolute;
-    border-radius: 100px;
-    bottom: 0;
-    left: -18;
-    z-index: 300;
-    width: 15px;
-    height: 15px;
-    display: flex;
-    font-size: 12px;
-    justify-self: center;
-    align-self: center;
-    background-color: #92adb9;
-    color: white;
-    justify-content: center;
-    align-items: center;
-    transition: 0.2s;
-    margin-right: 3px;
-}
-
-.expandButton:hover {
-    background-color: rgba(0, 0, 0, 1);
-    transform: scale(1.2);
-}
-
-.presItem-groupNum {
-    color: #d5dce2;
-    position: absolute;
-    left: -15px;
-    top: 1;
-    font-weight: 600;
-    font-size: 12;
-}
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
deleted file mode 100644
index 7e0375275..000000000
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ /dev/null
@@ -1,628 +0,0 @@
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Tooltip } from '@mui/material';
-import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import { returnFalse, returnTrue, setupMoveUpEvents } from '../../../../ClientUtils';
-import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
-import { Id } from '../../../../fields/FieldSymbols';
-import { List } from '../../../../fields/List';
-import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
-import { emptyFunction } from '../../../../Utils';
-import { Docs } from '../../../documents/Documents';
-import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
-import { DragManager } from '../../../util/DragManager';
-import { SnappingManager } from '../../../util/SnappingManager';
-import { Transform } from '../../../util/Transform';
-import { undoable, undoBatch } from '../../../util/UndoManager';
-import { TreeView } from '../../collections/TreeView';
-import { ViewBoxBaseComponent } from '../../DocComponent';
-import { EditableView } from '../../EditableView';
-import { Colors } from '../../global/globalEnums';
-import { PinDocView } from '../../PinFuncs';
-import { StyleProp } from '../../StyleProp';
-import { returnEmptyDocViewList } from '../../StyleProvider';
-import { DocumentView } from '../DocumentView';
-import { FieldView, FieldViewProps } from '../FieldView';
-import { PresBox } from './PresBox';
-import './PresElementBox.scss';
-import { PresMovement } from './PresEnums';
-/**
- * This class models the view a document added to presentation will have in the presentation.
- * It involves some functionality for its buttons and options.
- */
-@observer
-export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
-    public static LayoutString(fieldKey: string) {
-        return FieldView.LayoutString(PresElementBox, fieldKey);
-    }
-    private _itemRef: React.RefObject<HTMLDivElement> = React.createRef();
-    private _dragRef: React.RefObject<HTMLDivElement> = React.createRef();
-    private _titleRef: React.RefObject<EditableView> = React.createRef();
-    private _heightDisposer: IReactionDisposer | undefined;
-    readonly expandViewHeight = 100;
-    readonly collapsedHeight = 35;
-
-    constructor(props: FieldViewProps) {
-        super(props);
-        makeObservable(this);
-    }
-
-    @observable _dragging = false;
-
-    // the presentation view that renders this slide
-    @computed get presBoxView() {
-        return this.DocumentView?.()
-            .containerViewPath?.()
-            .slice()
-            .reverse()
-            .find(dv => dv?.ComponentView instanceof PresBox)?.ComponentView as PresBox;
-    }
-
-    // the presentation view document that renders this slide
-    @computed get presBox() {
-        return this.presBoxView?.Document;
-    }
-
-    // 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 this.rootDoc;
-    }
-
-    // this is the document in the workspaces that is targeted by the slide
-    @computed get targetDoc() {
-        return Cast(this.slideDoc.presentation_targetDoc, Doc, null) || this.slideDoc;
-    }
-
-    // computes index of this presentation slide in the presBox list
-    @computed get indexInPres() {
-        return this.presBoxView?.SlideIndex(this.slideDoc) ?? 0;
-    }
-
-    @computed get selectedArray() {
-        return this.presBoxView?.selectedArray;
-    }
-
-    @computed get videoRecordingIsInOverlay() {
-        return Doc.MyOverlayDocs.some(doc => doc.slides === this.slideDoc);
-    }
-
-    componentDidMount() {
-        this.layoutDoc.layout_hideLinkButton = true;
-        this._heightDisposer = reaction(
-            () => ({ expand: this.slideDoc.presentation_expandInlineButton, height: this.collapsedHeight }),
-            ({ expand, height }) => {
-                this.layoutDoc._height = height + (expand ? this.expandViewHeight : 0);
-            },
-            { fireImmediately: true }
-        );
-    }
-    componentWillUnmount() {
-        this._heightDisposer?.();
-    }
-
-    presExpandDocumentClick = () => {
-        this.slideDoc.presentation_expandInlineButton = !this.slideDoc.presentation_expandInlineButton;
-    };
-    embedHeight = () => this.collapsedHeight + this.expandViewHeight;
-    embedWidth = () => this._props.PanelWidth() / 2;
-    // prettier-ignore
-    styleProvider = ( doc: Doc | undefined, props: Opt<FieldViewProps>, property: string ) => 
-        (property === StyleProp.Opacity ? 1 : this._props.styleProvider?.(doc, props, property));
-    /**
-     * The function that is responsible for rendering a preview or not for this
-     * presentation element.
-     */
-    @computed get renderEmbeddedInline() {
-        return !this.slideDoc.presentation_expandInlineButton || !this.targetDoc ? null : (
-            <div className="presItem-embedded" style={{ height: this.embedHeight(), width: '50%' }}>
-                <DocumentView
-                    Document={PresBox.targetRenderedDoc(this.slideDoc)}
-                    PanelWidth={this.embedWidth}
-                    PanelHeight={this.embedHeight}
-                    isContentActive={this._props.isContentActive}
-                    styleProvider={this.styleProvider}
-                    hideLinkButton
-                    ScreenToLocalTransform={Transform.Identity}
-                    renderDepth={this._props.renderDepth + 1}
-                    containerViewPath={returnEmptyDocViewList}
-                    childFilters={this._props.childFilters}
-                    childFiltersByRanges={this._props.childFiltersByRanges}
-                    searchFilterDocs={this._props.searchFilterDocs}
-                    addDocument={returnFalse}
-                    removeDocument={returnFalse}
-                    fitContentsToBox={returnTrue}
-                    moveDocument={this._props.moveDocument!}
-                    focus={emptyFunction}
-                    whenChildContentsActiveChanged={returnFalse}
-                    addDocTab={returnFalse}
-                    pinToPres={returnFalse}
-                />
-            </div>
-        );
-    }
-
-    @computed get renderGroupSlides() {
-        const childDocs = DocListCast(this.targetDoc.data);
-        const groupSlides = childDocs.map((doc: Doc, ind: number) => (
-            <div
-                key={doc[Id]}
-                className="presItem-groupSlide"
-                onClick={e => {
-                    e.stopPropagation();
-                    e.preventDefault();
-                    this.presBoxView?.modifierSelect(doc, this._itemRef.current!, this._dragRef.current!, e.shiftKey || e.ctrlKey || e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey);
-                    this.presExpandDocumentClick();
-                }}>
-                <div className="presItem-groupNum">{`${ind + 1}.`}</div>
-                <div className="presItem-name">
-                    <EditableView
-                        ref={this._titleRef}
-                        editing={undefined}
-                        contents={StrCast(doc.title)}
-                        overflow="ellipsis"
-                        GetValue={() => StrCast(doc.title)}
-                        SetValue={(value: string) => {
-                            doc.title = !value.trim().length ? '-untitled-' : value;
-                            return true;
-                        }}
-                    />
-                </div>
-            </div>
-        ));
-        return groupSlides;
-    }
-
-    @computed get transition() {
-        let transitionInS: number;
-        if (this.slideDoc.presentation_transition) transitionInS = NumCast(this.slideDoc.presentation_transition) / 1000;
-        else transitionInS = 0.5;
-        return this.slideDoc.presentation_movement === PresMovement.Jump || this.slideDoc.presentation_movement === PresMovement.None ? null : 'M: ' + transitionInS + 's';
-    }
-
-    @action
-    headerDown = (e: React.PointerEvent<HTMLDivElement>) => {
-        const element = e.target as HTMLDivElement;
-        e.stopPropagation();
-        e.preventDefault();
-        if (element && !(e.ctrlKey || e.metaKey || e.button === 2)) {
-            this.presBoxView?.regularSelect(this.slideDoc, this._itemRef.current!, this._dragRef.current!, true, false);
-            setupMoveUpEvents(this, e, this.startDrag, emptyFunction, clickEv => {
-                clickEv.stopPropagation();
-                clickEv.preventDefault();
-                this.presBoxView?.modifierSelect(this.slideDoc, this._itemRef.current!, this._dragRef.current!, clickEv.shiftKey || clickEv.ctrlKey || clickEv.metaKey, clickEv.ctrlKey || clickEv.metaKey, clickEv.shiftKey);
-                this.presBoxView?.activeItem && this.showRecording(this.presBoxView?.activeItem);
-            });
-        }
-    };
-
-    /**
-     * Function to drag and drop the pres element to a diferent location
-     */
-    startDrag = (e: PointerEvent) => {
-        this.presBoxView?.regularSelect(this.slideDoc, this._itemRef.current!, this._dragRef.current!, true, false);
-        const miniView: boolean = this.toolbarWidth <= 100;
-        const activeItem = this.slideDoc;
-        const dragArray = this.presBoxView?._dragArray ?? [];
-        const dragData = new DragManager.DocumentDragData(this.presBoxView?.sortArray() ?? []);
-        if (!dragData.draggedDocuments.length) dragData.draggedDocuments.push(this.slideDoc);
-        dragData.treeViewDoc = this.presBox?._type_collection === CollectionViewType.Tree ? this.presBox : undefined; // this.DocumentView?.()?._props.treeViewDoc;
-        dragData.moveDocument = this._props.moveDocument;
-        const dragItem: HTMLElement[] = [];
-        const classesToRestore = new Map<HTMLElement, string>();
-        if (dragArray.length === 1) {
-            const doc = this._itemRef.current || dragArray[0];
-            if (doc) {
-                classesToRestore.set(doc, doc.className);
-                doc.className = miniView ? 'presItem-miniSlide' : 'presItem-slide';
-                dragItem.push(doc);
-            }
-        } else if (dragArray.length >= 1) {
-            const doc = document.createElement('div');
-            doc.className = 'presItem-multiDrag';
-            doc.innerText = 'Move ' + (this.selectedArray?.size ?? 0) + ' slides';
-            doc.style.position = 'absolute';
-            doc.style.top = e.clientY + 'px';
-            doc.style.left = e.clientX - 50 + 'px';
-            dragItem.push(doc);
-        }
-
-        if (activeItem) {
-            runInAction(() => {
-                this._dragging = true;
-            });
-            DragManager.StartDocumentDrag(
-                dragItem.map(ele => ele),
-                dragData,
-                e.clientX,
-                e.clientY,
-                undefined,
-                action(() => {
-                    Array.from(classesToRestore).forEach(pair => (pair[0].className = pair[1]));
-                    this._dragging = false;
-                })
-            );
-            return true;
-        }
-        return false;
-    };
-
-    onPointerOver = () => {
-        document.removeEventListener('pointermove', this.onPointerMove);
-        document.addEventListener('pointermove', this.onPointerMove);
-    };
-
-    onPointerMove = (e: PointerEvent) => {
-        const slide = this._itemRef.current;
-        const dragIsPresItem = DragManager.docsBeingDragged.some(d => d.presentation_targetDoc);
-        if (slide && dragIsPresItem) {
-            const rect = slide.getBoundingClientRect();
-            const y = e.clientY - rect.top; // y position within the element.
-            const height = slide.clientHeight;
-            const halfLine = height / 2;
-            if (y <= halfLine) {
-                slide.style.borderTop = `solid 2px ${Colors.MEDIUM_BLUE}`;
-                slide.style.borderBottom = '0px';
-            } else if (y > halfLine) {
-                slide.style.borderTop = '0px';
-                slide.style.borderBottom = `solid 2px ${Colors.MEDIUM_BLUE}`;
-            }
-        }
-        document.removeEventListener('pointermove', this.onPointerMove);
-    };
-
-    onPointerLeave = () => {
-        const slide = this._itemRef.current;
-        if (slide) {
-            slide.style.borderTop = '0px';
-            slide.style.borderBottom = '0px';
-        }
-        document.removeEventListener('pointermove', this.onPointerMove);
-    };
-
-    @action
-    toggleProperties = () => {
-        if (SnappingManager.PropertiesWidth < 5) {
-            SnappingManager.SetPropertiesWidth(250);
-        }
-    };
-
-    removePresentationItem = undoable(
-        action((e: React.MouseEvent) => {
-            e.stopPropagation();
-            if (this.presBox && this.indexInPres < (this.presBoxView?.itemIndex || 0)) {
-                this.presBox.itemIndex = (this.presBoxView?.itemIndex || 0) - 1;
-            }
-            this._props.removeDocument?.(this.slideDoc);
-            this.presBoxView?.removeFromSelectedArray(this.slideDoc);
-            this.removeAllRecordingInOverlay();
-        }),
-        'Remove doc from pres trail'
-    );
-
-    // set title of the individual pres slide
-    onSetValue = undoable(
-        action((value: string) => {
-            this.slideDoc.title = !value.trim().length ? '-untitled-' : value;
-            return true;
-        }),
-        'set title of pres element'
-    );
-
-    /**
-     * Method called for updating the view of the currently selected document
-     *
-     * @param targetDoc
-     * @param activeItem
-     */
-    @undoBatch
-    updateCapturedContainerLayout = (presTargetDoc: Doc, activeItem: Doc) => {
-        const targetDoc = DocCast(presTargetDoc.annotationOn) ?? presTargetDoc;
-        activeItem.config_x = NumCast(targetDoc.x);
-        activeItem.config_y = NumCast(targetDoc.y);
-        activeItem.config_rotation = NumCast(targetDoc.rotation);
-        activeItem.config_width = NumCast(targetDoc.width);
-        activeItem.config_height = NumCast(targetDoc.height);
-        activeItem.config_pinLayout = !activeItem.config_pinLayout;
-        // activeItem.config_pinLayout = true;
-    };
-
-    /**
-     * Method called for updating the view of the currently selected document
-     *
-     * @param presTargetDoc
-     * @param activeItem
-     */
-    updateCapturedViewContents = undoable(
-        action((presTargetDoc: Doc, activeItem: Doc) => {
-            const target = DocCast(presTargetDoc.annotationOn) ?? presTargetDoc;
-            PinDocView(activeItem, { pinData: PresBox.pinDataTypes(target) }, target);
-        }),
-        'updated captured view contents'
-    );
-
-    // a previously recorded video will have timecode defined
-    static videoIsRecorded = (activeItem: Opt<Doc>) => 'layout_currentTimecode' in (DocCast(activeItem?.recording) ?? {});
-
-    removeAllRecordingInOverlay = () => Doc.MyOverlayDocs.filter(doc => doc.slides === this.slideDoc).forEach(Doc.RemFromMyOverlay);
-
-    /// remove all videos that have been recorded from overlay (leave videso that are being recorded to avoid losing data)
-    static removeEveryExistingRecordingInOverlay = () => {
-        Doc.MyOverlayDocs.filter(doc => doc.slides !== null && PresElementBox.videoIsRecorded(DocCast(doc.slides))) //
-            .forEach(Doc.RemFromMyOverlay);
-    };
-
-    hideRecording = undoable(
-        action((e: React.MouseEvent) => {
-            e.stopPropagation();
-            this.removeAllRecordingInOverlay();
-        }),
-        'hide video recording'
-    );
-
-    showRecording = undoable(
-        action((activeItem: Doc, iconClick: boolean = false) => {
-            // remove the overlays on switch *IF* not opened from the specific icon
-            if (!iconClick) PresElementBox.removeEveryExistingRecordingInOverlay();
-
-            activeItem.recording && Doc.AddToMyOverlay(DocCast(activeItem.recording));
-        }),
-        'show video recording'
-    );
-
-    startRecording = undoable(
-        action((e: React.MouseEvent, activeItem: Doc) => {
-            e.stopPropagation();
-            if (PresElementBox.videoIsRecorded(activeItem)) {
-                // if we already have an existing recording
-                this.showRecording(activeItem, true);
-                //   // if we already have an existing recording
-                //   Doc.AddToMyOverlay(Cast(activeItem.recording, Doc, null));
-            } else {
-                // we dont have any recording
-                // Remove every recording that already exists in overlay view
-                // this is a design decision to clear to focus in on the recoding mode
-                PresElementBox.removeEveryExistingRecordingInOverlay();
-
-                // create and add a recording to the slide
-                // make recording box appear in the bottom right corner of the screen
-                Doc.AddToMyOverlay(
-                    (activeItem.recording = Docs.Create.WebCamDocument('', {
-                        _width: 384,
-                        _height: 216,
-                        overlayX: window.innerWidth - 384 - 20,
-                        overlayY: window.innerHeight - 216 - 20,
-                        layout_hideDocumentButtonBar: true,
-                        layout_hideDecorationTitle: true,
-                        layout_hideOpenButton: true,
-                        cloneFieldFilter: new List<string>(['isSystem']),
-                        slides: activeItem, // attach the slide to the recording
-                    }))
-                );
-            }
-        }),
-        'start video recording'
-    );
-
-    @undoBatch
-    lfg = (e: React.MouseEvent) => {
-        e.stopPropagation();
-        // TODO: fix this bug
-        // const { toggleChildrenRun } = this.slideDoc;
-        TreeView.ToggleChildrenRun.get(this.slideDoc)?.();
-
-        // call this.slideDoc.recurChildren() to get all the children
-        // if (iconClick) PresElementBox.showVideo = false;
-    };
-
-    @computed
-    get toolbarWidth(): number {
-        const presBoxDocView = DocumentView.getDocumentView(this.presBox);
-        const width = NumCast(this.presBox?._width);
-        return presBoxDocView ? presBoxDocView._props.PanelWidth() : width || 300;
-    }
-
-    @computed get presButtons() {
-        const { presBox, targetDoc, slideDoc: activeItem } = this;
-        const presBoxColor = StrCast(presBox?._backgroundColor);
-        const presColorBool = presBoxColor ? presBoxColor !== Colors.WHITE && presBoxColor !== 'transparent' : false;
-        const hasChildren = BoolCast(this.slideDoc?.hasChildren);
-
-        const items: JSX.Element[] = [];
-
-        items.push(
-            <Tooltip key="slide" title={<div className="dash-tooltip">Update captured doc layout</div>}>
-                <div
-                    className="slideButton"
-                    onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this.updateCapturedContainerLayout(targetDoc, activeItem), true)}
-                    style={{ opacity: activeItem.config_pinLayout ? 1 : 0.5, fontWeight: 700, display: 'flex' }}>
-                    L
-                </div>
-            </Tooltip>
-        );
-        items.push(
-            <Tooltip key="flex" title={<div className="dash-tooltip">Update captured doc content</div>}>
-                <div
-                    className="slideButton"
-                    onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this.updateCapturedViewContents(targetDoc, activeItem))}
-                    style={{ opacity: activeItem.config_pinData || activeItem.config_pinView ? 1 : 0.5, fontWeight: 700, display: 'flex' }}>
-                    C
-                </div>
-            </Tooltip>
-        );
-        items.push(
-            <Tooltip key="slash" title={<div className="dash-tooltip">{this.videoRecordingIsInOverlay ? 'Hide Recording' : `${PresElementBox.videoIsRecorded(activeItem) ? 'Show' : 'Start'} recording`}</div>}>
-                <div className="slideButton" onClick={e => (this.videoRecordingIsInOverlay ? this.hideRecording(e) : this.startRecording(e, activeItem))} style={{ fontWeight: 700 }}>
-                    <FontAwesomeIcon icon={`video${this.videoRecordingIsInOverlay ? '-slash' : ''}`} onPointerDown={e => e.stopPropagation()} />
-                </div>
-            </Tooltip>
-        );
-        if (this.indexInPres !== 0) {
-            items.push(
-                <Tooltip
-                    key="arrow"
-                    title={
-                        <div className="dash-tooltip">
-                            {!activeItem.presentation_groupWithUp
-                                ? 'Not grouped with previous slide (click to group)'
-                                : activeItem.presentation_groupWithUp === 1
-                                  ? 'Run simultaneously with previous slide (click again to run after)'
-                                  : 'Run after previous slide (click to ungroup from previous)'}
-                        </div>
-                    }>
-                    <div
-                        className="slideButton"
-                        onClick={() => {
-                            activeItem.presentation_groupWithUp = (NumCast(activeItem.presentation_groupWithUp) + 1) % 3;
-                        }}
-                        style={{
-                            zIndex: 1000 - this.indexInPres,
-                            fontWeight: 700,
-                            backgroundColor: activeItem.presentation_groupWithUp ? (presColorBool ? presBoxColor : Colors.MEDIUM_BLUE) : undefined,
-                            outline: NumCast(activeItem.presentation_groupWithUp) > 1 ? 'solid black 1px' : undefined,
-                            height: activeItem.presentation_groupWithUp ? 53 : 18,
-                            transform: activeItem.presentation_groupWithUp ? 'translate(0, -17px)' : undefined,
-                        }}>
-                        <div style={{ transform: activeItem.presentation_groupWithUp ? 'rotate(180deg) translate(0, -17.5px)' : 'rotate(0deg)' }}>
-                            <FontAwesomeIcon icon="arrow-up" onPointerDown={e => e.stopPropagation()} />
-                        </div>
-                    </div>
-                </Tooltip>
-            );
-        }
-        items.push(
-            <Tooltip key="eye" title={<div className="dash-tooltip">{this.slideDoc.presentation_expandInlineButton ? 'Minimize' : 'Expand'}</div>}>
-                <div
-                    className="slideButton"
-                    onClick={e => {
-                        e.stopPropagation();
-                        this.presExpandDocumentClick();
-                    }}>
-                    <FontAwesomeIcon icon={this.slideDoc.presentation_expandInlineButton ? 'eye-slash' : 'eye'} onPointerDown={e => e.stopPropagation()} />
-                </div>
-            </Tooltip>
-        );
-        if (!Doc.noviceMode && hasChildren) {
-            // TODO: replace with if treeveiw, has childrenDocs
-            items.push(
-                <Tooltip key="children" title={<div className="dash-tooltip">Run child processes (tree only)</div>}>
-                    <div
-                        className="slideButton"
-                        onClick={e => {
-                            e.stopPropagation();
-                            this.lfg(e);
-                        }}
-                        style={{ fontWeight: 700 }}>
-                        <FontAwesomeIcon icon="circle-play" onPointerDown={e => e.stopPropagation()} />
-                    </div>
-                </Tooltip>
-            );
-        }
-        items.push(
-            <Tooltip key="trash" title={<div className="dash-tooltip">Remove from presentation</div>}>
-                <div className="slideButton" onClick={this.removePresentationItem}>
-                    <FontAwesomeIcon icon="trash" onPointerDown={e => e.stopPropagation()} />
-                </div>
-            </Tooltip>
-        );
-        items.push(
-            <Tooltip key="customize-slide" title={<div className="dash-tooltip">Customize Slide</div>}>
-                <div
-                    className={'slideButton'}
-                    onClick={() => {
-                        this.presBoxView?.regularSelect(this.slideDoc, this._itemRef.current!, this._dragRef.current!, true, false);
-                        PresBox.Instance.navigateToActiveItem();
-                        PresBox.Instance.openProperties();
-                        PresBox.Instance.slideToModify = this.Document;
-                    }}>
-                    <FontAwesomeIcon icon={'edit'} onPointerDown={e => e.stopPropagation()} />
-                </div>
-            </Tooltip>
-        );
-        return items;
-    }
-
-    @computed get mainItem() {
-        const { presBox, slideDoc: activeItem } = this;
-        const isSelected: boolean = !!this.selectedArray?.has(activeItem);
-        const isCurrent: boolean = this.presBox?._itemIndex === this.indexInPres;
-        const miniView: boolean = this.toolbarWidth <= 110;
-        const presBoxColor: string = StrCast(presBox?._backgroundColor);
-        const presColorBool: boolean = presBoxColor ? presBoxColor !== Colors.WHITE && presBoxColor !== 'transparent' : false;
-
-        return (
-            <div
-                className="presItem-container"
-                key={activeItem[Id] + this.indexInPres}
-                ref={this._itemRef}
-                style={{
-                    backgroundColor: presColorBool ? (isSelected ? 'rgba(250,250,250,0.3)' : 'transparent') : isSelected ? Colors.LIGHT_BLUE : 'transparent',
-                    opacity: this._dragging ? 0.3 : 1,
-                    paddingLeft: NumCast(this.layoutDoc._xPadding, this._props.xPadding),
-                    paddingRight: NumCast(this.layoutDoc._xPadding, this._props.xPadding),
-                    paddingTop: NumCast(this.layoutDoc._yPadding, this._props.yPadding),
-                    paddingBottom: NumCast(this.layoutDoc._yPadding, this._props.yPadding),
-                }}
-                onDoubleClick={action(() => {
-                    this.toggleProperties();
-                    this.presBoxView?.regularSelect(activeItem, this._itemRef.current!, this._dragRef.current!, false);
-                })}
-                onPointerOver={this.onPointerOver}
-                onPointerLeave={this.onPointerLeave}
-                onPointerDown={this.headerDown}>
-                {miniView ? (
-                    <div className={`presItem-miniSlide ${isSelected ? 'active' : ''}`} ref={this._dragRef}>
-                        {`${this.indexInPres + 1}.`}
-                    </div>
-                ) : (
-                    <div
-                        ref={this._dragRef}
-                        className={`presItem-slide ${isCurrent ? 'active' : ''}${activeItem.runProcess ? ' testingv2' : ''}`}
-                        style={{
-                            display: 'infline-block',
-                            backgroundColor: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor) as string,
-                            // layout_boxShadow: presBoxColor && presBoxColor !== 'white' && presBoxColor !== 'transparent' ? (isCurrent ? '0 0 0px 1.5px' + presBoxColor : undefined) : undefined,
-                            border: presBoxColor && presBoxColor !== 'white' && presBoxColor !== 'transparent' ? (isCurrent ? presBoxColor + ' solid 2.5px' : undefined) : undefined,
-                        }}>
-                        <div
-                            className="presItem-name"
-                            style={{
-                                display: 'inline-flex',
-                                pointerEvents: isSelected ? undefined : 'none',
-                                width: `calc(100% ${activeItem.presentation_expandInlineButton ? '- 50%' : ''} - ${this.presButtons.length * 22}px`,
-                                cursor: isSelected ? 'text' : 'grab',
-                            }}>
-                            <div
-                                className="presItem-number"
-                                title="select without navigation"
-                                onPointerDown={e => {
-                                    e.stopPropagation();
-                                    if (this._itemRef.current && this._dragRef.current) {
-                                        this.presBoxView?.modifierSelect(activeItem, this._itemRef.current, this._dragRef.current, true, false, false);
-                                    }
-                                }}
-                                onClick={e => e.stopPropagation()}>{`${this.indexInPres + 1}.  `}</div>
-                            <EditableView ref={this._titleRef} oneLine editing={!isSelected ? false : undefined} contents={StrCast(activeItem.title)} overflow="ellipsis" GetValue={() => StrCast(activeItem.title)} SetValue={this.onSetValue} />
-                        </div>
-                        {/* <Tooltip title={<><div className="dash-tooltip">{"Movement speed"}</div></>}><div className="presItem-time" style={{ display: showMore ? "block" : "none" }}>{this.transition}</div></Tooltip> */}
-                        {/* <Tooltip title={<><div className="dash-tooltip">{"Duration"}</div></>}><div className="presItem-time" style={{ display: showMore ? "block" : "none" }}>{this.duration}</div></Tooltip> */}
-                        <div className="presItem-slideButtons" style={{ position: 'absolute', right: 0 }}>
-                            {...this.presButtons}
-                        </div>
-                        {this.renderEmbeddedInline}
-                    </div>
-                )}
-            </div>
-        );
-    }
-
-    render() {
-        return !(this.slideDoc instanceof Doc) || this.targetDoc instanceof Promise ? null : this.mainItem;
-    }
-}
-
-Docs.Prototypes.TemplateMap.set(DocumentType.PRESELEMENT, {
-    layout: { view: PresElementBox, dataField: 'data' },
-    options: { acl: '', title: 'pres element template', _layout_fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: 'data' },
-});
diff --git a/src/client/views/nodes/trails/PresSlideBox.scss b/src/client/views/nodes/trails/PresSlideBox.scss
new file mode 100644
index 000000000..9ac2b5a94
--- /dev/null
+++ b/src/client/views/nodes/trails/PresSlideBox.scss
@@ -0,0 +1,308 @@
+$light-blue: #aeddf8;
+$dark-blue: #5b9fdd;
+$light-background: #ececec;
+$slide-background: #d5dce2;
+$slide-active: #5b9fdd;
+
+.testingv2 {
+    background-color: red;
+}
+
+.presItem-container {
+    cursor: grab;
+    display: flex;
+    grid-template-columns: 20px auto;
+    font-family: Roboto;
+    letter-spacing: normal;
+    position: relative;
+    pointer-events: all;
+    width: 100%;
+    height: 100%;
+    font-weight: 400;
+    -webkit-touch-callout: none;
+    -webkit-user-select: none;
+    -khtml-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+    align-items: center;
+
+    //     .presItem-number {
+    //         margin-top: 3.5px;
+    //         font-size: 12px;
+    //         font-weight: 700;
+    //         text-align: center;
+    //         justify-self: center;
+    //         align-self: flex-start;
+    //         position: relative;
+    //         display: inline-block;
+    //         overflow: hidden;
+    //     }
+}
+
+.presItem-slide {
+    position: relative;
+    height: 100%;
+    width: 100%;
+    border-bottom: 0.5px solid grey;
+    display: flex;
+    align-items: center;
+
+    .presItem-number {
+        cursor: pointer;
+        &:hover {
+            background-color: $light-blue;
+        }
+    }
+    .presItem-name {
+        display: flex;
+        min-width: 20px;
+        z-index: 300;
+        top: 2px;
+        align-self: center;
+        font-size: 11px;
+        font-family: Roboto;
+        font-weight: 500;
+        position: relative;
+        padding-left: 10px;
+        padding-right: 10px;
+        letter-spacing: normal;
+        width: max-content;
+        text-overflow: ellipsis;
+        overflow: hidden;
+        white-space: pre;
+    }
+
+    .presItem-docName {
+        min-width: 20px;
+        z-index: 300;
+        align-self: center;
+        font-size: 9px;
+        font-family: Roboto;
+        font-weight: 300;
+        position: relative;
+        padding-left: 10px;
+        padding-right: 10px;
+        letter-spacing: normal;
+        width: max-content;
+        text-overflow: ellipsis;
+        overflow: hidden;
+        white-space: pre;
+        grid-row: 2;
+        grid-column: 1/6;
+    }
+
+    .presItem-time {
+        align-self: center;
+        position: relative;
+        padding-right: 10px;
+        top: 1px;
+        font-size: 10;
+        font-weight: 300;
+        font-family: Roboto;
+        z-index: 300;
+        letter-spacing: normal;
+    }
+
+    .presItem-embedded {
+        overflow: hidden;
+        grid-row: 3;
+        grid-column: 1/8;
+        position: relative;
+        display: inline-block;
+    }
+
+    .presItem-embeddedMask {
+        width: 100%;
+        height: 100%;
+        position: absolute;
+        border-radius: 3px;
+        top: 0;
+        left: 0;
+        z-index: 1;
+        overflow: hidden;
+    }
+
+    .presItem-slideButtons {
+        display: flex;
+        position: absolute;
+        width: max-content;
+        justify-self: right;
+        justify-content: flex-end;
+
+        .slideButton {
+            cursor: pointer;
+            position: relative;
+            border-radius: 100px;
+            z-index: 300;
+            width: 18px;
+            height: 18px;
+            display: flex;
+            font-size: 12px;
+            justify-self: center;
+            align-self: center;
+            background-color: rgba(0, 0, 0, 0.5);
+            color: white;
+            justify-content: center;
+            align-items: center;
+            transition: 0.2s;
+            margin-right: 3px;
+        }
+
+        .slideButton:hover {
+            background-color: rgba(0, 0, 0, 1);
+            transform: scale(1.2);
+        }
+    }
+}
+
+// .presItem-slide:hover {
+//     .presItem-slideButtons {
+//         display: flex;
+//         grid-column: 7;
+//         grid-row: 1/3;
+//         width: max-content;
+//         justify-self: right;
+//         justify-content: flex-end;
+
+//         .slideButton {
+//             cursor: pointer;
+//             position: relative;
+//             border-radius: 100px;
+//             z-index: 300;
+//             width: 18px;
+//             height: 18px;
+//             display: flex;
+//             font-size: 12px;
+//             justify-self: center;
+//             align-self: center;
+//             background-color: rgba(0, 0, 0, 0.5);
+//             color: white;
+//             justify-content: center;
+//             align-items: center;
+//             transition: 0.2s;
+//             margin-right: 3px;
+//         }
+
+//         .slideButton:hover {
+//             background-color: rgba(0, 0, 0, 1);
+//             transform: scale(1.2);
+//         }
+//     }
+// }
+
+.presItem-slide.active {
+    //box-shadow: 0 0 0px 2.5px $dark-blue;
+    border: $dark-blue solid 2.5px;
+}
+
+.presItem-slide.group {
+    border-radius: 5px;
+}
+
+.presItem-slide.activeGroup {
+    border-radius: 5px;
+    box-shadow: 0 0 0px 2.5px $dark-blue;
+}
+
+.presItem-groupSlideContainer {
+    position: absolute;
+    /* grid-row: 3; */
+    /* grid-column: 1/8; */
+    top: 28;
+    display: block;
+    background: #92adb9;
+    width: 100%;
+    height: 10px;
+    border-radius: 0px 0px 5px 5px;
+    height: calc(100% - 28px);
+    display: grid;
+    grid-template-rows: auto auto auto;
+    grid-template-columns: 100%;
+    justify-items: left;
+    align-items: center;
+}
+
+.presItem-groupSlide {
+    position: relative;
+    background-color: #d5dce2;
+    border-radius: 5px;
+    height: calc(100% - 7px);
+    width: calc(100% - 20px);
+    left: 15px;
+    /* height: 20px; */
+    /* width: calc(100% - 19px); */
+    display: flex;
+    grid-template-rows: 16px 10px auto;
+    grid-template-columns: max-content max-content max-content max-content auto;
+}
+
+.presItem-multiDrag {
+    font-family: Roboto;
+    font-weight: 600;
+    color: white;
+    text-align: center;
+    justify-content: center;
+    align-content: center;
+    width: 100px;
+    height: 30px;
+    position: absolute;
+    background-color: $dark-blue;
+    z-index: 4000;
+    border-radius: 10px;
+    box-shadow: black 0.4vw 0.4vw 0.8vw;
+    line-height: 30px;
+}
+
+.presItem-miniSlide {
+    font-weight: 700;
+    font-size: 12;
+    grid-column: 1/8;
+    align-self: center;
+    justify-self: center;
+    background-color: #d5dce2;
+    width: 26px;
+    text-align: center;
+    height: 26px;
+    line-height: 28px;
+    border-radius: 100%;
+}
+
+.presItem-miniSlide.active {
+    box-shadow: 0 0 0px 1.5px $dark-blue;
+}
+
+.expandButton {
+    cursor: pointer;
+    position: absolute;
+    border-radius: 100px;
+    bottom: 0;
+    left: -18;
+    z-index: 300;
+    width: 15px;
+    height: 15px;
+    display: flex;
+    font-size: 12px;
+    justify-self: center;
+    align-self: center;
+    background-color: #92adb9;
+    color: white;
+    justify-content: center;
+    align-items: center;
+    transition: 0.2s;
+    margin-right: 3px;
+}
+
+.expandButton:hover {
+    background-color: rgba(0, 0, 0, 1);
+    transform: scale(1.2);
+}
+
+.presItem-groupNum {
+    color: #d5dce2;
+    position: absolute;
+    left: -15px;
+    top: 1;
+    font-weight: 600;
+    font-size: 12;
+}
diff --git a/src/client/views/nodes/trails/PresSlideBox.tsx b/src/client/views/nodes/trails/PresSlideBox.tsx
new file mode 100644
index 000000000..3dbb3da88
--- /dev/null
+++ b/src/client/views/nodes/trails/PresSlideBox.tsx
@@ -0,0 +1,628 @@
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@mui/material';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { returnFalse, returnTrue, setupMoveUpEvents } from '../../../../ClientUtils';
+import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
+import { Id } from '../../../../fields/FieldSymbols';
+import { List } from '../../../../fields/List';
+import { BoolCast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
+import { emptyFunction } from '../../../../Utils';
+import { Docs } from '../../../documents/Documents';
+import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
+import { DragManager } from '../../../util/DragManager';
+import { SnappingManager } from '../../../util/SnappingManager';
+import { Transform } from '../../../util/Transform';
+import { undoable, undoBatch } from '../../../util/UndoManager';
+import { TreeView } from '../../collections/TreeView';
+import { ViewBoxBaseComponent } from '../../DocComponent';
+import { EditableView } from '../../EditableView';
+import { Colors } from '../../global/globalEnums';
+import { PinDocView } from '../../PinFuncs';
+import { StyleProp } from '../../StyleProp';
+import { returnEmptyDocViewList } from '../../StyleProvider';
+import { DocumentView } from '../DocumentView';
+import { FieldView, FieldViewProps } from '../FieldView';
+import { PresBox } from './PresBox';
+import './PresSlideBox.scss';
+import { PresMovement } from './PresEnums';
+/**
+ * This class models the view a document added to presentation will have in the presentation.
+ * It involves some functionality for its buttons and options.
+ */
+@observer
+export class PresSlideBox extends ViewBoxBaseComponent<FieldViewProps>() {
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(PresSlideBox, fieldKey);
+    }
+    private _itemRef: React.RefObject<HTMLDivElement> = React.createRef();
+    private _dragRef: React.RefObject<HTMLDivElement> = React.createRef();
+    private _titleRef: React.RefObject<EditableView> = React.createRef();
+    private _heightDisposer: IReactionDisposer | undefined;
+    readonly expandViewHeight = 100;
+    readonly collapsedHeight = 35;
+
+    constructor(props: FieldViewProps) {
+        super(props);
+        makeObservable(this);
+    }
+
+    @observable _dragging = false;
+
+    // the presentation view that renders this slide
+    @computed get presBoxView() {
+        return this.DocumentView?.()
+            .containerViewPath?.()
+            .slice()
+            .reverse()
+            .find(dv => dv?.ComponentView instanceof PresBox)?.ComponentView as Opt<PresBox>;
+    }
+
+    // the presentation view document that renders this slide
+    @computed get presBox() {
+        return this.presBoxView?.Document;
+    }
+
+    // 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 this.rootDoc;
+    }
+
+    // this is the document in the workspaces that is targeted by the slide
+    @computed get targetDoc() {
+        return DocCast(this.slideDoc.presentation_targetDoc, this.slideDoc)!;
+    }
+
+    // computes index of this presentation slide in the presBox list
+    @computed get indexInPres() {
+        return this.presBoxView?.SlideIndex(this.slideDoc) ?? 0;
+    }
+
+    @computed get selectedArray() {
+        return this.presBoxView?.selectedArray;
+    }
+
+    @computed get videoRecordingIsInOverlay() {
+        return Doc.MyOverlayDocs.some(doc => doc.slides === this.slideDoc);
+    }
+
+    componentDidMount() {
+        this.layoutDoc.layout_hideLinkButton = true;
+        this._heightDisposer = reaction(
+            () => ({ expand: this.slideDoc.presentation_expandInlineButton, height: this.collapsedHeight }),
+            ({ expand, height }) => {
+                this.layoutDoc._height = height + (expand ? this.expandViewHeight : 0);
+            },
+            { fireImmediately: true }
+        );
+    }
+    componentWillUnmount() {
+        this._heightDisposer?.();
+    }
+
+    presExpandDocumentClick = () => {
+        this.slideDoc.presentation_expandInlineButton = !this.slideDoc.presentation_expandInlineButton;
+    };
+    embedHeight = () => this.collapsedHeight + this.expandViewHeight;
+    embedWidth = () => this._props.PanelWidth() / 2;
+    // prettier-ignore
+    styleProvider = ( doc: Doc | undefined, props: Opt<FieldViewProps>, property: string ) => 
+        (property === StyleProp.Opacity ? 1 : this._props.styleProvider?.(doc, props, property));
+    /**
+     * The function that is responsible for rendering a preview or not for this
+     * presentation element.
+     */
+    @computed get renderEmbeddedInline() {
+        return !this.slideDoc.presentation_expandInlineButton || !this.targetDoc ? null : (
+            <div className="presItem-embedded" style={{ height: this.embedHeight(), width: '50%' }}>
+                <DocumentView
+                    Document={PresBox.targetRenderedDoc(this.slideDoc) ?? this.slideDoc}
+                    PanelWidth={this.embedWidth}
+                    PanelHeight={this.embedHeight}
+                    isContentActive={this._props.isContentActive}
+                    styleProvider={this.styleProvider}
+                    hideLinkButton
+                    ScreenToLocalTransform={Transform.Identity}
+                    renderDepth={this._props.renderDepth + 1}
+                    containerViewPath={returnEmptyDocViewList}
+                    childFilters={this._props.childFilters}
+                    childFiltersByRanges={this._props.childFiltersByRanges}
+                    searchFilterDocs={this._props.searchFilterDocs}
+                    addDocument={returnFalse}
+                    removeDocument={returnFalse}
+                    fitContentsToBox={returnTrue}
+                    moveDocument={this._props.moveDocument!}
+                    focus={emptyFunction}
+                    whenChildContentsActiveChanged={returnFalse}
+                    addDocTab={returnFalse}
+                    pinToPres={returnFalse}
+                />
+            </div>
+        );
+    }
+
+    @computed get renderGroupSlides() {
+        const childDocs = DocListCast(this.targetDoc.data);
+        const groupSlides = childDocs.map((doc: Doc, ind: number) => (
+            <div
+                key={doc[Id]}
+                className="presItem-groupSlide"
+                onClick={e => {
+                    e.stopPropagation();
+                    e.preventDefault();
+                    this.presBoxView?.modifierSelect(doc, this._itemRef.current!, this._dragRef.current!, e.shiftKey || e.ctrlKey || e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey);
+                    this.presExpandDocumentClick();
+                }}>
+                <div className="presItem-groupNum">{`${ind + 1}.`}</div>
+                <div className="presItem-name">
+                    <EditableView
+                        ref={this._titleRef}
+                        editing={undefined}
+                        contents={StrCast(doc.title)}
+                        overflow="ellipsis"
+                        GetValue={() => StrCast(doc.title)}
+                        SetValue={(value: string) => {
+                            doc.title = !value.trim().length ? '-untitled-' : value;
+                            return true;
+                        }}
+                    />
+                </div>
+            </div>
+        ));
+        return groupSlides;
+    }
+
+    @computed get transition() {
+        let transitionInS: number;
+        if (this.slideDoc.presentation_transition) transitionInS = NumCast(this.slideDoc.presentation_transition) / 1000;
+        else transitionInS = 0.5;
+        return this.slideDoc.presentation_movement === PresMovement.Jump || this.slideDoc.presentation_movement === PresMovement.None ? null : 'M: ' + transitionInS + 's';
+    }
+
+    @action
+    headerDown = (e: React.PointerEvent<HTMLDivElement>) => {
+        const element = e.target as HTMLDivElement;
+        e.stopPropagation();
+        e.preventDefault();
+        if (element && !(e.ctrlKey || e.metaKey || e.button === 2)) {
+            this.presBoxView?.regularSelect(this.slideDoc, this._itemRef.current!, this._dragRef.current!, true, false);
+            setupMoveUpEvents(this, e, this.startDrag, emptyFunction, clickEv => {
+                clickEv.stopPropagation();
+                clickEv.preventDefault();
+                this.presBoxView?.modifierSelect(this.slideDoc, this._itemRef.current!, this._dragRef.current!, clickEv.shiftKey || clickEv.ctrlKey || clickEv.metaKey, clickEv.ctrlKey || clickEv.metaKey, clickEv.shiftKey);
+                this.presBoxView?.activeItem && this.showRecording(this.presBoxView?.activeItem);
+            });
+        }
+    };
+
+    /**
+     * Function to drag and drop the pres element to a diferent location
+     */
+    startDrag = (e: PointerEvent) => {
+        this.presBoxView?.regularSelect(this.slideDoc, this._itemRef.current!, this._dragRef.current!, true, false);
+        const miniView: boolean = this.toolbarWidth <= 100;
+        const activeItem = this.slideDoc;
+        const dragArray = this.presBoxView?._dragArray ?? [];
+        const dragData = new DragManager.DocumentDragData(this.presBoxView?.sortArray() ?? []);
+        if (!dragData.draggedDocuments.length) dragData.draggedDocuments.push(this.slideDoc);
+        dragData.treeViewDoc = this.presBox?._type_collection === CollectionViewType.Tree ? this.presBox : undefined; // this.DocumentView?.()?._props.treeViewDoc;
+        dragData.moveDocument = this._props.moveDocument;
+        const dragItem: HTMLElement[] = [];
+        const classesToRestore = new Map<HTMLElement, string>();
+        if (dragArray.length === 1) {
+            const doc = this._itemRef.current || dragArray[0];
+            if (doc) {
+                classesToRestore.set(doc, doc.className);
+                doc.className = miniView ? 'presItem-miniSlide' : 'presItem-slide';
+                dragItem.push(doc);
+            }
+        } else if (dragArray.length >= 1) {
+            const doc = document.createElement('div');
+            doc.className = 'presItem-multiDrag';
+            doc.innerText = 'Move ' + (this.selectedArray?.size ?? 0) + ' slides';
+            doc.style.position = 'absolute';
+            doc.style.top = e.clientY + 'px';
+            doc.style.left = e.clientX - 50 + 'px';
+            dragItem.push(doc);
+        }
+
+        if (activeItem) {
+            runInAction(() => {
+                this._dragging = true;
+            });
+            DragManager.StartDocumentDrag(
+                dragItem.map(ele => ele),
+                dragData,
+                e.clientX,
+                e.clientY,
+                undefined,
+                action(() => {
+                    Array.from(classesToRestore).forEach(pair => (pair[0].className = pair[1]));
+                    this._dragging = false;
+                })
+            );
+            return true;
+        }
+        return false;
+    };
+
+    onPointerOver = () => {
+        document.removeEventListener('pointermove', this.onPointerMove);
+        document.addEventListener('pointermove', this.onPointerMove);
+    };
+
+    onPointerMove = (e: PointerEvent) => {
+        const slide = this._itemRef.current;
+        const dragIsPresItem = DragManager.docsBeingDragged.some(d => d.presentation_targetDoc);
+        if (slide && dragIsPresItem) {
+            const rect = slide.getBoundingClientRect();
+            const y = e.clientY - rect.top; // y position within the element.
+            const height = slide.clientHeight;
+            const halfLine = height / 2;
+            if (y <= halfLine) {
+                slide.style.borderTop = `solid 2px ${Colors.MEDIUM_BLUE}`;
+                slide.style.borderBottom = '0px';
+            } else if (y > halfLine) {
+                slide.style.borderTop = '0px';
+                slide.style.borderBottom = `solid 2px ${Colors.MEDIUM_BLUE}`;
+            }
+        }
+        document.removeEventListener('pointermove', this.onPointerMove);
+    };
+
+    onPointerLeave = () => {
+        const slide = this._itemRef.current;
+        if (slide) {
+            slide.style.borderTop = '0px';
+            slide.style.borderBottom = '0px';
+        }
+        document.removeEventListener('pointermove', this.onPointerMove);
+    };
+
+    @action
+    toggleProperties = () => {
+        if (SnappingManager.PropertiesWidth < 5) {
+            SnappingManager.SetPropertiesWidth(250);
+        }
+    };
+
+    removePresentationItem = undoable(
+        action((e: React.MouseEvent) => {
+            e.stopPropagation();
+            if (this.presBox && this.indexInPres < (this.presBoxView?.itemIndex || 0)) {
+                this.presBox.itemIndex = (this.presBoxView?.itemIndex || 0) - 1;
+            }
+            this._props.removeDocument?.(this.slideDoc);
+            this.presBoxView?.removeFromSelectedArray(this.slideDoc);
+            this.removeAllRecordingInOverlay();
+        }),
+        'Remove doc from pres trail'
+    );
+
+    // set title of the individual pres slide
+    onSetValue = undoable(
+        action((value: string) => {
+            this.slideDoc.title = !value.trim().length ? '-untitled-' : value;
+            return true;
+        }),
+        'set title of pres element'
+    );
+
+    /**
+     * Method called for updating the view of the currently selected document
+     *
+     * @param targetDoc
+     * @param activeItem
+     */
+    @undoBatch
+    updateCapturedContainerLayout = (presTargetDoc: Doc, activeItem: Doc) => {
+        const targetDoc = DocCast(presTargetDoc.annotationOn) ?? presTargetDoc;
+        activeItem.config_x = NumCast(targetDoc.x);
+        activeItem.config_y = NumCast(targetDoc.y);
+        activeItem.config_rotation = NumCast(targetDoc.rotation);
+        activeItem.config_width = NumCast(targetDoc.width);
+        activeItem.config_height = NumCast(targetDoc.height);
+        activeItem.config_pinLayout = !activeItem.config_pinLayout;
+        // activeItem.config_pinLayout = true;
+    };
+
+    /**
+     * Method called for updating the view of the currently selected document
+     *
+     * @param presTargetDoc
+     * @param activeItem
+     */
+    updateCapturedViewContents = undoable(
+        action((presTargetDoc: Doc, activeItem: Doc) => {
+            const target = DocCast(presTargetDoc.annotationOn) ?? presTargetDoc;
+            PinDocView(activeItem, { pinData: PresBox.pinDataTypes(target) }, target);
+        }),
+        'updated captured view contents'
+    );
+
+    // a previously recorded video will have timecode defined
+    static videoIsRecorded = (activeItem: Opt<Doc>) => 'layout_currentTimecode' in (DocCast(activeItem?.recording) ?? {});
+
+    removeAllRecordingInOverlay = () => Doc.MyOverlayDocs.filter(doc => doc.slides === this.slideDoc).forEach(Doc.RemFromMyOverlay);
+
+    /// remove all videos that have been recorded from overlay (leave videso that are being recorded to avoid losing data)
+    static removeEveryExistingRecordingInOverlay = () => {
+        Doc.MyOverlayDocs.filter(doc => doc.slides !== null && PresSlideBox.videoIsRecorded(DocCast(doc.slides))) //
+            .forEach(Doc.RemFromMyOverlay);
+    };
+
+    hideRecording = undoable(
+        action((e: React.MouseEvent) => {
+            e.stopPropagation();
+            this.removeAllRecordingInOverlay();
+        }),
+        'hide video recording'
+    );
+
+    showRecording = undoable(
+        action((activeItem: Doc, iconClick: boolean = false) => {
+            // remove the overlays on switch *IF* not opened from the specific icon
+            if (!iconClick) PresSlideBox.removeEveryExistingRecordingInOverlay();
+
+            DocCast(activeItem.recording) && Doc.AddToMyOverlay(DocCast(activeItem.recording)!);
+        }),
+        'show video recording'
+    );
+
+    startRecording = undoable(
+        action((e: React.MouseEvent, activeItem: Doc) => {
+            e.stopPropagation();
+            if (PresSlideBox.videoIsRecorded(activeItem)) {
+                // if we already have an existing recording
+                this.showRecording(activeItem, true);
+                //   // if we already have an existing recording
+                //   Doc.AddToMyOverlay(Cast(activeItem.recording, Doc, null));
+            } else {
+                // we dont have any recording
+                // Remove every recording that already exists in overlay view
+                // this is a design decision to clear to focus in on the recoding mode
+                PresSlideBox.removeEveryExistingRecordingInOverlay();
+
+                // create and add a recording to the slide
+                // make recording box appear in the bottom right corner of the screen
+                Doc.AddToMyOverlay(
+                    (activeItem.recording = Docs.Create.WebCamDocument('', {
+                        _width: 384,
+                        _height: 216,
+                        overlayX: window.innerWidth - 384 - 20,
+                        overlayY: window.innerHeight - 216 - 20,
+                        layout_hideDocumentButtonBar: true,
+                        layout_hideDecorationTitle: true,
+                        layout_hideOpenButton: true,
+                        cloneFieldFilter: new List<string>(['isSystem']),
+                        slides: activeItem, // attach the slide to the recording
+                    }))
+                );
+            }
+        }),
+        'start video recording'
+    );
+
+    @undoBatch
+    lfg = (e: React.MouseEvent) => {
+        e.stopPropagation();
+        // TODO: fix this bug
+        // const { toggleChildrenRun } = this.slideDoc;
+        TreeView.ToggleChildrenRun.get(this.slideDoc)?.();
+
+        // call this.slideDoc.recurChildren() to get all the children
+        // if (iconClick) PresSlideBox.showVideo = false;
+    };
+
+    @computed
+    get toolbarWidth(): number {
+        const presBoxDocView = DocumentView.getDocumentView(this.presBox);
+        const width = NumCast(this.presBox?._width);
+        return presBoxDocView ? presBoxDocView._props.PanelWidth() : width || 300;
+    }
+
+    @computed get presButtons() {
+        const { presBox, targetDoc, slideDoc: activeItem } = this;
+        const presBoxColor = StrCast(presBox?._backgroundColor);
+        const presColorBool = presBoxColor ? presBoxColor !== Colors.WHITE && presBoxColor !== 'transparent' : false;
+        const hasChildren = BoolCast(this.slideDoc?.hasChildren);
+
+        const items: JSX.Element[] = [];
+
+        items.push(
+            <Tooltip key="slide" title={<div className="dash-tooltip">Update captured doc layout</div>}>
+                <div
+                    className="slideButton"
+                    onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this.updateCapturedContainerLayout(targetDoc, activeItem), true)}
+                    style={{ opacity: activeItem.config_pinLayout ? 1 : 0.5, fontWeight: 700, display: 'flex' }}>
+                    L
+                </div>
+            </Tooltip>
+        );
+        items.push(
+            <Tooltip key="flex" title={<div className="dash-tooltip">Update captured doc content</div>}>
+                <div
+                    className="slideButton"
+                    onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this.updateCapturedViewContents(targetDoc, activeItem))}
+                    style={{ opacity: activeItem.config_pinData || activeItem.config_pinView ? 1 : 0.5, fontWeight: 700, display: 'flex' }}>
+                    C
+                </div>
+            </Tooltip>
+        );
+        items.push(
+            <Tooltip key="slash" title={<div className="dash-tooltip">{this.videoRecordingIsInOverlay ? 'Hide Recording' : `${PresSlideBox.videoIsRecorded(activeItem) ? 'Show' : 'Start'} recording`}</div>}>
+                <div className="slideButton" onClick={e => (this.videoRecordingIsInOverlay ? this.hideRecording(e) : this.startRecording(e, activeItem))} style={{ fontWeight: 700 }}>
+                    <FontAwesomeIcon icon={`video${this.videoRecordingIsInOverlay ? '-slash' : ''}`} onPointerDown={e => e.stopPropagation()} />
+                </div>
+            </Tooltip>
+        );
+        if (this.indexInPres !== 0) {
+            items.push(
+                <Tooltip
+                    key="arrow"
+                    title={
+                        <div className="dash-tooltip">
+                            {!activeItem.presentation_groupWithUp
+                                ? 'Not grouped with previous slide (click to group)'
+                                : activeItem.presentation_groupWithUp === 1
+                                  ? 'Run simultaneously with previous slide (click again to run after)'
+                                  : 'Run after previous slide (click to ungroup from previous)'}
+                        </div>
+                    }>
+                    <div
+                        className="slideButton"
+                        onClick={() => {
+                            activeItem.presentation_groupWithUp = (NumCast(activeItem.presentation_groupWithUp) + 1) % 3;
+                        }}
+                        style={{
+                            zIndex: 1000 - this.indexInPres,
+                            fontWeight: 700,
+                            backgroundColor: activeItem.presentation_groupWithUp ? (presColorBool ? presBoxColor : Colors.MEDIUM_BLUE) : undefined,
+                            outline: NumCast(activeItem.presentation_groupWithUp) > 1 ? 'solid black 1px' : undefined,
+                            height: activeItem.presentation_groupWithUp ? 53 : 18,
+                            transform: activeItem.presentation_groupWithUp ? 'translate(0, -17px)' : undefined,
+                        }}>
+                        <div style={{ transform: activeItem.presentation_groupWithUp ? 'rotate(180deg) translate(0, -17.5px)' : 'rotate(0deg)' }}>
+                            <FontAwesomeIcon icon="arrow-up" onPointerDown={e => e.stopPropagation()} />
+                        </div>
+                    </div>
+                </Tooltip>
+            );
+        }
+        items.push(
+            <Tooltip key="eye" title={<div className="dash-tooltip">{this.slideDoc.presentation_expandInlineButton ? 'Minimize' : 'Expand'}</div>}>
+                <div
+                    className="slideButton"
+                    onClick={e => {
+                        e.stopPropagation();
+                        this.presExpandDocumentClick();
+                    }}>
+                    <FontAwesomeIcon icon={this.slideDoc.presentation_expandInlineButton ? 'eye-slash' : 'eye'} onPointerDown={e => e.stopPropagation()} />
+                </div>
+            </Tooltip>
+        );
+        if (!Doc.noviceMode && hasChildren) {
+            // TODO: replace with if treeveiw, has childrenDocs
+            items.push(
+                <Tooltip key="children" title={<div className="dash-tooltip">Run child processes (tree only)</div>}>
+                    <div
+                        className="slideButton"
+                        onClick={e => {
+                            e.stopPropagation();
+                            this.lfg(e);
+                        }}
+                        style={{ fontWeight: 700 }}>
+                        <FontAwesomeIcon icon="circle-play" onPointerDown={e => e.stopPropagation()} />
+                    </div>
+                </Tooltip>
+            );
+        }
+        items.push(
+            <Tooltip key="trash" title={<div className="dash-tooltip">Remove from presentation</div>}>
+                <div className="slideButton" onClick={this.removePresentationItem}>
+                    <FontAwesomeIcon icon="trash" onPointerDown={e => e.stopPropagation()} />
+                </div>
+            </Tooltip>
+        );
+        items.push(
+            <Tooltip key="customize-slide" title={<div className="dash-tooltip">Customize Slide</div>}>
+                <div
+                    className={'slideButton'}
+                    onClick={() => {
+                        this.presBoxView?.regularSelect(this.slideDoc, this._itemRef.current!, this._dragRef.current!, true, false);
+                        PresBox.Instance.navigateToActiveItem();
+                        PresBox.Instance.openProperties();
+                        PresBox.Instance.slideToModify = this.Document;
+                    }}>
+                    <FontAwesomeIcon icon={'edit'} onPointerDown={e => e.stopPropagation()} />
+                </div>
+            </Tooltip>
+        );
+        return items;
+    }
+
+    @computed get mainItem() {
+        const { presBox, slideDoc: activeItem } = this;
+        const isSelected: boolean = !!this.selectedArray?.has(activeItem);
+        const isCurrent: boolean = this.presBox?._itemIndex === this.indexInPres;
+        const miniView: boolean = this.toolbarWidth <= 110;
+        const presBoxColor: string = StrCast(presBox?._backgroundColor);
+        const presColorBool: boolean = presBoxColor ? presBoxColor !== Colors.WHITE && presBoxColor !== 'transparent' : false;
+
+        return (
+            <div
+                className="presItem-container"
+                key={activeItem[Id] + this.indexInPres}
+                ref={this._itemRef}
+                style={{
+                    backgroundColor: presColorBool ? (isSelected ? 'rgba(250,250,250,0.3)' : 'transparent') : isSelected ? Colors.LIGHT_BLUE : 'transparent',
+                    opacity: this._dragging ? 0.3 : 1,
+                    paddingLeft: NumCast(this.layoutDoc._xPadding, this._props.xPadding),
+                    paddingRight: NumCast(this.layoutDoc._xPadding, this._props.xPadding),
+                    paddingTop: NumCast(this.layoutDoc._yPadding, this._props.yPadding),
+                    paddingBottom: NumCast(this.layoutDoc._yPadding, this._props.yPadding),
+                }}
+                onDoubleClick={action(() => {
+                    this.toggleProperties();
+                    this.presBoxView?.regularSelect(activeItem, this._itemRef.current!, this._dragRef.current!, false);
+                })}
+                onPointerOver={this.onPointerOver}
+                onPointerLeave={this.onPointerLeave}
+                onPointerDown={this.headerDown}>
+                {miniView ? (
+                    <div className={`presItem-miniSlide ${isSelected ? 'active' : ''}`} ref={this._dragRef}>
+                        {`${this.indexInPres + 1}.`}
+                    </div>
+                ) : (
+                    <div
+                        ref={this._dragRef}
+                        className={`presItem-slide ${isCurrent ? 'active' : ''}${activeItem.runProcess ? ' testingv2' : ''}`}
+                        style={{
+                            display: 'infline-block',
+                            backgroundColor: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor) as string,
+                            // layout_boxShadow: presBoxColor && presBoxColor !== 'white' && presBoxColor !== 'transparent' ? (isCurrent ? '0 0 0px 1.5px' + presBoxColor : undefined) : undefined,
+                            border: presBoxColor && presBoxColor !== 'white' && presBoxColor !== 'transparent' ? (isCurrent ? presBoxColor + ' solid 2.5px' : undefined) : undefined,
+                        }}>
+                        <div
+                            className="presItem-name"
+                            style={{
+                                display: 'inline-flex',
+                                pointerEvents: isSelected ? undefined : 'none',
+                                width: `calc(100% ${activeItem.presentation_expandInlineButton ? '- 50%' : ''} - ${this.presButtons.length * 22}px`,
+                                cursor: isSelected ? 'text' : 'grab',
+                            }}>
+                            <div
+                                className="presItem-number"
+                                title="select without navigation"
+                                onPointerDown={e => {
+                                    e.stopPropagation();
+                                    if (this._itemRef.current && this._dragRef.current) {
+                                        this.presBoxView?.modifierSelect(activeItem, this._itemRef.current, this._dragRef.current, true, false, false);
+                                    }
+                                }}
+                                onClick={e => e.stopPropagation()}>{`${this.indexInPres + 1}.  `}</div>
+                            <EditableView ref={this._titleRef} oneLine editing={!isSelected ? false : undefined} contents={StrCast(activeItem.title)} overflow="ellipsis" GetValue={() => StrCast(activeItem.title)} SetValue={this.onSetValue} />
+                        </div>
+                        {/* <Tooltip title={<><div className="dash-tooltip">{"Movement speed"}</div></>}><div className="presItem-time" style={{ display: showMore ? "block" : "none" }}>{this.transition}</div></Tooltip> */}
+                        {/* <Tooltip title={<><div className="dash-tooltip">{"Duration"}</div></>}><div className="presItem-time" style={{ display: showMore ? "block" : "none" }}>{this.duration}</div></Tooltip> */}
+                        <div className="presItem-slideButtons" style={{ position: 'absolute', right: 0 }}>
+                            {...this.presButtons}
+                        </div>
+                        {this.renderEmbeddedInline}
+                    </div>
+                )}
+            </div>
+        );
+    }
+
+    render() {
+        return !(this.slideDoc instanceof Doc) || this.targetDoc instanceof Promise ? null : this.mainItem;
+    }
+}
+
+Docs.Prototypes.TemplateMap.set(DocumentType.PRESSLIDE, {
+    layout: { view: PresSlideBox, dataField: 'data' },
+    options: { acl: '', title: 'presSlide', _layout_fitWidth: true, _xMargin: 0, isTemplateDoc: true },
+});
diff --git a/src/client/views/nodes/trails/index.ts b/src/client/views/nodes/trails/index.ts
index 7b18974df..a5bc55221 100644
--- a/src/client/views/nodes/trails/index.ts
+++ b/src/client/views/nodes/trails/index.ts
@@ -1,3 +1,3 @@
 export * from './PresBox';
-export * from './PresElementBox';
+export * from './PresSlideBox';
 export * from './PresEnums';
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index e3ee51424..c45d8e052 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -210,7 +210,7 @@ export class GPTPopup extends ObservableReactComponent<object> {
     generateFireflyImage = (imgDesc: string) => {
         const selView = DocumentView.Selected().lastElement();
         const selDoc = selView?.Document;
-        if (selDoc && (selView._props.renderDepth > 1 || selDoc[Doc.LayoutFieldKey(selDoc)] instanceof ImageField)) {
+        if (selDoc && (selView._props.renderDepth > 1 || selDoc[Doc.LayoutDataKey(selDoc)] instanceof ImageField)) {
             const oldPrompt = StrCast(selDoc.ai_firefly_prompt, StrCast(selDoc.title));
             const newPrompt = oldPrompt ? `${oldPrompt} ~~~ ${imgDesc}` : imgDesc;
             return DrawingFillHandler.drawingToImage(selDoc, 100, newPrompt, selDoc)
diff --git a/src/client/views/search/FaceRecognitionHandler.tsx b/src/client/views/search/FaceRecognitionHandler.tsx
index 86e28a768..f60eb486e 100644
--- a/src/client/views/search/FaceRecognitionHandler.tsx
+++ b/src/client/views/search/FaceRecognitionHandler.tsx
@@ -60,7 +60,7 @@ export class FaceRecognitionHandler {
      * @param imgDoc image with faces
      * @returns faceDoc array
      */
-    public static ImageDocFaceAnnos = (imgDoc: Doc) => DocListCast(imgDoc[`${Doc.LayoutFieldKey(imgDoc)}_annotations`]).filter(doc => doc.face);
+    public static ImageDocFaceAnnos = (imgDoc: Doc) => DocListCast(imgDoc[`${Doc.LayoutDataKey(imgDoc)}_annotations`]).filter(doc => doc.face);
 
     /**
      * returns a list of all face collection Docs on the current dashboard
@@ -207,7 +207,7 @@ export class FaceRecognitionHandler {
         } else if (imgDoc.type === DocumentType.LOADING && !imgDoc.loadingError) {
             setTimeout(() => this.classifyFacesInImage(imgDoc), 1000);
         } else {
-            const imgUrl = ImageCast(imgDoc[Doc.LayoutFieldKey(imgDoc)]);
+            const imgUrl = ImageCast(imgDoc[Doc.LayoutDataKey(imgDoc)]);
             if (imgUrl && !DocListCast(Doc.MyFaceCollection.examinedFaceDocs).includes(imgDoc[DocData])) {
                 // only examine Docs that have an image and that haven't already been examined.
                 Doc.AddDocToList(Doc.MyFaceCollection, 'examinedFaceDocs', imgDoc[DocData]);
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 1f6e80bd1..ad0f56cb9 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -218,7 +218,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
                     .filter(d => d)
                     // eslint-disable-next-line no-loop-func
                     .map(async d => {
-                        const fieldKey = Doc.LayoutFieldKey(d);
+                        const fieldKey = Doc.LayoutDataKey(d);
                         const annos = !Field.toString(Doc.LayoutField(d) as FieldType).includes('CollectionView');
                         const data = d[annos ? fieldKey + '_annotations' : fieldKey];
                         const dataDocs = await DocListCastAsync(data);
diff --git a/src/client/views/smartdraw/StickerPalette.tsx b/src/client/views/smartdraw/StickerPalette.tsx
index 2260d1f73..0e234e966 100644
--- a/src/client/views/smartdraw/StickerPalette.tsx
+++ b/src/client/views/smartdraw/StickerPalette.tsx
@@ -56,10 +56,10 @@ export class StickerPalette extends ObservableReactComponent<StickerPaletteProps
         if (!doc.savedAsSticker) {
             const docView = DocumentView.getDocumentView(doc);
             await docView?.ComponentView?.updateIcon?.(true);
-            const { clone } = await Doc.MakeClone(doc);
+            const { clone } = Doc.MakeClone(doc);
             clone.title = doc.title;
-            const image = ImageCast(doc.icon, ImageCast(clone[Doc.LayoutFieldKey(clone)]))?.url?.href;
-            Doc.AddDocToList(Doc.MyStickers, 'data', makeUserTemplateButtonOrImage(clone, image));
+            const image = ImageCast(doc.icon, ImageCast(clone[Doc.LayoutDataKey(clone)]))?.url?.href;
+            Doc.MyStickers && Doc.AddDocToList(Doc.MyStickers, 'data', makeUserTemplateButtonOrImage(clone, image));
             doc.savedAsSticker = true;
         }
     };
@@ -327,9 +327,10 @@ export class StickerPalette extends ObservableReactComponent<StickerPaletteProps
     );
     renderPaletteView = () => (
         <>
-            {this.renderDoc(Doc.MyStickers, (r: DocumentView) => {
-                this._docView = r;
-            })}
+            {Doc.MyStickers &&
+                this.renderDoc(Doc.MyStickers, (r: DocumentView) => {
+                    this._docView = r;
+                })}
             <Button text="Add" icon={<FontAwesomeIcon icon="square-plus" />} color={SettingsManager.userColor} onClick={() => this.setPaletteMode(StickerPaletteMode.create)} />
         </>
     );
diff --git a/src/debug/Repl.tsx b/src/debug/Repl.tsx
index 8ac502348..37586fcc0 100644
--- a/src/debug/Repl.tsx
+++ b/src/debug/Repl.tsx
@@ -13,7 +13,7 @@ import { makeInterface } from '../fields/Schema';
 class Repl extends React.Component {
     @observable text: string = '';
 
-    @observable executedCommands: { command: string; result: any }[] = [];
+    @observable executedCommands: { command: string; result: unknown }[] = [];
 
     onChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
         this.text = e.target.value;
@@ -30,7 +30,7 @@ class Repl extends React.Component {
             if (!script.compiled) {
                 this.executedCommands.push({ command: this.text, result: 'Compile Error' });
             } else {
-                const result = script.run({ makeInterface }, err => this.executedCommands.push({ command: this.text, result: err.message || err }));
+                const result = script.run({ makeInterface }, err => this.executedCommands.push({ command: this.text, result: err }));
                 result.success && this.executedCommands.push({ command: this.text, result: result.result });
             }
             this.text = '';
@@ -40,7 +40,7 @@ class Repl extends React.Component {
     @computed
     get commands() {
         return this.executedCommands.map(command => (
-            <div style={{ marginTop: '5px' }}>
+            <div key={command.command} style={{ marginTop: '5px' }}>
                 <p>{command.command}</p>
                 {/* <pre>{JSON.stringify(command.result, null, 2)}</pre> */}
                 <pre>{command.result instanceof RefField || command.result instanceof ObjectField ? 'object' : String(command.result)}</pre>
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 2db8d3b85..abce7ed26 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -17,7 +17,7 @@ import { Copy, FieldChanged, HandleUpdate, Id, Parent, ToJavascriptString, ToScr
 import { InkEraserTool, InkInkTool, InkTool } from './InkField';
 import { List } from './List';
 import { ObjectField, serverOpType } from './ObjectField';
-import { PrefetchProxy, ProxyField } from './Proxy';
+import { PrefetchProxy } from './Proxy';
 import { FieldId, RefField } from './RefField';
 import { RichTextField } from './RichTextField';
 import { listSpec } from './Schema';
@@ -47,7 +47,7 @@ export namespace Field {
      * @returns string representation of the field
      */
     export function toKeyValueString(doc: Doc, key: string, showComputedValue?: boolean, schemaCell?: boolean): string {
-        const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
+        const cfield = ComputedField.DisableCompute(() => FieldValue(doc[key]));
         const valFunc = (field: FieldType): string => {
             const res =
                 field instanceof ComputedField && showComputedValue
@@ -133,13 +133,13 @@ export function DocListCastAsync(field: FieldResult, defaultValue?: Doc[]) {
     return list ? Promise.all(list).then(() => list) : Promise.resolve(defaultValue);
 }
 export function NumListCast(field: FieldResult, defaultVal: number[] = []) {
-    return Cast(field, listSpec('number'), defaultVal);
+    return Cast(field, listSpec('number'), defaultVal)!;
 }
 export function StrListCast(field: FieldResult, defaultVal: string[] = []) {
-    return Cast(field, listSpec('string'), defaultVal);
+    return Cast(field, listSpec('string'), defaultVal)!;
 }
 export function DocListCast(field: FieldResult, defaultVal: Doc[] = []) {
-    return Cast(field, listSpec(Doc), defaultVal).filter(d => d instanceof Doc) as Doc[];
+    return Cast(field, listSpec(Doc), defaultVal)!.filter(d => d instanceof Doc);
 }
 
 export enum aclLevel {
@@ -176,13 +176,27 @@ export function updateCachedAcls(doc: Doc) {
         }
 
         if (doc.proto instanceof Promise) {
-            doc.proto.then(proto => updateCachedAcls(DocCast(proto)));
+            doc.proto.then(proto => DocCast(proto) && updateCachedAcls(DocCast(proto)!));
             return doc.proto;
         }
     }
     return undefined;
 }
 
+/**
+ * computes a field name for where to store and expanded template Doc
+ * The format is layout_[ROOT_TEMPLATE_NMAE]_[ROOT_TEMPLATE_CHILD_NAME]_...
+ * @param template the template (either a root or a root child Doc)
+ * @param layoutFieldKey the fieldKey of the container of the template
+ * @returns field key to store expanded template Doc
+ */
+export function expandedFieldName(template: Doc, layoutFieldKey?: string) {
+    const layout_key = !layoutFieldKey?.endsWith(']') 
+            ? 'layout'          // layout_SOMETHING = SOMETHING => layout_[SOMETHING] = [SOMETHING]
+            : layoutFieldKey; // prettier-ignore
+    const tempTitle = '[' + StrCast(template.title).replace(/^\[(.*)\]$/, '$1') + ']';
+    return `${layout_key}_${tempTitle}`; // prettier-ignore
+}
 @scriptingGlobal
 @Deserializable('Doc', (obj: unknown) => updateCachedAcls(obj as Doc), ['id'])
 export class Doc extends RefField {
@@ -266,22 +280,28 @@ export class Doc extends RefField {
     public static set ActiveDashboard(val: Opt<Doc>) {  Doc.UserDoc().activeDashboard = val; } // prettier-ignore
     public static get MyFilterHotKeys()       { return DocListCast(DocCast(DocCast(Doc.UserDoc().myContextMenuBtns)?.Filter)?.data).filter(key => key.toolType !== "-opts-"); } // prettier-ignore
     public static RemFromFilterHotKeys(doc: Doc) {
-        return Doc.RemoveDocFromList(DocCast(DocCast(Doc.UserDoc().myContextMenuBtns)?.Filter), 'data', doc);
+        return (filters => filters && Doc.RemoveDocFromList(filters, 'data', doc))(DocCast(DocCast(Doc.UserDoc().myContextMenuBtns)?.Filter));
     }
     public static AddToFilterHotKeys(doc: Doc) {
-        return Doc.AddDocToList(DocCast(DocCast(Doc.UserDoc().myContextMenuBtns)?.Filter), 'data', doc);
+        return (btns => btns && Doc.AddDocToList(btns, 'data', doc))(DocCast(DocCast(Doc.UserDoc().myContextMenuBtns)?.Filter));
     }
     public static IsInMyOverlay(doc: Doc)     { return Doc.MyOverlayDocs.includes(doc); } // prettier-ignore
-    public static AddToMyOverlay(doc: Doc)    { return Doc.ActiveDashboard ? Doc.AddDocToList(Doc.ActiveDashboard, 'myOverlayDocs', doc) : Doc.AddDocToList(DocCast(Doc.UserDoc().myOverlayDocs), undefined, doc); } // prettier-ignore
-    public static RemFromMyOverlay(doc: Doc)  { return Doc.ActiveDashboard ? Doc.RemoveDocFromList(Doc.ActiveDashboard,'myOverlayDocs', doc) : Doc.RemoveDocFromList(DocCast(Doc.UserDoc().myOverlayDocs), undefined, doc); } // prettier-ignore
-    public static AddToMyPublished(doc: Doc)  {
+    public static AddToMyOverlay(doc: Doc) {
+        return Doc.ActiveDashboard && Doc.AddDocToList(Doc.ActiveDashboard, 'myOverlayDocs', doc);
+    } //  : Doc.AddDocToList(DocCast(Doc.UserDoc().myOverlayDocs), undefined, doc); } // prettier-ignore
+    public static RemFromMyOverlay(doc: Doc) {
+        return Doc.ActiveDashboard && Doc.RemoveDocFromList(Doc.ActiveDashboard, 'myOverlayDocs', doc);
+    } //  : Doc.RemoveDocFromList(DocCast(Doc.UserDoc().myOverlayDocs), undefined, doc); } // prettier-ignore
+    public static AddToMyPublished(doc: Doc) {
         doc.$title_custom = true;
-        doc.$layout_showTitle = 'title'; 
-        Doc.ActiveDashboard ? Doc.AddDocToList(Doc.ActiveDashboard, 'myPublishedDocs', doc) : Doc.AddDocToList(DocCast(Doc.UserDoc().myPublishedDocs), undefined, doc); } // prettier-ignore
-    public static RemFromMyPublished(doc: Doc){ 
+        doc.$layout_showTitle = 'title';
+        Doc.ActiveDashboard && Doc.AddDocToList(Doc.ActiveDashboard, 'myPublishedDocs', doc);
+    } //  : Doc.AddDocToList(DocCast(Doc.UserDoc().myPublishedDocs), undefined, doc); } // prettier-ignore
+    public static RemFromMyPublished(doc: Doc) {
         doc.$title_custom = false;
         doc.$layout_showTitle = undefined;
-        Doc.ActiveDashboard ? Doc.RemoveDocFromList(Doc.ActiveDashboard,'myPublishedDocs', doc) : Doc.RemoveDocFromList(DocCast(Doc.UserDoc().myPublishedDocs), undefined, doc); } // prettier-ignore
+        Doc.ActiveDashboard && Doc.RemoveDocFromList(Doc.ActiveDashboard, 'myPublishedDocs', doc);
+    } // : Doc.RemoveDocFromList(DocCast(Doc.UserDoc().myPublishedDocs), undefined, doc); } // prettier-ignore
     public static IsComicStyle(doc?: Doc)     { return doc && Doc.ActiveDashboard && !Doc.IsSystem(doc) && Doc.UserDoc().renderStyle === 'comic' ; } // prettier-ignore
 
     constructor(id?: FieldId, forceSave?: boolean) {
@@ -407,20 +427,17 @@ export class Doc extends RefField {
     }
     @computed get __DATA__(): Doc {
         const self = this[SelfProxy];
-        return self.rootDocument && !self.isTemplateForField ? self : Doc.GetProto(Cast(self[DocLayout].rootDocument, Doc, null) || self);
+        return self.rootDocument && !self.isTemplateForField ? self : Doc.GetProto(DocCast(self[DocLayout].rootDocument, self)!);
     }
     @computed get __LAYOUT__(): Doc {
         const self = this[SelfProxy];
         const templateLayoutDoc = Cast(Doc.LayoutField(self), Doc, null);
         if (templateLayoutDoc) {
-            let renderFieldKey: string = '';
             const layoutField = templateLayoutDoc[StrCast(templateLayoutDoc.layout_fieldKey, 'layout')];
-            if (typeof layoutField === 'string') {
-                [renderFieldKey] = layoutField.split("fieldKey={'")[1].split("'"); // layoutField.split("'")[1];
-            } else {
-                return Cast(layoutField, Doc, null);
+            if (typeof layoutField !== 'string') {
+                return DocCast(layoutField, self)!;
             }
-            return Cast(self['layout_' + templateLayoutDoc.title + '(' + renderFieldKey + ')'], Doc, null) || templateLayoutDoc;
+            return DocCast(self[expandedFieldName(templateLayoutDoc)], templateLayoutDoc)!;
         }
         return self;
     }
@@ -597,7 +614,7 @@ export namespace Doc {
     // return the doc's proto, but rather recursively searches through the proto inheritance chain
     // and returns the document who's proto is undefined or whose proto is marked as a data doc ('isDataDoc').
     export function GetProto(doc: Doc): Doc {
-        const proto = doc && (Doc.GetT(doc, 'isDataDoc', 'boolean', true) ? doc : DocCast(doc.proto, doc));
+        const proto = doc && (Doc.GetT(doc, 'isDataDoc', 'boolean', true) ? doc : DocCast(doc.proto, doc)!);
         return proto === doc ? proto : Doc.GetProto(proto);
     }
     export function GetDataDoc(doc: Doc): Doc {
@@ -630,7 +647,7 @@ export namespace Doc {
      * @returns true if successful, false otherwise.
      */
     export function RemoveDocFromList(listDoc: Doc, fieldKey: string | undefined, doc: Doc, ignoreProto = false) {
-        const key = fieldKey || Doc.LayoutFieldKey(listDoc);
+        const key = fieldKey || Doc.LayoutDataKey(listDoc);
         const list = Doc.Get(listDoc, key, ignoreProto) === undefined ? (listDoc['$' + key] = new List<Doc>()) : Cast(listDoc[key], listSpec(Doc));
         if (list) {
             const ind = list.indexOf(doc);
@@ -647,7 +664,7 @@ export namespace Doc {
      * @returns true if successful, false otherwise.
      */
     export function AddDocToList(listDoc: Doc, fieldKey: string | undefined, doc: Doc, relativeTo?: Doc, before?: boolean, first?: boolean, allowDuplicates?: boolean, reversed?: boolean, ignoreProto?: boolean) {
-        const key = fieldKey || Doc.LayoutFieldKey(listDoc);
+        const key = fieldKey || Doc.LayoutDataKey(listDoc);
         const list = Doc.Get(listDoc, key, ignoreProto) === undefined ? (listDoc['$' + key] = new List<Doc>()) : Cast(listDoc[key], listSpec(Doc));
         if (list) {
             if (!allowDuplicates) {
@@ -686,6 +703,14 @@ export namespace Doc {
         return DocListCast(Doc.Get(doc[DocData], 'proto_embeddings', true));
     }
 
+    /**
+     * Makes an embedding of a Doc.  This Doc shares the data portion of the origiginal Doc.
+     * If the copied Doc has no prototype, then instead of copying the Doc, this just creates
+     * a new Doc that is a delegate of the original Doc.
+     * @param doc Doc to embed
+     * @param id id to use for embedded Doc
+     * @returns a new Doc that is an embedding of the original Doc
+     */
     export function MakeEmbedding(doc: Doc, id?: string) {
         const embedding = (!Doc.IsDataProto(doc) && doc.proto) || doc.type === DocumentType.CONFIG ? Doc.MakeCopy(doc, undefined, id) : Doc.MakeDelegate(doc, id);
         embedding.createdFrom = doc;
@@ -705,17 +730,9 @@ export namespace Doc {
 
     // this lists out all the tag ids that can be  in a RichTextField that might contain document ids.
     // if a document is cloned, we need to make sure to clone all of these referenced documents as well;
-    export const DocsInTextFieldIds = ['audioId', 'textId', 'anchorId', 'docId'];
-    export async function makeClone(
-        doc: Doc,
-        cloneMap: Map<string, Doc>,
-        linkMap: Map<string, Doc>,
-        rtfs: { copy: Doc; key: string; field: RichTextField }[],
-        exclusions: string[],
-        pruneDocs: Doc[],
-        cloneLinks: boolean,
-        cloneTemplates: boolean
-    ): Promise<Doc> {
+    const FindDocsInRTF = new RegExp(/(audioId|textId|anchorId|docId)"\s*:\s*"(.*?)"/g);
+
+    export function makeClone(doc: Doc, cloneMap: Map<string, Doc>, linkMap: Map<string, Doc>, rtfs: { copy: Doc; key: string; field: RichTextField }[], exclusions: string[], pruneDocs: Doc[], cloneLinks: boolean, cloneTemplates: boolean): Doc {
         if (Doc.IsBaseProto(doc) || ((Doc.isTemplateDoc(doc) || Doc.isTemplateForField(doc)) && !cloneTemplates)) {
             return doc;
         }
@@ -723,80 +740,59 @@ export namespace Doc {
         const copy = new Doc(undefined, true);
         cloneMap.set(doc[Id], copy);
         const filter = [...exclusions, ...StrListCast(doc.cloneFieldFilter)];
-        await Promise.all(
-            Object.keys(doc).map(async key => {
-                if (filter.includes(key)) return;
-                const assignKey = (val: Opt<FieldType>) => {
-                    copy[key] = val;
-                };
-                const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
-                const field = ProxyField.WithoutProxy(() => doc[key]);
-                const copyObjectField = async (objField: ObjectField) => {
-                    const list = await Cast(doc[key], listSpec(Doc));
-                    const docs = list && (await DocListCastAsync(list))?.filter(d => d instanceof Doc);
-                    if (docs !== undefined && docs.length) {
-                        const clones = await Promise.all(docs.map(async d => Doc.makeClone(d, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates)));
-                        assignKey(new List<Doc>(clones));
-                    } else {
-                        assignKey(ObjectField.MakeCopy(objField));
-                        if (objField instanceof RichTextField) {
-                            if (DocsInTextFieldIds.some(id => objField.Data.includes(`"${id}":`))) {
-                                const docidsearch = new RegExp('(' + DocsInTextFieldIds.map(exp => '(' + exp + ')').join('|') + ')":"([a-z-A-Z0-9_]*)"', 'g');
-                                const rawdocids = objField.Data.match(docidsearch);
-                                const docids = rawdocids?.map((str: string) =>
-                                    DocsInTextFieldIds.reduce((output, exp) => output.replace(new RegExp(`${exp}":`, 'g'), ''), str)
-                                        .replace(/"/g, '')
-                                        .trim()
-                                );
-                                const results = docids && (await DocServer.GetRefFields(docids));
-                                const rdocs = results && Array.from(Object.keys(results)).map(rkey => DocCast(results.get(rkey)));
-                                rdocs?.map(d => d && Doc.makeClone(d, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates));
-                                rtfs.push({ copy, key, field: objField });
-                            }
-                        }
-                    }
-                };
-                const docAtKey = doc[key];
+        Object.keys(doc)
+            .filter(key => !filter.includes(key))
+            .map(key => {
+                const assignKey = (val: Opt<FieldType>) => (copy[key] = val);
+
                 if (key === 'author') {
-                    assignKey(ClientUtils.CurrentUserEmail());
-                } else if (docAtKey instanceof Doc) {
-                    if (pruneDocs.includes(docAtKey)) {
-                        // prune doc and do nothing
-                    } else if (
-                        !Doc.IsSystem(docAtKey) &&
-                        (key.startsWith('layout') ||
-                            ['embedContainer', 'annotationOn', 'proto'].includes(key) || //
-                            (['link_anchor_1', 'link_anchor_2'].includes(key) && doc.author === ClientUtils.CurrentUserEmail()))
-                    ) {
-                        assignKey(await Doc.makeClone(docAtKey, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates));
-                    } else {
-                        assignKey(docAtKey);
+                    return assignKey(ClientUtils.CurrentUserEmail());
+                }
+                const cfield = ComputedField.DisableCompute(() => doc[key]);
+                if (cfield instanceof ComputedField) {
+                    return assignKey(cfield[Copy]());
+                }
+                const field = doc[key];
+                if (field instanceof Doc) {
+                    const doCopy = () => Doc.IsSystem(field) ||
+                    !( key.startsWith('layout') ||
+                        ['embedContainer', 'annotationOn', 'proto'].includes(key) || //
+                        (['link_anchor_1', 'link_anchor_2'].includes(key) && doc.author === ClientUtils.CurrentUserEmail()) ); // prettier-ignore
+                    return !pruneDocs.includes(field) && 
+                    assignKey(doCopy()
+                        ? field //
+                        : Doc.makeClone(field, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates)); // prettier-ignore
+                }
+                if (field instanceof RichTextField) {
+                    rtfs.push({ copy, key, field });
+                    let docId: string | undefined;
+                    while ((docId = (FindDocsInRTF.exec(field.Data) ?? [undefined, undefined, undefined])[2])) {
+                        const docCopy = DocServer.GetCachedRefField(docId);
+                        docCopy && Doc.makeClone(docCopy, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates);
                     }
-                } else if (field instanceof RefField) {
-                    assignKey(field);
-                } else if (cfield instanceof ComputedField) {
-                    assignKey(cfield[Copy]());
-                } else if (field instanceof ObjectField) {
-                    await copyObjectField(field);
-                } else if (field instanceof Promise) {
-                    // eslint-disable-next-line no-debugger
-                    debugger; // This shouldn't happen...
-                } else {
-                    assignKey(field);
+                    return assignKey(ObjectField.MakeCopy(field));
                 }
-            })
-        );
-        Array.from(doc[DirectLinks]).forEach(async link => {
+                if (field instanceof ObjectField) {
+                    if (DocListCast(field).length) {
+                        return assignKey(new List<Doc>(DocListCast(field).map(d => Doc.makeClone(d, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates))));
+                    }
+                    return assignKey(ObjectField.MakeCopy(field)); // otherwise just copy the field
+                }
+                if (!(field instanceof Promise)) return assignKey(field);
+                // eslint-disable-next-line no-debugger
+                debugger; // This shouldn't happen...
+            });
+        Array.from(doc[DirectLinks]).forEach(link => {
             if (
                 cloneLinks ||
-                ((cloneMap.has(DocCast(link.link_anchor_1)?.[Id]) || cloneMap.has(DocCast(DocCast(link.link_anchor_1)?.annotationOn)?.[Id])) &&
-                    (cloneMap.has(DocCast(link.link_anchor_2)?.[Id]) || cloneMap.has(DocCast(DocCast(link.link_anchor_2)?.annotationOn)?.[Id])))
+                ((cloneMap.has(DocCast(link.link_anchor_1)?.[Id] ?? '') || cloneMap.has(DocCast(DocCast(link.link_anchor_1)?.annotationOn)?.[Id] ?? '')) &&
+                    (cloneMap.has(DocCast(link.link_anchor_2)?.[Id] ?? '') || cloneMap.has(DocCast(DocCast(link.link_anchor_2)?.annotationOn)?.[Id] ?? '')))
             ) {
-                linkMap.set(link[Id], await Doc.makeClone(link, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates));
+                linkMap.set(link[Id], Doc.makeClone(link, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates));
             }
         });
         copy.cloneOf = doc;
-        const cfield = ComputedField.WithoutComputed(() => FieldValue(doc.title));
+        const cfield = ComputedField.DisableCompute(() => FieldValue(doc.title));
         if (Doc.Get(copy, 'title', true) && !(cfield instanceof ComputedField)) copy.title = '>:' + doc.title;
         cloneMap.set(doc[Id], copy);
 
@@ -823,25 +819,27 @@ export namespace Doc {
         return docs.map(doc => Doc.MakeClone(doc, cloneLinks, cloneTemplates, cloneMap));
     }
 
-    export async function MakeClone(doc: Doc, cloneLinks = true, cloneTemplates = true, cloneMap: Map<string, Doc> = new Map()) {
+    /**
+     * Copies a Doc and all of the Docs that it references.  This is a deep copy of the Doc.
+     * However, the additional flags allow you to control whether to copy links and templates.
+     * @param doc Doc to clone
+     * @param cloneLinks whether to clone links to this Doc
+     * @param cloneTemplates whether to clone the templates used by this Doc
+     * @param cloneMap a map from the Doc ids of the original Doc to the new Docs
+     * @returns a clone of the original Doc
+     */
+    export function MakeClone(doc: Doc, cloneLinks = true, cloneTemplates = true, cloneMap: Map<string, Doc> = new Map()) {
         const linkMap = new Map<string, Doc>();
         const rtfMap: { copy: Doc; key: string; field: RichTextField }[] = [];
-        const clone = await Doc.makeClone(doc, cloneMap, linkMap, rtfMap, ['cloneOf'], doc.embedContainer ? [DocCast(doc.embedContainer)] : [], cloneLinks, cloneTemplates);
+        const clone = Doc.makeClone(doc, cloneMap, linkMap, rtfMap, ['cloneOf'], DocCast(doc.embedContainer) ? [DocCast(doc.embedContainer)!] : [], cloneLinks, cloneTemplates);
         const repaired = new Set<Doc>();
         const linkedDocs = Array.from(linkMap.values());
         linkedDocs.forEach(link => Doc.AddLink?.(link, true));
         rtfMap.forEach(({ copy, key, field }) => {
-            const replacer = (match: string, attr: string, id: string /* , offset: any, string: any */) => {
-                const mapped = cloneMap.get(id);
-                return attr + '"' + (mapped ? mapped[Id] : id) + '"';
-            };
-            const replacer2 = (match: string, href: string, id: string /* , offset: any, string: any */) => {
-                const mapped = cloneMap.get(id);
-                return href + (mapped ? mapped[Id] : id);
-            };
+            const replacer = (match: string, attr: string, id: string) => attr + '":"' + (cloneMap.get(id)?.[Id] ?? id) + '"';
+            const replacer2 = (match: string, href: string, id: string) => href + (cloneMap.get(id)?.[Id] ?? id);
             const re = new RegExp(`(${Doc.localServerPath()})([^"]*)`, 'g');
-            const docidsearch = new RegExp('(' + DocsInTextFieldIds.map(exp => `"${exp}":`).join('|') + ')"([^"]+)"', 'g');
-            copy[key] = new RichTextField(field.Data.replace(docidsearch, replacer).replace(re, replacer2), field.Text);
+            copy[key] = new RichTextField(field.Data.replace(FindDocsInRTF, replacer).replace(re, replacer2), field.Text);
         });
         const clonedDocs = [...Array.from(cloneMap.values()), ...linkedDocs];
         clonedDocs.forEach(cloneDoc => Doc.repairClone(cloneDoc, cloneMap, cloneTemplates, repaired));
@@ -849,23 +847,29 @@ export namespace Doc {
     }
 
     const _pendingMap = new Set<string>();
-    //
-    // Returns an expanded template layout for a target data document if there is a template relationship
-    // between the two. If so, the layoutDoc is expanded into a new document that inherits the properties
-    // of the original layout while allowing for individual layout properties to be overridden in the expanded layout.
-    export function expandTemplateLayout(templateLayoutDoc: Doc, targetDoc?: Doc) {
+    /**
+     * Returns an expanded template layout for a target data document if there is a template relationship
+     * between the two. If so, the layoutDoc is expanded into a new document that inherits the properties
+     * of the original layout while allowing for individual layout properties to be overridden in the expanded layout
+     * @param templateLayoutDoc a rendering template Doc
+     * @param targetDoc the Doc that the render template will be applied to
+     * @param layoutFieldKey the accumulated layoutFieldKey for the container of this expanded template
+     * @returns a Doc to use to render the targetDoc in the style of the template layout
+     */
+    export function expandTemplateLayout(templateLayoutDoc: Doc, targetDoc?: Doc, layoutFieldKey?: string) {
         // nothing to do if the layout isn't a template or we don't have a target that's different than the template
         if (!targetDoc || templateLayoutDoc === targetDoc || (!Doc.isTemplateForField(templateLayoutDoc) && !Doc.isTemplateDoc(templateLayoutDoc))) {
             return templateLayoutDoc;
         }
 
-        const templateField = StrCast(templateLayoutDoc.isTemplateForField, Doc.LayoutFieldKey(templateLayoutDoc)); // the field that the template renders
+        const templateField = StrCast(templateLayoutDoc.isTemplateForField, Doc.LayoutDataKey(templateLayoutDoc)); // the field that the template renders
+
         // First it checks if an expanded layout already exists -- if so it will be stored on the dataDoc
         // using the template layout doc's id as the field key.
         // If it doesn't find the expanded layout, then it makes a delegate of the template layout and
         // saves it on the data doc indexed by the template layout's id.
         //
-        const expandedLayoutFieldKey = 'layout_' + templateLayoutDoc.title + '(' + templateField + ')';
+        const expandedLayoutFieldKey = expandedFieldName(templateLayoutDoc, layoutFieldKey);
         let expandedTemplateLayout = targetDoc?.[expandedLayoutFieldKey];
 
         if (templateLayoutDoc.rootDocument instanceof Promise) {
@@ -876,7 +880,7 @@ export namespace Doc {
                 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.rootDocument && (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(
@@ -885,6 +889,7 @@ export namespace Doc {
                             const dataDoc = Doc.GetProto(targetDoc);
                             newLayoutDoc.rootDocument = targetDoc;
                             newLayoutDoc.embedContainer = targetDoc;
+                            newLayoutDoc.cloneOnCopy = true;
                             newLayoutDoc.acl_Guest = SharingPermissions.Edit;
                             if (dataDoc[templateField] === undefined && (templateLayoutDoc[templateField] as List<Doc>)?.length) {
                                 dataDoc[templateField] = ObjectField.MakeCopy(templateLayoutDoc[templateField] as List<Doc>);
@@ -901,87 +906,78 @@ export namespace Doc {
         return expandedTemplateLayout instanceof Doc ? expandedTemplateLayout : undefined; // layout is undefined if the expandedTemplateLayout is pending.
     }
 
-    // if the childDoc is a template for a field, then this will return the expanded layout with its data doc.
-    // otherwise, it just returns the childDoc
-    export function GetLayoutDataDocPair(containerDoc: Doc, containerDataDoc: Opt<Doc>, childDoc: Doc) {
+    /**
+     * Returns a layout and data Doc pair to use to render the specified childDoc of the container.
+     * if the childDoc is a template for a field, then this will return the expanded layout with its data doc.
+     * otherwise, only a layout is returned since that will contain the data as its prototype.
+     * @param containerDoc the template container (that may be nested within a template, the root of a template, or not a template)
+     * @param containerDataDoc the template container's data doc (if the container is nested within the template)
+     * @param childDoc the doc to render
+     * @param layoutFieldKey the accumulated layoutFieldKey for the container of the doc being rendered
+     * @returns a layout Doc to render and an optional data Doc if the layout is a template
+     */
+    export function GetLayoutDataDocPair(containerDoc: Doc, containerDataDoc: Opt<Doc>, childDoc: Doc, layoutFieldKey?: string) {
         if (!childDoc || childDoc instanceof Promise || !Doc.GetProto(childDoc)) {
             console.log('Warning: GetLayoutDataDocPair childDoc not defined');
             return { layout: childDoc, data: childDoc };
         }
         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 };
+        return { layout: Doc.expandTemplateLayout(childDoc, templateRoot, layoutFieldKey), data };
     }
 
-    export function FindReferences(infield: Doc | List<Doc>, references: Set<Doc>, system: boolean | undefined) {
-        if (infield instanceof Promise) return;
-        if (!(infield instanceof Doc)) {
-            infield?.forEach(val => (val instanceof Doc || val instanceof List) && FindReferences(val, references, system));
-            return;
+    /**
+     * Recursively travels through all the metadata reachable from the Doc to find all referenced Docs
+     * @param doc Doc to search
+     * @param references all Docs reachable from the Doc
+     * @param system whether to include system Docs
+     * @returns all Docs reachable from the Doc
+     */
+    export function FindReferences(doc: Doc | List<Doc> | undefined, references: Set<Doc>, system: boolean | undefined) {
+        if (!doc || doc instanceof Promise) {
+            return references;
+        }
+        if (!(doc instanceof Doc)) {
+            doc?.forEach(val => (val instanceof Doc || val instanceof List) && FindReferences(val, references, system));
+            return references;
         }
-        const doc = infield as Doc;
         if (references.has(doc)) {
-            references.add(doc);
-            return;
+            return references;
+        }
+        if (system !== undefined && ((system && !Doc.IsSystem(doc)) || (!system && Doc.IsSystem(doc)))) {
+            return references;
         }
-        const excludeLists = [Doc.MyRecentlyClosed, Doc.MyHeaderBar, Doc.MyDashboards].includes(doc);
-        if (system !== undefined && ((system && !Doc.IsSystem(doc)) || (!system && Doc.IsSystem(doc)))) return;
+
         references.add(doc);
-        Object.keys(doc).forEach(key => {
-            if (key === 'proto') {
-                if (doc.proto instanceof Doc) {
-                    Doc.FindReferences(doc.proto, references, system);
+        Object.keys(doc)
+            .filter(key => key !== 'author' && !(ComputedField.DisableCompute(() => FieldValue(doc[key])) instanceof ComputedField))
+            .map(key => doc[key])
+            .forEach(field => {
+                if (field instanceof List) {
+                    return ![Doc.MyRecentlyClosed, Doc.MyHeaderBar, Doc.MyDashboards].includes(doc) && Doc.FindReferences(field, references, system);
                 }
-            } else {
-                const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
-                const field = key === 'author' ? ClientUtils.CurrentUserEmail() : ProxyField.WithoutProxy(() => doc[key]);
-                if (field instanceof RefField) {
-                    if (field instanceof Doc) {
-                        if (key === 'myLinkDatabase') {
-                            field instanceof Doc && references.add(field);
-                            // skip docs that have been closed and are scheduled for garbage collection
-                        } else {
-                            Doc.FindReferences(field, references, system);
-                        }
-                    }
-                } else if (cfield instanceof ComputedField) {
-                    /* empty */
-                } else if (field instanceof ObjectField) {
-                    if (field instanceof Doc) {
-                        Doc.FindReferences(field, references, system);
-                    } else if (field instanceof List) {
-                        !excludeLists && Doc.FindReferences(field, references, system);
-                    } else if (field instanceof ProxyField) {
-                        if (key === 'myLinkDatabase') {
-                            field instanceof Doc && references.add(field);
-                            // skip docs that have been closed and are scheduled for garbage collection
-                        } else {
-                            Doc.FindReferences(field.value, references, system);
-                        }
-                    } else if (field instanceof PrefetchProxy) {
-                        Doc.FindReferences(field.value, references, system);
-                    } else if (field instanceof RichTextField) {
-                        const re = /"docId"\s*:\s*"(.*?)"/g;
-                        let match: string[] | null;
-                        while ((match = re.exec(field.Data)) !== null) {
-                            const urlString = match[1];
-                            if (urlString) {
-                                const rdoc = DocServer.GetCachedRefField(urlString);
-                                if (rdoc) {
-                                    references.add(rdoc);
-                                    Doc.FindReferences(rdoc, references, system);
-                                }
-                            }
-                        }
+                if (field instanceof Doc) {
+                    return Doc.FindReferences(field, references, system);
+                }
+                if (field instanceof RichTextField) {
+                    let docId: string | undefined;
+                    while ((docId = (FindDocsInRTF.exec(field.Data) ?? [undefined, undefined, undefined])[2])) {
+                        Doc.FindReferences(DocServer.GetCachedRefField(docId), references, system);
                     }
-                } else if (field instanceof Promise) {
-                    // eslint-disable-next-line no-debugger
-                    debugger; // This shouldn't happend...
                 }
-            }
-        });
+            });
+        return references;
     }
 
+    /**
+     * Copies a Doc by copying its embedding (and optionally its prototype).  Values within the Doc are copied except for Docs which are
+     * sipmly referenced (except if they're marked to be copied - eg., template layout Docs)
+     * @param doc Doc to copy
+     * @param copyProto  whether to copy the Docs proto
+     * @param copyProtoId  the id to use for the proto if copied
+     * @param retitle whether to retitle the copy by adding a copy number to the title
+     * @returns the copied Doc
+     */
     export function MakeCopy(doc: Doc, copyProto: boolean = false, copyProtoId?: string, retitle = false): Doc {
         const copy = runInAction(() => new Doc(copyProtoId, true));
         updateCachedAcls(copy);
@@ -994,24 +990,18 @@ export namespace Doc {
                         copy[key] = Doc.MakeCopy(doc.proto, false);
                     }
                 } else {
-                    const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
-                    const field = key === 'author' ? ClientUtils.CurrentUserEmail() : ProxyField.WithoutProxy(() => doc[key]);
-                    if (field instanceof RefField) {
-                        copy[key] = field;
-                    } else if (cfield instanceof ComputedField) {
-                        copy[key] = cfield[Copy](); // ComputedField.MakeFunction(cfield.script.originalScript);
-                    } else if (field instanceof ObjectField) {
-                        const docAtKey = doc[key];
-                        copy[key] =
-                            docAtKey instanceof Doc && (key.includes('layout[') || docAtKey.cloneOnCopy)
-                                ? new ProxyField(Doc.MakeCopy(docAtKey)) // copy the expanded render template
-                                : ObjectField.MakeCopy(field);
-                    } else if (field instanceof Promise) {
+                    const field = key === 'author' ? ClientUtils.CurrentUserEmail() : doc[key];
+                    if (field instanceof Promise) {
                         // eslint-disable-next-line no-debugger
                         debugger; // This shouldn't happend...
-                    } else {
-                        copy[key] = field;
                     }
+                    copy[key] = (() => {
+                        const cfield = ComputedField.DisableCompute(() => FieldValue(doc[key]));
+                        if (cfield instanceof ComputedField) return cfield[Copy]();
+                        if (field instanceof Doc) return field.cloneOnCopy ? Doc.MakeCopy(field) : field; // copy the expanded render template
+                        if (field instanceof ObjectField) return ObjectField.MakeCopy(field);
+                        return field;
+                    })();
                 }
             });
         if (copyProto) {
@@ -1027,6 +1017,14 @@ export namespace Doc {
         return copy;
     }
 
+    /**
+     * Makes a delegate of a prototype Doc.  Delegates inherit all of the properties of the
+     * prototype Doc, but can add new properties or mask existing prototype properties.
+     * @param doc prototype Doc to make a delgate of
+     * @param id id to use for delegate
+     * @param title title to use for delegate
+     * @returns a new Doc that is a delegate of the original Doc
+     */
     export function MakeDelegate(doc: Doc, id?: string, title?: string): Doc;
     export function MakeDelegate(doc: Opt<Doc>, id?: string, title?: string): Opt<Doc>;
     export function MakeDelegate(doc: Opt<Doc>, id?: string, title?: string): Opt<Doc> {
@@ -1096,13 +1094,13 @@ export namespace Doc {
     //
     export function MakeMetadataFieldTemplate(templateField: Doc, templateDoc: Opt<Doc>, keepFieldKey = false): boolean {
         // find the metadata field key that this template field doc will display (indicated by its title)
-        const metadataFieldKey = keepFieldKey ? Doc.LayoutFieldKey(templateField) : StrCast(templateField.isTemplateForField) || StrCast(templateField.title).replace(/^-/, '') || Doc.LayoutFieldKey(templateField);
+        const metadataFieldKey = keepFieldKey ? Doc.LayoutDataKey(templateField) : StrCast(templateField.isTemplateForField) || StrCast(templateField.title).replace(/^-/, '') || Doc.LayoutDataKey(templateField);
 
         // update the original template to mark it as a template
         templateField.isTemplateForField = metadataFieldKey;
         !keepFieldKey && (templateField.title = metadataFieldKey);
 
-        const templateFieldValue = templateField[metadataFieldKey] || templateField[Doc.LayoutFieldKey(templateField)];
+        const templateFieldValue = templateField[metadataFieldKey] || templateField[Doc.LayoutDataKey(templateField)];
         // move any data that the template field had been rendering over to the template doc so that things will still be rendered
         // when the template field is adjusted to point to the new metadatafield key.
         // note 1: if the template field contained a list of documents, each of those documents will be converted to templates as well.
@@ -1148,55 +1146,74 @@ export namespace Doc {
         @observable _searchQuery: string = '';
     }
 
-    // the document containing the view layout information - will be the Document itself unless the Document has
-    // a layout field or 'layout' is given.
-    export function Layout(doc: Doc, template?: Doc): Doc {
-        const expandedTemplate = template && Cast(doc['layout_' + template.title + '(' + StrCast(template.isTemplateForField, 'data') + ')'], Doc, null);
+    /**
+     * The layout Doc containing the view layout information - this will be :
+     *   a) the Doc being rendered itself unless
+     *   b) a template Doc stored in the field sepcified by Doc's layout_fieldKey
+     *   c) or the specifeid 'template' Doc;
+     * If a template is specified, it will be expanded to create an instance specific to the rendered doc;
+     * @param doc the doc to render
+     * @param template a Doc to use as a template for the layout
+     * @returns
+     */
+    export function LayoutDoc(doc: Doc, template?: Doc): Doc {
+        const expandedTemplate = template && Cast(doc[expandedFieldName(template)], Doc, null);
         return expandedTemplate || doc[DocLayout];
     }
+    /**
+     * The JSX or Doc value defining how to render the Doc.
+     * @param doc Doc to render
+     * @returns a JSX string or Doc that describes how to render the Doc
+     */
     export function LayoutField(doc: Doc) {
         return doc[StrCast(doc.layout_fieldKey, 'layout')];
     }
-    export function LayoutFieldKey(doc: Doc, templateLayoutString?: string): string {
+    /**
+     * The field key of the Doc where the primary Data can be found to render the Doc.
+     * eg., for an image, this is likely to be the 'data' field which contains an image url,
+     * and for a text doc, this is likely to be the 'text' field ontaingin the text of the doc.
+     * @param doc Doc to render
+     * @param templateLayoutString optional JSX string that specifies the doc's data field key
+     * @returns field key where data is stored on Doc
+     */
+    export function LayoutDataKey(doc: Doc, templateLayoutString?: string): string {
         const match = StrCast(templateLayoutString || doc[DocLayout].layout).match(/fieldKey={'([^']+)'}/);
-        return match?.[1] || ''; // bcz: TODO check on this .  used to always reference 'layout', now it uses the layout speicfied by the current layout_fieldKey
+        return match?.[1] || '';
     }
     export function NativeAspect(doc: Doc, dataDoc?: Doc, useDim?: boolean) {
         return Doc.NativeWidth(doc, dataDoc, useDim) / (Doc.NativeHeight(doc, dataDoc, useDim) || 1);
     }
     export function NativeWidth(doc?: Doc, dataDoc?: Doc, useWidth?: boolean) {
-        return !doc ? 0 : NumCast(doc._nativeWidth, NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + '_nativeWidth'], useWidth ? NumCast(doc._width) : 0));
+        return !doc ? 0 : NumCast(doc._nativeWidth, NumCast((dataDoc || doc)[Doc.LayoutDataKey(doc) + '_nativeWidth'], useWidth ? NumCast(doc._width) : 0));
     }
     export function NativeHeight(doc?: Doc, dataDoc?: Doc, useHeight?: boolean) {
         if (!doc) return 0;
         const nheight = (Doc.NativeWidth(doc, dataDoc, useHeight) / NumCast(doc._width)) * NumCast(doc._height); // divide before multiply to avoid floating point errrorin case nativewidth = width
-        const dheight = NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + '_nativeHeight'], useHeight ? NumCast(doc._height) : 0);
+        const dheight = NumCast((dataDoc || doc)[Doc.LayoutDataKey(doc) + '_nativeHeight'], useHeight ? NumCast(doc._height) : 0);
         return NumCast(doc._nativeHeight, nheight || dheight);
     }
     export function SetNativeWidth(doc: Doc, width: number | undefined, fieldKey?: string) {
-        doc[(fieldKey || Doc.LayoutFieldKey(doc)) + '_nativeWidth'] = width;
+        doc[(fieldKey || Doc.LayoutDataKey(doc)) + '_nativeWidth'] = width;
     }
     export function SetNativeHeight(doc: Doc, height: number | undefined, fieldKey?: string) {
-        doc[(fieldKey || Doc.LayoutFieldKey(doc)) + '_nativeHeight'] = height;
+        doc[(fieldKey || Doc.LayoutDataKey(doc)) + '_nativeHeight'] = height;
     }
 
     const manager = new UserDocData();
-    export function SearchQuery(): string {
+    export function SearchQuery() {
         return manager._searchQuery;
     }
     export function SetSearchQuery(query: string) {
-        runInAction(() => {
-            manager._searchQuery = query;
-        });
+        manager._searchQuery = query;
     }
-    export function UserDoc(): Doc {
+    export function UserDoc() {
         return manager._user_doc;
     }
-    export function SharingDoc(): Doc {
+    export function SharingDoc() {
         return Doc.MySharedDocs;
     }
-    export function LinkDBDoc(): Doc {
-        return Cast(Doc.UserDoc().myLinkDatabase, Doc, null);
+    export function LinkDBDoc() {
+        return DocCast(Doc.UserDoc().myLinkDatabase);
     }
     export function SetUserDoc(doc: Doc) {
         return (manager._user_doc = doc);
@@ -1221,7 +1238,7 @@ export namespace Doc {
         if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(doc[DocData]) === AclPrivate) return doc;
         const result = brushManager.SearchMatchDoc.get(doc);
         const num = Math.abs(result?.searchMatch || 0) + 1;
-        runInAction(() => result && brushManager.SearchMatchDoc.set(doc, { searchMatch: backward ? -num : num }));
+        result && brushManager.SearchMatchDoc.set(doc, { searchMatch: backward ? -num : num });
         return doc;
     }
     export function ClearSearchMatches() {
@@ -1299,7 +1316,7 @@ export namespace Doc {
                 highlightedDocs.add(doc[DocData]);
                 doc[DocData][Highlight] = true;
                 // want to highlight the targets of presentation docs explicitly since following a pres target does not highlight PDf <Annotations> which are not DocumentViews
-                if (doc.presentation_targetDoc) DocCast(doc.presentation_targetDoc)[Highlight] = true;
+                if (DocCast(doc.presentation_targetDoc)) DocCast(doc.presentation_targetDoc)![Highlight] = true;
             }
         });
     }
@@ -1311,7 +1328,7 @@ export namespace Doc {
                 highlightedDocs.delete(doc[DocData]);
                 doc[Highlight] = doc[DocData][Highlight] = false;
                 doc[Animation] = undefined;
-                if (doc.presentation_targetDoc) DocCast(doc.presentation_targetDoc)[Highlight] = false;
+                if (DocCast(doc.presentation_targetDoc)) DocCast(doc.presentation_targetDoc)![Highlight] = false;
             });
         });
     }
@@ -1338,7 +1355,7 @@ export namespace Doc {
     export function setDocRangeFilter(container: Opt<Doc>, key: string, range?: readonly number[], modifiers?: 'remove') {
         if (!container) return;
 
-        const childFiltersByRanges = Cast(container._childFiltersByRanges, listSpec('string'), []);
+        const childFiltersByRanges = StrListCast(container._childFiltersByRanges);
 
         for (let i = 0; i < childFiltersByRanges.length; i += 3) {
             if (childFiltersByRanges[i] === key) {
@@ -1346,7 +1363,7 @@ export namespace Doc {
                 break;
             }
         }
-        if (range !== undefined) {
+        if (range) {
             childFiltersByRanges.push(key);
             childFiltersByRanges.push(range[0].toString());
             childFiltersByRanges.push(range[1].toString());
@@ -1401,7 +1418,7 @@ export namespace Doc {
         });
     }
     export function readDocRangeFilter(doc: Doc, key: string) {
-        const childFiltersByRanges = Cast(doc._childFiltersByRanges, listSpec('string'), []);
+        const childFiltersByRanges = StrListCast(doc._childFiltersByRanges);
         for (let i = 0; i < childFiltersByRanges.length; i += 3) {
             if (childFiltersByRanges[i] === key) {
                 return [Number(childFiltersByRanges[i + 1]), Number(childFiltersByRanges[i + 2])];
@@ -1436,8 +1453,9 @@ export namespace Doc {
         DocServer.GetRefFields(docids).then(async fieldlist => {
             const list = Array.from(fieldlist.values())
                 .map(d => DocCast(d))
-                .filter(d => d);
-            const docs = clone ? (await Promise.all(Doc.MakeClones(list, false, false))).map(res => res.clone) : list;
+                .filter(d => d)
+                .map(d => d!);
+            const docs = clone ? Doc.MakeClones(list, false, false).map(res => res.clone) : list;
             if (ptx !== undefined && pty !== undefined && newPoint !== undefined) {
                 const firstx = list.length ? NumCast(list[0].x) + ptx - newPoint[0] : 0;
                 const firsty = list.length ? NumCast(list[0].y) + pty - newPoint[1] : 0;
@@ -1457,16 +1475,16 @@ export namespace Doc {
      * @returns
      */
     export function getDescription(doc: Doc) {
-        const curDescription = StrCast(doc['$' + Doc.LayoutFieldKey(doc) + '_description']);
+        const curDescription = StrCast(doc['$' + Doc.LayoutDataKey(doc) + '_description']);
         const docText = (async (tdoc:Doc) => {
             switch (tdoc.type) {
                 case DocumentType.PDF: return curDescription || StrCast(tdoc.text).split(/\s+/).slice(0, 50).join(' '); // first 50 words of pdf text
-                case DocumentType.IMG: return curDescription || imageUrlToBase64(ImageCastWithSuffix(tdoc[Doc.LayoutFieldKey(tdoc)], '_o') ?? '')
+                case DocumentType.IMG: return curDescription || imageUrlToBase64(ImageCastWithSuffix(tdoc[Doc.LayoutDataKey(tdoc)], '_o') ?? '')
                                                 .then(hrefBase64 => gptImageLabel(hrefBase64, 'Give three to five labels to describe this image.'));
-                case DocumentType.RTF: return RTFCast(tdoc[Doc.LayoutFieldKey(tdoc)])?.Text ?? StrCast(tdoc[Doc.LayoutFieldKey(tdoc)]);
+                case DocumentType.RTF: return RTFCast(tdoc[Doc.LayoutDataKey(tdoc)])?.Text ?? StrCast(tdoc[Doc.LayoutDataKey(tdoc)]);
                 default:               return StrCast(tdoc.title).startsWith("Untitled") ? "" : StrCast(tdoc.title);
         }}); // prettier-ignore
-        return docText(doc).then(text => (doc['$' + Doc.LayoutFieldKey(doc) + '_description'] = text));
+        return docText(doc).then(text => (doc['$' + Doc.LayoutDataKey(doc) + '_description'] = text));
     }
 
     // prettier-ignore
@@ -1714,7 +1732,7 @@ export function IdToDoc(id: string) {
     return DocCast(DocServer.GetCachedRefField(id));
 }
 // eslint-disable-next-line prefer-arrow-callback
-ScriptingGlobals.add(function idToDoc(id: string): Doc {
+ScriptingGlobals.add(function idToDoc(id: string): Doc | undefined {
     return IdToDoc(id);
 });
 // eslint-disable-next-line prefer-arrow-callback
@@ -1768,7 +1786,7 @@ ScriptingGlobals.add(function docCastAsync(doc: FieldResult): FieldResult<Doc> {
 // eslint-disable-next-line prefer-arrow-callback
 ScriptingGlobals.add(function activePresentationItem() {
     const curPres = Doc.ActivePresentation;
-    return curPres && DocListCast(curPres[Doc.LayoutFieldKey(curPres)])[NumCast(curPres._itemIndex)];
+    return curPres && DocListCast(curPres[Doc.LayoutDataKey(curPres)])[NumCast(curPres._itemIndex)];
 });
 // eslint-disable-next-line prefer-arrow-callback
 ScriptingGlobals.add(function setDocFilter(container: Doc, key: string, value: string, modifiers: 'match' | 'check' | 'x' | 'remove') {
diff --git a/src/fields/List.ts b/src/fields/List.ts
index 22bbcb9ab..ba03c0d38 100644
--- a/src/fields/List.ts
+++ b/src/fields/List.ts
@@ -255,7 +255,7 @@ export class ListImpl<T extends FieldType> extends ObjectField {
         if (fields) {
             this[SelfProxy].push(...fields);
         }
-        // eslint-disable-next-line no-constructor-return
+
         return list; // need to return the proxy here, otherwise we don't get any of our list handler functions
     }
 
@@ -317,8 +317,8 @@ export class ListImpl<T extends FieldType> extends ObjectField {
 
 // declare List as a type so you can use it in type declarations, e.g., { l: List, ...}
 export type List<T extends FieldType> = ListImpl<T> & (T | (T extends RefField ? Promise<T> : never))[];
-// decalre List as a value so you can invoke 'new' on it, e.g., new List<Doc>()  (since List<T> IS ListImpl<T>, we can safely cast the 'new' return value to return List<T>)
-// eslint-disable-next-line no-redeclare
+// declare List as a value so you can invoke 'new' on it, e.g., new List<Doc>()  (since List<T> IS ListImpl<T>, we can safely cast the 'new' return value to return List<T>)
+// eslint-disable-next-line no-use-before-define
 export const List: { new <T extends FieldType>(fields?: T[]): List<T> } = ListImpl as unknown as { new <T extends FieldType>(fields?: T[]): List<T> };
 
 ScriptingGlobals.add('List', List);
diff --git a/src/fields/Proxy.ts b/src/fields/Proxy.ts
index 48c336e60..21b9fe89b 100644
--- a/src/fields/Proxy.ts
+++ b/src/fields/Proxy.ts
@@ -16,9 +16,26 @@ function deserializeProxy(field: serializedProxyType) {
 }
 @Deserializable('proxy', (obj: unknown) => deserializeProxy(obj as serializedProxyType))
 export class ProxyField<T extends Doc> extends ObjectField {
+    @serializable(primitive())
+    readonly fieldId: string = '';
+
+    private failed = false;
+
+    // This getter/setter and nested object thing is
+    // because mobx doesn't play well with observable proxies
+    @observable.ref
+    private _cache: { readonly field: T | undefined; p: FieldWaiting<T> | undefined } = { field: undefined, p: undefined };
+    private get cache(): { field: T | undefined; p: FieldWaiting<T> | undefined } {
+        return this._cache;
+    }
+    private set cache(val: { field: T | undefined; p: FieldWaiting<T> | undefined }) {
+        runInAction(() => (this._cache = { ...val }));
+    }
+
     constructor();
     constructor(value: T);
     constructor(fieldId: string);
+    constructor(value: T | string);
     constructor(value?: T | string) {
         super();
         if (typeof value === 'string') {
@@ -30,13 +47,12 @@ export class ProxyField<T extends Doc> extends ObjectField {
         }
     }
 
-    [ToValue](/* doc: any */) {
+    [ToValue]() {
         return ProxyField.toValue(this);
     }
 
     [Copy]() {
-        if (this.cache.field) return new ProxyField<T>(this.cache.field);
-        return new ProxyField<T>(this.fieldId);
+        return new ProxyField<T>(this.cache.field ?? this.fieldId);
     }
 
     [ToJavascriptString]() {
@@ -46,27 +62,9 @@ export class ProxyField<T extends Doc> extends ObjectField {
         return Field.toScriptString(this[ToValue]()?.value as T); // not sure this is quite right since it doesn't recreate a proxy field, but better than 'invalid' ?
     }
     [ToString]() {
-        return Field.toString(this[ToValue]()?.value);
+        return Field.toString(this[ToValue]()?.value as T);
     }
 
-    @serializable(primitive())
-    readonly fieldId: string = '';
-
-    // This getter/setter and nested object thing is
-    // because mobx doesn't play well with observable proxies
-    @observable.ref
-    private _cache: { readonly field: T | undefined; p: FieldWaiting<T> | undefined } = { field: undefined, p: undefined };
-    private get cache(): { field: T | undefined; p: FieldWaiting<T> | undefined } {
-        return this._cache;
-    }
-    private set cache(val: { field: T | undefined; p: FieldWaiting<T> | undefined }) {
-        runInAction(() => {
-            this._cache = { ...val };
-        });
-    }
-
-    private failed = false;
-
     @computed get value(): T | undefined | FieldWaiting<T> {
         if (this.cache.field) return this.cache.field;
         if (this.failed) return undefined;
@@ -94,32 +92,19 @@ export class ProxyField<T extends Doc> extends ObjectField {
         return field;
     }
 }
-
-// eslint-disable-next-line no-redeclare, @typescript-eslint/no-namespace
 export namespace ProxyField {
     let useProxy = true;
-    export function DisableProxyFields() {
+    export function DisableDereference<T>(fn: () => T) {
         useProxy = false;
-    }
-
-    export function EnableProxyFields() {
-        useProxy = true;
-    }
-
-    export function WithoutProxy<T>(fn: () => T) {
-        DisableProxyFields();
         try {
             return fn();
         } finally {
-            EnableProxyFields();
+            useProxy = true;
         }
     }
 
     export function toValue(value: { value: unknown }) {
-        if (useProxy) {
-            return { value: value.value };
-        }
-        return undefined;
+        return useProxy ? { value: value.value } : undefined;
     }
 }
 
@@ -129,5 +114,6 @@ function prefetchValue(proxy: PrefetchProxy<Doc>) {
 }
 
 @scriptingGlobal
-@Deserializable('prefetch_proxy', (obj:unknown) => prefetchValue(obj as PrefetchProxy<Doc>))
+// eslint-disable-next-line no-use-before-define
+@Deserializable('prefetch_proxy', (obj: unknown) => prefetchValue(obj as PrefetchProxy<Doc>))
 export class PrefetchProxy<T extends Doc> extends ProxyField<T> {}
diff --git a/src/fields/Schema.ts b/src/fields/Schema.ts
index 89e5cda8d..cdd278d67 100644
--- a/src/fields/Schema.ts
+++ b/src/fields/Schema.ts
@@ -1,6 +1,3 @@
-/* eslint-disable guard-for-in */
-/* eslint-disable no-restricted-syntax */
-/* eslint-disable no-redeclare */
 /* eslint-disable no-use-before-define */
 import { Interface, ToInterface, Cast, ToConstructor, HasTail, Head, Tail, ListSpec, ToType, DefaultFieldConstructor } from './Types';
 import { Doc, FieldType } from './Doc';
@@ -16,7 +13,6 @@ type AllToInterface<T extends Interface[]> = {
 
 export const emptySchema = createSchema({});
 export const Document = makeInterface(emptySchema);
-// eslint-disable-next-line no-redeclare
 export type Document = makeInterface<[typeof emptySchema]>;
 
 export interface InterfaceFunc<T extends Interface[]> {
@@ -36,9 +32,10 @@ export function makeInterface<T extends Interface[]>(...schemas: T): InterfaceFu
     const proto = new Proxy(
         {},
         {
-            get(target: any, prop, receiver) {
+            get(target: unknown, prop, receiver) {
                 const field = receiver.doc?.[prop];
                 if (prop in schema) {
+                    // eslint-disable-next-line @typescript-eslint/no-explicit-any
                     const desc = prop === 'proto' ? Doc : (schema as any)[prop]; // bcz: proto doesn't appear in schemas ... maybe it should?
                     if (typeof desc === 'object' && 'defaultVal' in desc && 'type' in desc) {
                         // defaultSpec
@@ -59,7 +56,7 @@ export function makeInterface<T extends Interface[]>(...schemas: T): InterfaceFu
                 }
                 return field;
             },
-            set(target: any, prop, value, receiver) {
+            set(target: unknown, prop, value, receiver) {
                 receiver.doc && (receiver.doc[prop] = value); // receiver.doc may be undefined as the result of a change in acls
                 return true;
             },
@@ -77,10 +74,10 @@ export function makeStrictInterface<T extends Interface>(schema: T): (doc: Doc)
         const type = schema[key];
         Object.defineProperty(proto, key, {
             get() {
-                return Cast(this.__doc[key], type as any);
+                return Cast(this.__doc[key], type as never);
             },
             set(setValue) {
-                const value = Cast(setValue, type as any);
+                const value = Cast(setValue, type as never);
                 if (value !== undefined) {
                     this.__doc[key] = value;
                     return;
@@ -89,7 +86,7 @@ export function makeStrictInterface<T extends Interface>(schema: T): (doc: Doc)
             },
         });
     }
-    return function (doc: any) {
+    return function (doc: unknown) {
         if (!(doc instanceof Doc)) {
             throw new Error("Currently wrapping a schema in another schema isn't supported");
         }
@@ -101,18 +98,18 @@ export function makeStrictInterface<T extends Interface>(schema: T): (doc: Doc)
 
 // eslint-disable-next-line @typescript-eslint/no-unused-vars
 export function createSchema<T extends Interface>(schema: T): T & { proto: ToConstructor<Doc> } {
-    return undefined as any;
+    return undefined as never;
     // (schema as any).proto = Doc;
     // return schema as any;
 }
 
 export function listSpec<U extends ToConstructor<FieldType>>(type: U): ListSpec<ToType<U>> {
-    return { List: type as any }; // TODO Types
+    return { List: type as never }; // TODO Types
 }
 
 export function defaultSpec<T extends ToConstructor<FieldType>>(type: T, defaultVal: ToType<T>): DefaultFieldConstructor<ToType<T>> {
     return {
-        type: type as any,
+        type: type as never,
         defaultVal,
     };
 }
diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts
index 6e2d7eb78..37b65684b 100644
--- a/src/fields/ScriptField.ts
+++ b/src/fields/ScriptField.ts
@@ -149,7 +149,6 @@ export class ScriptField extends ObjectField {
                 ...params,
             },
             transformer,
-            typecheck: false,
             editable: true,
             addReturn: addReturn,
             capturedVariables,
@@ -189,14 +188,12 @@ export class ScriptField extends ObjectField {
 export class ComputedField extends ScriptField {
     static undefined = '__undefined';
     static useComputed = true;
-    static DisableComputedFields() { this.useComputed = false; } // prettier-ignore
-    static EnableComputedFields()  { this.useComputed = true; } // prettier-ignore
-    static WithoutComputed<T>(fn: () => T) {
-        this.DisableComputedFields();
+    static DisableCompute<T>(fn: () => T) {
+        this.useComputed = false;
         try {
             return fn();
         } finally {
-            this.EnableComputedFields();
+            this.useComputed = true;
         }
     }
 
@@ -239,7 +236,8 @@ export class ComputedField extends ScriptField {
     public static MakeInterpolatedNumber(fieldKey: string, interpolatorKey: string, doc: Doc, curTimecode: number, defaultVal: Opt<number>) {
         if (!doc[`${fieldKey}_indexed`]) {
             const flist = new List<number>(numberRange(curTimecode + 1).map(emptyFunction) as unknown as number[]);
-            flist[curTimecode] = Cast(doc[fieldKey], 'number', null);
+            if (Cast(doc[fieldKey], 'number', null) === undefined) delete flist[curTimecode];
+            else flist[curTimecode] = Cast(doc[fieldKey], 'number', null)!;
             doc[`${fieldKey}_indexed`] = flist;
         }
         const getField = ScriptField.CompileScript(`getIndexVal(this['${fieldKey}_indexed'], this.${interpolatorKey}, ${defaultVal})`, {}, true, {});
diff --git a/src/fields/Types.ts b/src/fields/Types.ts
index dc7516f38..e6755f828 100644
--- a/src/fields/Types.ts
+++ b/src/fields/Types.ts
@@ -8,7 +8,7 @@ import { ScriptField } from './ScriptField';
 import { AudioField, CsvField, ImageField, PdfField, VideoField, WebField } from './URLField';
 
 // eslint-disable-next-line no-use-before-define
-export type ToConstructor<T extends FieldType> = T extends string ? 'string' : T extends number ? 'number' : T extends boolean ? 'boolean' : T extends List<infer U> ? ListSpec<U> : new (...args: any[]) => T;
+export type ToConstructor<T extends FieldType> = T extends string ? 'string' : T extends number ? 'number' : T extends boolean ? 'boolean' : T extends List<infer U> ? ListSpec<U> : new (...args: never[]) => T;
 
 export type DefaultFieldConstructor<T extends FieldType> = {
     type: ToConstructor<T>;
@@ -17,7 +17,7 @@ export type DefaultFieldConstructor<T extends FieldType> = {
 // type ListSpec<T extends Field[]> = { List: ToContructor<Head<T>> | ListSpec<Tail<T>> };
 export type ListSpec<T extends FieldType> = { List: ToConstructor<T> };
 
-export type InterfaceValue = ToConstructor<FieldType> | ListSpec<FieldType> | DefaultFieldConstructor<FieldType> | ((doc?: Doc) => any);
+export type InterfaceValue = ToConstructor<FieldType> | ListSpec<FieldType> | DefaultFieldConstructor<FieldType> | ((doc?: Doc) => never);
 
 export type ToType<T extends InterfaceValue> = T extends 'string'
     ? string
@@ -31,9 +31,9 @@ export type ToType<T extends InterfaceValue> = T extends 'string'
             // eslint-disable-next-line @typescript-eslint/no-unused-vars
             T extends DefaultFieldConstructor<infer _U>
             ? never
-            : T extends { new (...args: any[]): List<FieldType> }
+            : T extends { new (...args: never[]): List<FieldType> }
               ? never
-              : T extends { new (...args: any[]): infer R }
+              : T extends { new (...args: never[]): infer R }
                 ? R
                 : T extends (doc?: Doc) => infer R
                   ? R
@@ -41,29 +41,26 @@ export type ToType<T extends InterfaceValue> = T extends 'string'
 
 export interface Interface {
     [key: string]: InterfaceValue;
-    // [key: string]: ToConstructor<Field> | ListSpec<Field[]>;
 }
 export type ToInterface<T extends Interface> = {
     [P in Exclude<keyof T, 'proto'>]: T[P] extends DefaultFieldConstructor<infer F> ? Exclude<FieldResult<F>, undefined> : FieldResult<ToType<T[P]>>;
 };
 
-// type ListType<U extends Field[]> = { 0: List<ListType<Tail<U>>>, 1: ToType<Head<U>> }[HasTail<U> extends true ? 0 : 1];
-
-export type Head<T extends any[]> = T extends [any, ...any[]] ? T[0] : never;
-export type Tail<T extends any[]> = ((...t: T) => any) extends (_: any, ...tail: infer TT) => any ? TT : [];
-export type HasTail<T extends any[]> = T extends [] | [any] ? false : true;
+export type Head<T extends unknown[]> = T extends [unknown, ...unknown[]] ? T[0] : Interface;
+export type Tail<T extends unknown[]> = ((...t: T) => unknown) extends (_: unknown, ...tail: infer TT) => unknown ? TT : [];
+export type HasTail<T extends unknown[]> = T extends [] | [unknown] ? false : true;
 // TODO Allow you to optionally specify default values for schemas, which should then make that field not be partial
-export type WithoutRefField<T extends FieldType> = T extends RefField ? never : T;
+export type WithoutRefField<T extends FieldType> = T extends RefField ? unknown : T;
 
 export type CastCtor = ToConstructor<FieldType> | ListSpec<FieldType>;
 
 type WithoutList<T extends FieldType> = T extends List<infer R> ? (R extends RefField ? (R | Promise<R>)[] : R[]) : T;
 
-export function Cast<T extends CastCtor>(field: FieldResult, ctor: T): FieldResult<ToType<T>>;
-export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal: WithoutList<WithoutRefField<ToType<T>>> | null): WithoutList<ToType<T>>;
+export function Cast<T extends CastCtor>(field: FieldResult, ctor: T): FieldResult<ToType<T>> | undefined;
+export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal: WithoutList<ToType<T>> | null): WithoutList<ToType<T>> | undefined;
 export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal?: ToType<T> | null): FieldResult<ToType<T>> | undefined {
     if (field instanceof Promise) {
-        return defaultVal === undefined ? (field.then(f => Cast(f, ctor) as any) as any) : defaultVal === null ? undefined : defaultVal;
+        return defaultVal === undefined ? (field.then(f => Cast(f, ctor) as ToType<T>) as ToType<T>) : defaultVal === null ? undefined : defaultVal;
     }
     if (field !== undefined && !(field instanceof Promise)) {
         if (typeof ctor === 'string') {
@@ -72,10 +69,12 @@ export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal
             }
         } else if (typeof ctor === 'object') {
             if (field instanceof List) {
-                return field as any;
+                return field as ToType<T>;
             }
+            // eslint-disable-next-line @typescript-eslint/no-explicit-any
         } else if (field instanceof (ctor as any)) {
             return field as ToType<T>;
+            // eslint-disable-next-line @typescript-eslint/no-explicit-any
         } else if (field instanceof ProxyField && field.value instanceof (ctor as any)) {
             return field.value as ToType<T>;
         }
@@ -83,57 +82,27 @@ export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal
     return defaultVal === null ? undefined : defaultVal;
 }
 
-export function toList(doc: Doc | Doc[]) {
-    return doc instanceof Doc ? [doc] : doc;
-}
+export function toList(doc: Doc | Doc[]) { return doc instanceof Doc ? [doc] : doc; } // prettier-ignore
 
 export function DocCast(field: FieldResult, defaultVal?: Doc) {
-    const doc = Cast(field, Doc, null);
-    return doc && !(doc instanceof Promise) ? doc : (defaultVal as Doc);
-}
-
-export function NumCast(field: FieldResult, defaultVal: number | null = 0) {
-    return Cast(field, 'number', defaultVal);
-}
-
-export function StrCast(field: FieldResult, defaultVal: string | null = '') {
-    return Cast(field, 'string', defaultVal);
-}
+    return ((doc: Doc | undefined) => (doc && !(doc instanceof Promise) ? doc : defaultVal))(Cast(field, Doc, null));
+}
+export function NumCast   (field: FieldResult, defaultVal: number | null = 0)          { return Cast(field, 'number', defaultVal)!; } // prettier-ignore
+export function StrCast   (field: FieldResult, defaultVal: string | null = '')         { return Cast(field, 'string', defaultVal)!; } // prettier-ignore
+export function BoolCast  (field: FieldResult, defaultVal: boolean | null = false)     { return Cast(field, 'boolean', defaultVal)!; } // prettier-ignore
+export function DateCast  (field: FieldResult, defaultVal: DateField | null = null)    { return Cast(field, DateField, defaultVal); } // prettier-ignore
+export function RTFCast   (field: FieldResult, defaultVal: RichTextField | null = null){ return Cast(field, RichTextField, defaultVal); } // prettier-ignore
+export function ScriptCast(field: FieldResult, defaultVal: ScriptField | null = null)  { return Cast(field, ScriptField, defaultVal); } // prettier-ignore
+export function CsvCast   (field: FieldResult, defaultVal: CsvField | null = null)     { return Cast(field, CsvField, defaultVal); } // prettier-ignore
+export function WebCast   (field: FieldResult, defaultVal: WebField | null = null)     { return Cast(field, WebField, defaultVal); } // prettier-ignore
+export function VideoCast (field: FieldResult, defaultVal: VideoField | null = null)   { return Cast(field, VideoField, defaultVal); } // prettier-ignore
+export function AudioCast (field: FieldResult, defaultVal: AudioField | null = null)   { return Cast(field, AudioField, defaultVal); } // prettier-ignore
+export function PDFCast   (field: FieldResult, defaultVal: PdfField | null = null)     { return Cast(field, PdfField, defaultVal); } // prettier-ignore
+export function ImageCast (field: FieldResult, defaultVal: ImageField | null = null)   { return Cast(field, ImageField, defaultVal); } // prettier-ignore
 
-export function BoolCast(field: FieldResult, defaultVal: boolean | null = false) {
-    return Cast(field, 'boolean', defaultVal);
-}
-export function DateCast(field: FieldResult) {
-    return Cast(field, DateField, null);
-}
-export function RTFCast(field: FieldResult) {
-    return Cast(field, RichTextField, null);
-}
-
-export function ScriptCast(field: FieldResult, defaultVal: ScriptField | null = null) {
-    return Cast(field, ScriptField, defaultVal);
-}
-export function CsvCast(field: FieldResult, defaultVal: CsvField | null = null) {
-    return Cast(field, CsvField, defaultVal);
-}
-export function WebCast(field: FieldResult, defaultVal: WebField | null = null) {
-    return Cast(field, WebField, defaultVal);
-}
-export function VideoCast(field: FieldResult, defaultVal: VideoField | null = null) {
-    return Cast(field, VideoField, defaultVal);
-}
-export function AudioCast(field: FieldResult, defaultVal: AudioField | null = null) {
-    return Cast(field, AudioField, defaultVal);
-}
-export function PDFCast(field: FieldResult, defaultVal: PdfField | null = null) {
-    return Cast(field, PdfField, defaultVal);
-}
-export function ImageCast(field: FieldResult, defaultVal: ImageField | null = null) {
-    return Cast(field, ImageField, defaultVal);
-}
 export function ImageCastToNameType(field: FieldResult, defaultVal: ImageField | null = null) {
     const href = ImageCast(field, defaultVal)?.url.href;
-    return href ? [href.replace(/.[^.]*$/, ''), href.split('.').lastElement()] : ["", ""];
+    return href ? [href.replace(/.[^.]*$/, ''), href.split('.').lastElement()] : ['', ''];
 }
 export function ImageCastWithSuffix(field: FieldResult, suffix: string, defaultVal: ImageField | null = null) {
     const [name, type] = ImageCastToNameType(field, defaultVal);
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 205c500a5..799fa3758 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -241,19 +241,19 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
 
         dataDoc[DirectLinks].forEach(link => distributeAcls(key, acl, link, visited, !!allowUpgrade));
 
-        DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc)]).forEach(d => {
+        DocListCast(dataDoc[Doc.LayoutDataKey(dataDoc)]).forEach(d => {
             distributeAcls(key, acl, d, visited, !!allowUpgrade);
             d !== d[DocData] && distributeAcls(key, acl, d[DocData], visited, !!allowUpgrade);
         });
 
-        DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + '_annotations']).forEach(d => {
+        DocListCast(dataDoc[Doc.LayoutDataKey(dataDoc) + '_annotations']).forEach(d => {
             distributeAcls(key, acl, d, visited, !!allowUpgrade);
             d !== d[DocData] && distributeAcls(key, acl, d[DocData], visited, !!allowUpgrade);
         });
 
-        Object.keys(target) // share expanded layout templates (eg, for presElementBox'es )
-            .filter(lkey => lkey.includes('layout[') && DocCast(target[lkey]))
-            .forEach(lkey => distributeAcls(key, acl, DocCast(target[lkey]), visited, !!allowUpgrade));
+        Object.keys(target) // share expanded layout templates (eg, for PresSlideBox'es )
+            .filter(lkey => lkey.includes('layout_[') && DocCast(target[lkey]))
+            .forEach(lkey => distributeAcls(key, acl, DocCast(target[lkey])!, visited, !!allowUpgrade));
 
         if (GetEffectiveAcl(dataDoc) === AclAdmin) {
             dataDoc[key] = acl;
-- 
cgit v1.2.3-70-g09d2

