From 932ecd6092bd1b0ac3391309a550bac76cfb0e04 Mon Sep 17 00:00:00 2001
From: Sophie Zhang <sophie_zhang@brown.edu>
Date: Thu, 29 Jun 2023 13:56:41 -0400
Subject: undo and redo

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

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

diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 0dc186eaf..3fa5c099b 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -882,6 +882,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     };
 
     animateRes = (resIndex: number) => {
+        console.log(this.gptRes);
         if (resIndex < this.gptRes.length) {
             this.dataDoc.text = (this.dataDoc.text as RichTextField)?.Text + this.gptRes[resIndex];
             setTimeout(() => {
-- 
cgit v1.2.3-70-g09d2


From 162ca319eae256be523f2ee75e7aae7a9a408e37 Mon Sep 17 00:00:00 2001
From: Sophie Zhang <sophie_zhang@brown.edu>
Date: Tue, 18 Jul 2023 12:06:21 -0400
Subject: .

---
 src/client/views/nodes/formattedText/FormattedTextBox.tsx | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

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

diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 3fa5c099b..307e52cff 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -882,7 +882,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     };
 
     animateRes = (resIndex: number) => {
-        console.log(this.gptRes);
+        console.log(this.dataDoc.text);
         if (resIndex < this.gptRes.length) {
             this.dataDoc.text = (this.dataDoc.text as RichTextField)?.Text + this.gptRes[resIndex];
             setTimeout(() => {
@@ -891,6 +891,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         }
     };
 
+    // breakupdictiation for reference
+    // prepareForTyping
+    // stored marks - userMark and timestamp, setStoredMarks
+    //the._editorView.dispatch(state.tr.setSelection(updatted).insertText('\n, to))
+
     askGPT = action(async () => {
         try {
             let res = await gptAPICall((this.dataDoc.text as RichTextField)?.Text, GPTCallType.COMPLETION);
-- 
cgit v1.2.3-70-g09d2


From 078d595cb498592667a653a937b8ba25bcbf41bb Mon Sep 17 00:00:00 2001
From: Sophie Zhang <sophie_zhang@brown.edu>
Date: Thu, 20 Jul 2023 00:54:07 -0400
Subject: refactoring gpt functionalities

---
 src/client/views/MainView.tsx                      |   2 +
 .../views/nodes/formattedText/FormattedTextBox.tsx |   2 +-
 src/client/views/pdf/AnchorMenu.tsx                | 157 ++++++---------------
 src/client/views/pdf/GPTPopup/GPTPopup.scss        |  12 +-
 src/client/views/pdf/GPTPopup/GPTPopup.tsx         | 102 +++++++++----
 src/client/views/pdf/PDFViewer.tsx                 |   5 +
 6 files changed, 130 insertions(+), 150 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index a129c35cf..f6829a4cb 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -67,6 +67,7 @@ import { PropertiesView } from './PropertiesView';
 import { DashboardStyleProvider, DefaultStyleProvider } from './StyleProvider';
 import { TopBar } from './topbar/TopBar';
 import GenerativeFill from './nodes/generativeFill/GenerativeFill';
+import { GPTPopup } from './pdf/GPTPopup/GPTPopup';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 @observer
@@ -1032,6 +1033,7 @@ export class MainView extends React.Component {
                 <InkTranscription />
                 {this.snapLines}
                 <LightboxView key="lightbox" PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} />
+                <GPTPopup key="gptpopup" />
                 <GenerativeFill imageEditorOpen={this.imageEditorOpen} imageEditorSource={this.imageEditorSource} imageRootDoc={this.imageRootDoc} addDoc={this.addDoc} />
                 {/* <NewLightboxView key="newLightbox" PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} /> */}
             </div>
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 20a176c58..212d24165 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -909,7 +909,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     // breakupdictiation for reference
     // prepareForTyping
     // stored marks - userMark and timestamp, setStoredMarks
-    //the._editorView.dispatch(state.tr.setSelection(updatted).insertText('\n, to))
+    // the._editorView.dispatch(state.tr.setSelection(updated).insertText('\n, to))
 
     askGPT = action(async () => {
         try {
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index 07b2afd91..e6b9cb382 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -48,9 +48,6 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
     @observable public Status: 'marquee' | 'annotation' | '' = '';
 
     // GPT additions
-    @observable private GPTpopupText: string = '';
-    @observable private loadingGPT: boolean = false;
-    @observable private showGPTPopup: boolean = false;
     @observable private GPTMode: GPTPopupMode = GPTPopupMode.SUMMARY;
     @observable private selectedText: string = '';
     @observable private editorView?: EditorView;
@@ -58,25 +55,11 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
     @observable private highlightRange: number[] | undefined;
     private selectionRange: number[] | undefined;
 
-    @action
-    setGPTPopupVis = (vis: boolean) => {
-        this.showGPTPopup = vis;
-    };
     @action
     setGPTMode = (mode: GPTPopupMode) => {
         this.GPTMode = mode;
     };
 
-    @action
-    setGPTPopupText = (txt: string) => {
-        this.GPTpopupText = txt;
-    };
-
-    @action
-    setLoading = (loading: boolean) => {
-        this.loadingGPT = loading;
-    };
-
     @action
     setHighlightRange(r: number[] | undefined) {
         this.highlightRange = r;
@@ -130,19 +113,12 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
     componentDidMount() {
         this._disposer2 = reaction(
             () => this._opacity,
-            opacity => {
-                if (!opacity) {
-                    this.setGPTPopupVis(false);
-                    this.setGPTPopupText('');
-                }
-            },
+            opacity => {},
             { fireImmediately: true }
         );
         this._disposer = reaction(
             () => SelectionManager.Views().slice(),
             selected => {
-                this.setGPTPopupVis(false);
-                this.setGPTPopupText('');
                 AnchorMenu.Instance.fadeOut(true);
             }
         );
@@ -153,23 +129,22 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
      * @param e pointer down event
      */
     gptSummarize = async (e: React.PointerEvent) => {
+        GPTPopup.Instance.setVisible(true);
         this.setHighlightRange(undefined);
-        this.setGPTPopupVis(true);
         this.setGPTMode(GPTPopupMode.SUMMARY);
-        this.setLoading(true);
+        GPTPopup.Instance.setLoading(true);
 
         try {
             const res = await gptAPICall(this.selectedText, GPTCallType.SUMMARY);
             if (res) {
-                this.setGPTPopupText(res);
+                GPTPopup.Instance.setText(res);
             } else {
-                this.setGPTPopupText('Something went wrong.');
+                GPTPopup.Instance.setText('Something went wrong.');
             }
         } catch (err) {
             console.error(err);
         }
-
-        this.setLoading(false);
+        GPTPopup.Instance.setLoading(false);
     };
 
     /**
@@ -184,9 +159,9 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
         const fullText = state.doc.textBetween(0, this.editorView.state.doc.content.size, ' \n');
         const selectedText = state.doc.textBetween(sel.from, sel.to);
 
-        this.setGPTPopupVis(true);
+        GPTPopup.Instance.setVisible(true);
         this.setGPTMode(GPTPopupMode.EDIT);
-        this.setLoading(true);
+        GPTPopup.Instance.setLoading(true);
 
         try {
             let res = await gptAPICall(selectedText, GPTCallType.EDIT);
@@ -196,16 +171,16 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
             const resultText = fullText.slice(0, sel.from - 1) + res + fullText.slice(sel.to - 1);
 
             if (res) {
-                this.setGPTPopupText(resultText);
+                GPTPopup.Instance.setText(resultText);
                 this.setHighlightRange([sel.from - 1, sel.from - 1 + res.length]);
             } else {
-                this.setGPTPopupText('Something went wrong.');
+                GPTPopup.Instance.setText('Something went wrong.');
             }
         } catch (err) {
             console.error(err);
         }
 
-        this.setLoading(false);
+        GPTPopup.Instance.setLoading(false);
     };
 
     /**
@@ -253,21 +228,18 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
     };
 
     @computed get highlighter() {
-        return <Group>
-            <IconButton 
-                icon={<FontAwesomeIcon icon="highlighter" style={{ transition: 'transform 0.1s', transform: 'rotate(-45deg)' }} />}
-                tooltip={'Click to Highlight'}
-                onClick={this.highlightClicked}
-                colorPicker={this.highlightColor}
-                color={StrCast(Doc.UserDoc().userColor)}
-            />
-            <ColorPicker
-                colorPickerType={'github'}
-                selectedColor={this.highlightColor}
-                setSelectedColor={color => this.changeHighlightColor(color)}
-                size={Size.XSMALL}
-            />
-        </Group>
+        return (
+            <Group>
+                <IconButton
+                    icon={<FontAwesomeIcon icon="highlighter" style={{ transition: 'transform 0.1s', transform: 'rotate(-45deg)' }} />}
+                    tooltip={'Click to Highlight'}
+                    onClick={this.highlightClicked}
+                    colorPicker={this.highlightColor}
+                    color={StrCast(Doc.UserDoc().userColor)}
+                />
+                <ColorPicker colorPickerType={'github'} selectedColor={this.highlightColor} setSelectedColor={color => this.changeHighlightColor(color)} size={Size.XSMALL} />
+            </Group>
+        );
     }
 
     @action changeHighlightColor = (color: string) => {
@@ -312,12 +284,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
             this.Status === 'marquee' ? (
                 <>
                     {this.highlighter}
-                    <IconButton 
-                        tooltip={'Drag to Place Annotation'} 
-                        onPointerDown={this.pointerDown} 
-                        icon={<FontAwesomeIcon icon="comment-alt"/>}
-                        color={StrCast(Doc.UserDoc().userColor)}
-                    />
+                    <IconButton tooltip={'Drag to Place Annotation'} onPointerDown={this.pointerDown} icon={<FontAwesomeIcon icon="comment-alt" />} color={StrCast(Doc.UserDoc().userColor)} />
                     {/* GPT Summarize icon only shows up when text is highlighted, not on marquee selection*/}
                     {AnchorMenu.Instance.StartCropDrag === unimplementedFunction && this.canSummarize() && (
                         <Tooltip key="gpt" title={<div className="dash-tooltip">Summarize with AI</div>}>
@@ -326,7 +293,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
                             </button>
                         </Tooltip>
                     )}
-                    <GPTPopup
+                    {/* <GPTPopup
                         key="gptpopup"
                         visible={this.showGPTPopup}
                         text={this.GPTpopupText}
@@ -336,68 +303,32 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
                         callEditApi={this.gptEdit}
                         replaceText={this.replaceText}
                         mode={this.GPTMode}
-                    />
+                    /> */}
                     {AnchorMenu.Instance.OnAudio === unimplementedFunction ? null : (
-                        <IconButton 
-                            tooltip={'Click to Record Annotation'}
-                            onPointerDown={this.audioDown}
-                            icon={<FontAwesomeIcon icon="microphone" />}
-                            color={StrCast(Doc.UserDoc().userColor)}
-                        />
-                    )}
-                    {this.canEdit() && (
-                        <IconButton 
-                            tooltip={'AI edit suggestions'}
-                            onPointerDown={this.gptEdit}
-                            icon={<FontAwesomeIcon icon="pencil-alt" />}
-                            color={StrCast(Doc.UserDoc().userColor)}
-                        />
+                        <IconButton tooltip={'Click to Record Annotation'} onPointerDown={this.audioDown} icon={<FontAwesomeIcon icon="microphone" />} color={StrCast(Doc.UserDoc().userColor)} />
                     )}
-                    <Popup 
-                        tooltip='Find document to link to selected text'
-                        type={Type.PRIM}
-                        icon={<FontAwesomeIcon icon={'search'} />}
-                        popup={<LinkPopup key="popup" linkCreateAnchor={this.onMakeAnchor} />}
-                        color={StrCast(Doc.UserDoc().userColor)}
-                    />
+                    {this.canEdit() && <IconButton tooltip={'AI edit suggestions'} onPointerDown={this.gptEdit} icon={<FontAwesomeIcon icon="pencil-alt" />} color={StrCast(Doc.UserDoc().userColor)} />}
+                    <Popup tooltip="Find document to link to selected text" type={Type.PRIM} icon={<FontAwesomeIcon icon={'search'} />} popup={<LinkPopup key="popup" linkCreateAnchor={this.onMakeAnchor} />} color={StrCast(Doc.UserDoc().userColor)} />
                     {AnchorMenu.Instance.StartCropDrag === unimplementedFunction ? null : (
-                        <IconButton 
-                            tooltip={'Click/Drag to create cropped image'}
-                            onPointerDown={this.cropDown}
-                            icon={<FontAwesomeIcon icon="image"/>}
-                            color={StrCast(Doc.UserDoc().userColor)}
-                        />
+                        <IconButton tooltip={'Click/Drag to create cropped image'} onPointerDown={this.cropDown} icon={<FontAwesomeIcon icon="image" />} color={StrCast(Doc.UserDoc().userColor)} />
                     )}
                 </>
             ) : (
                 <>
-                    {this.Delete !== returnFalse && <IconButton 
-                        tooltip={'Remove Link Anchor'}
-                        onPointerDown={this.Delete}
-                        icon={<FontAwesomeIcon icon="trash-alt" />}
-                        color={StrCast(Doc.UserDoc().userColor)}
-                    />}
-                    {this.PinToPres !== returnFalse && <IconButton 
-                        tooltip={'Pin to Presentation'}
-                        onPointerDown={this.PinToPres}
-                        icon={<FontAwesomeIcon icon="map-pin" />}
-                        color={StrCast(Doc.UserDoc().userColor)}
-                    />}
-                    {this.ShowTargetTrail !== returnFalse && <IconButton 
-                        tooltip={'Show Linked Trail'}
-                        onPointerDown={this.ShowTargetTrail}
-                        icon={<FontAwesomeIcon icon="taxi" />}
-                        color={StrCast(Doc.UserDoc().userColor)}
-                    />}
-                    {this.IsTargetToggler !== returnFalse && <Toggle
-                        tooltip={'Make target visibility toggle on click'}
-                        type={Type.PRIM}
-                        toggleType={ToggleType.BUTTON}
-                        toggleStatus={this.IsTargetToggler()}
-                        onClick={this.MakeTargetToggle}
-                        icon={<FontAwesomeIcon icon="thumbtack" />}
-                        color={StrCast(Doc.UserDoc().userColor)}
-                    />}
+                    {this.Delete !== returnFalse && <IconButton tooltip={'Remove Link Anchor'} onPointerDown={this.Delete} icon={<FontAwesomeIcon icon="trash-alt" />} color={StrCast(Doc.UserDoc().userColor)} />}
+                    {this.PinToPres !== returnFalse && <IconButton tooltip={'Pin to Presentation'} onPointerDown={this.PinToPres} icon={<FontAwesomeIcon icon="map-pin" />} color={StrCast(Doc.UserDoc().userColor)} />}
+                    {this.ShowTargetTrail !== returnFalse && <IconButton tooltip={'Show Linked Trail'} onPointerDown={this.ShowTargetTrail} icon={<FontAwesomeIcon icon="taxi" />} color={StrCast(Doc.UserDoc().userColor)} />}
+                    {this.IsTargetToggler !== returnFalse && (
+                        <Toggle
+                            tooltip={'Make target visibility toggle on click'}
+                            type={Type.PRIM}
+                            toggleType={ToggleType.BUTTON}
+                            toggleStatus={this.IsTargetToggler()}
+                            onClick={this.MakeTargetToggle}
+                            icon={<FontAwesomeIcon icon="thumbtack" />}
+                            color={StrCast(Doc.UserDoc().userColor)}
+                        />
+                    )}
                 </>
             );
 
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.scss b/src/client/views/pdf/GPTPopup/GPTPopup.scss
index 44413ede7..2f0ff83e2 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.scss
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.scss
@@ -6,12 +6,6 @@ $button: #5b97ff;
 $highlightedText: #82e0ff;
 
 .summary-box {
-    display: flex;
-    flex-direction: column;
-    justify-content: space-between;
-    background-color: #ffffff;
-    box-shadow: 0 2px 5px #7474748d;
-    color: $textgrey;
     position: fixed;
     bottom: 10px;
     right: 10px;
@@ -21,6 +15,12 @@ $highlightedText: #82e0ff;
     padding: 15px;
     padding-bottom: 0;
     z-index: 999;
+    display: flex;
+    flex-direction: column;
+    justify-content: space-between;
+    background-color: #ffffff;
+    box-shadow: 0 2px 5px #7474748d;
+    color: $textgrey;
 
     .summary-heading {
         display: flex;
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index 8bd060d4f..9f28cb5d1 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -5,56 +5,96 @@ import { observer } from 'mobx-react';
 import ReactLoading from 'react-loading';
 import Typist from 'react-typist';
 import { Doc } from '../../../../fields/Doc';
-import { Docs } from '../../../documents/Documents';
+import { DocUtils, Docs } from '../../../documents/Documents';
 import './GPTPopup.scss';
+import { Button, IconButton } from 'browndash-components';
+import { StrCast } from '../../../../fields/Types';
 
 export enum GPTPopupMode {
     SUMMARY,
     EDIT,
 }
 
-interface GPTPopupProps {
-    visible: boolean;
-    text: string;
-    loading: boolean;
-    mode: GPTPopupMode;
-    callSummaryApi: (e: React.PointerEvent) => Promise<void>;
-    callEditApi: (e: React.PointerEvent) => Promise<void>;
-    replaceText: (replacement: string) => void;
-    highlightRange?: number[];
-}
+interface GPTPopupProps {}
 
 @observer
 export class GPTPopup extends React.Component<GPTPopupProps> {
     static Instance: GPTPopup;
 
     @observable
-    private done: boolean = false;
+    public visible: boolean = false;
+    @action
+    public setVisible = (vis: boolean) => {
+        this.visible = vis;
+    };
     @observable
-    private sidebarId: string = '';
+    public loading: boolean = false;
+    @action
+    public setLoading = (loading: boolean) => {
+        this.loading = loading;
+    };
+    @observable
+    public text: string = '';
+    @action
+    public setText = (text: string) => {
+        this.text = text;
+    };
+    @observable
+    public mode: GPTPopupMode = GPTPopupMode.SUMMARY;
+    @action
+    public setMode = (mode: GPTPopupMode) => {
+        this.mode = mode;
+    };
 
+    @observable
+    public highlightRange: number[] = [];
+    @action callSummaryApi = () => {};
+    @action callEditApi = () => {};
+    @action replaceText = (replacement: string) => {};
+
+    @observable
+    private done: boolean = false;
     @action
     public setDone = (done: boolean) => {
         this.done = done;
     };
+    @observable
+    private sidebarId: string = '';
     @action
     public setSidebarId = (id: string) => {
         this.sidebarId = id;
     };
 
+    // reference
+    // if (focusNode) {
+    //     const anchor = srcWeb?.ComponentView?.getAnchor?.(true);
+    //     anchor && DocUtils.MakeLink(htmlDoc, anchor, {});
+    // }
+    @observable
+    private pdfAnchor: Doc | undefined;
+    @action
+    public setPdfAnchor = (anchor: Doc) => {
+        this.pdfAnchor = anchor;
+    };
+
     public addDoc: (doc: Doc | Doc[], sidebarKey?: string | undefined) => boolean = () => false;
 
     /**
      * Transfers the summarization text to a sidebar annotation text document.
      */
     private transferToText = () => {
-        const newDoc = Docs.Create.TextDocument(this.props.text.trim(), {
+        const newDoc = Docs.Create.TextDocument(this.text.trim(), {
             _width: 200,
             _height: 50,
             _layout_fitWidth: true,
             _layout_autoHeight: true,
         });
         this.addDoc(newDoc, this.sidebarId);
+        if (this.pdfAnchor) {
+            DocUtils.MakeLink(newDoc, this.pdfAnchor, {
+                link_relationship: 'GPT Summary',
+            });
+        }
     };
 
     constructor(props: GPTPopupProps) {
@@ -63,7 +103,7 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
     }
 
     componentDidUpdate = () => {
-        if (this.props.loading) {
+        if (this.loading) {
             this.setDone(false);
         }
     };
@@ -73,10 +113,10 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
             <div>
                 {this.heading('SUMMARY')}
                 <div className="content-wrapper">
-                    {!this.props.loading &&
+                    {!this.loading &&
                         (!this.done ? (
                             <Typist
-                                key={this.props.text}
+                                key={this.text}
                                 avgTypingDelay={15}
                                 cursor={{ hideWhenDone: true }}
                                 onTypingDone={() => {
@@ -84,18 +124,18 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
                                         this.setDone(true);
                                     }, 500);
                                 }}>
-                                {this.props.text}
+                                {this.text}
                             </Typist>
                         ) : (
-                            this.props.text
+                            this.text
                         ))}
                 </div>
             </div>
-            {!this.props.loading && (
+            {!this.loading && (
                 <div className="btns-wrapper">
                     {this.done ? (
                         <>
-                            <button className="icon-btn" onPointerDown={e => this.props.callSummaryApi(e)}>
+                            {/* <button className="icon-btn" onClick={this.callSummaryApi}>
                                 <FontAwesomeIcon icon="redo-alt" size="lg" />
                             </button>
                             <button
@@ -104,7 +144,9 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
                                     this.transferToText();
                                 }}>
                                 Transfer to Text
-                            </button>
+                            </button> */}
+                            <IconButton onClick={this.callSummaryApi} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(Doc.UserDoc().userVariantColor)} />
+                            <Button text="Transfer To Text" onClick={this.transferToText} color={StrCast(Doc.UserDoc().userVariantColor)} />
                         </>
                     ) : (
                         <div className="summarizing">
@@ -125,7 +167,7 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
     );
 
     editBox = () => {
-        const hr = this.props.highlightRange;
+        const hr = this.highlightRange;
         return (
             <>
                 <div>
@@ -133,22 +175,22 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
                     <div className="content-wrapper">
                         {hr && (
                             <div>
-                                {this.props.text.slice(0, hr[0])} <span className="highlighted-text">{this.props.text.slice(hr[0], hr[1])}</span> {this.props.text.slice(hr[1])}
+                                {this.text.slice(0, hr[0])} <span className="highlighted-text">{this.text.slice(hr[0], hr[1])}</span> {this.text.slice(hr[1])}
                             </div>
                         )}
                     </div>
                 </div>
-                {hr && !this.props.loading && (
+                {hr && !this.loading && (
                     <>
                         <div className="btns-wrapper">
                             <>
-                                <button className="icon-btn" onPointerDown={e => this.props.callEditApi(e)}>
+                                <button className="icon-btn" onPointerDown={this.callEditApi}>
                                     <FontAwesomeIcon icon="redo-alt" size="lg" />
                                 </button>
                                 <button
                                     className="text-btn"
                                     onClick={e => {
-                                        this.props.replaceText(this.props.text);
+                                        this.replaceText(this.text);
                                     }}>
                                     Replace Text
                                 </button>
@@ -174,14 +216,14 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
     heading = (headingText: string) => (
         <div className="summary-heading">
             <label className="summary-text">{headingText}</label>
-            {this.props.loading && <ReactLoading type="spin" color="#bcbcbc" width={14} height={14} />}
+            {this.loading && <ReactLoading type="spin" color="#bcbcbc" width={14} height={14} />}
         </div>
     );
 
     render() {
         return (
-            <div className="summary-box" style={{ display: this.props.visible ? 'flex' : 'none' }}>
-                {this.props.mode === GPTPopupMode.SUMMARY ? this.summaryBox() : this.editBox()}
+            <div className="summary-box" style={{ display: this.visible ? 'flex' : 'none' }}>
+                {this.mode === GPTPopupMode.SUMMARY ? this.summaryBox() : this.editBox()}
             </div>
         );
     }
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 0fd93868a..4fc31ffe3 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -423,6 +423,11 @@ export class PDFViewer extends React.Component<IViewerProps> {
 
         // Changing which document to add the annotation to (the currently selected PDF)
         GPTPopup.Instance.setSidebarId('data_sidebar');
+        const anchor = this._getAnchor(undefined, false);
+        if (anchor) {
+            console.log(anchor);
+            GPTPopup.Instance.setPdfAnchor(anchor);
+        }
         GPTPopup.Instance.addDoc = this.props.sidebarAddDoc;
     };
 
-- 
cgit v1.2.3-70-g09d2


From fec79d2b5b8feb361e489c9ee41ee720507d0806 Mon Sep 17 00:00:00 2001
From: Sophie Zhang <sophie_zhang@brown.edu>
Date: Thu, 27 Jul 2023 13:23:42 -0400
Subject: changes

---
 report.20230720.011057.28881.0.001.json            | 1269 ++++++++++++++++++++
 src/client/apis/gpt/GPT.ts                         |   28 +-
 src/client/util/DragManager.ts                     |    4 +-
 src/client/views/collections/TabDocView.tsx        |    6 +-
 .../CollectionFreeFormLinkView.tsx                 |    8 +-
 src/client/views/global/globalScripts.ts           |   50 +-
 src/client/views/nodes/WebBox.tsx                  |    5 +
 .../views/nodes/formattedText/FormattedTextBox.tsx |   89 +-
 src/client/views/pdf/AnchorMenu.tsx                |   21 +-
 src/client/views/pdf/GPTPopup/GPTPopup.scss        |   52 +
 src/client/views/pdf/GPTPopup/GPTPopup.tsx         |  129 +-
 src/client/views/pdf/PDFViewer.tsx                 |    3 +-
 12 files changed, 1530 insertions(+), 134 deletions(-)
 create mode 100644 report.20230720.011057.28881.0.001.json

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

diff --git a/report.20230720.011057.28881.0.001.json b/report.20230720.011057.28881.0.001.json
new file mode 100644
index 000000000..5c72c0c91
--- /dev/null
+++ b/report.20230720.011057.28881.0.001.json
@@ -0,0 +1,1269 @@
+
+{
+  "header": {
+    "reportVersion": 1,
+    "event": "Allocation failed - JavaScript heap out of memory",
+    "trigger": "FatalError",
+    "filename": "report.20230720.011057.28881.0.001.json",
+    "dumpEventTime": "2023-07-20T01:10:57Z",
+    "dumpEventTimeStamp": "1689829857721",
+    "processId": 28881,
+    "cwd": "/Users/smallwhale/Desktop/Projects/Dash-Web",
+    "commandLine": [
+      "/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node",
+      "--max-old-space-size=2048",
+      "/Users/smallwhale/Desktop/Projects/Dash-Web/node_modules/ts-node-dev/lib/wrap.js",
+      "/Users/smallwhale/Desktop/Projects/Dash-Web/node_modules/fork-ts-checker-webpack-plugin/lib/service.js"
+    ],
+    "nodejsVersion": "v12.16.0",
+    "wordSize": 64,
+    "arch": "x64",
+    "platform": "darwin",
+    "componentVersions": {
+      "node": "12.16.0",
+      "v8": "7.8.279.23-node.31",
+      "uv": "1.34.0",
+      "zlib": "1.2.11",
+      "brotli": "1.0.7",
+      "ares": "1.15.0",
+      "modules": "72",
+      "nghttp2": "1.40.0",
+      "napi": "5",
+      "llhttp": "2.0.4",
+      "http_parser": "2.9.3",
+      "openssl": "1.1.1d",
+      "cldr": "35.1",
+      "icu": "64.2",
+      "tz": "2019c",
+      "unicode": "12.1"
+    },
+    "release": {
+      "name": "node",
+      "lts": "Erbium",
+      "headersUrl": "https://nodejs.org/download/release/v12.16.0/node-v12.16.0-headers.tar.gz",
+      "sourceUrl": "https://nodejs.org/download/release/v12.16.0/node-v12.16.0.tar.gz"
+    },
+    "osName": "Darwin",
+    "osRelease": "22.4.0",
+    "osVersion": "Darwin Kernel Version 22.4.0: Mon Mar  6 21:01:02 PST 2023; root:xnu-8796.101.5~3/RELEASE_ARM64_T8112",
+    "osMachine": "x86_64",
+    "cpus": [
+      {
+        "model": "Apple M2",
+        "speed": 2400,
+        "user": 40342390,
+        "nice": 0,
+        "sys": 32818540,
+        "idle": 263532590,
+        "irq": 0
+      },
+      {
+        "model": "Apple M2",
+        "speed": 2400,
+        "user": 37882510,
+        "nice": 0,
+        "sys": 28750670,
+        "idle": 270292020,
+        "irq": 0
+      },
+      {
+        "model": "Apple M2",
+        "speed": 2400,
+        "user": 32806560,
+        "nice": 0,
+        "sys": 24772270,
+        "idle": 279705870,
+        "irq": 0
+      },
+      {
+        "model": "Apple M2",
+        "speed": 2400,
+        "user": 28917140,
+        "nice": 0,
+        "sys": 21505900,
+        "idle": 287180070,
+        "irq": 0
+      },
+      {
+        "model": "Apple M2",
+        "speed": 2400,
+        "user": 52175300,
+        "nice": 0,
+        "sys": 11670040,
+        "idle": 274363170,
+        "irq": 0
+      },
+      {
+        "model": "Apple M2",
+        "speed": 2400,
+        "user": 30406640,
+        "nice": 0,
+        "sys": 8969580,
+        "idle": 298959950,
+        "irq": 0
+      },
+      {
+        "model": "Apple M2",
+        "speed": 2400,
+        "user": 21936370,
+        "nice": 0,
+        "sys": 6476860,
+        "idle": 310114030,
+        "irq": 0
+      },
+      {
+        "model": "Apple M2",
+        "speed": 2400,
+        "user": 16048180,
+        "nice": 0,
+        "sys": 4956530,
+        "idle": 317649610,
+        "irq": 0
+      }
+    ],
+    "networkInterfaces": [
+      {
+        "name": "lo0",
+        "internal": true,
+        "mac": "00:00:00:00:00:00",
+        "address": "127.0.0.1",
+        "netmask": "255.0.0.0",
+        "family": "IPv4"
+      },
+      {
+        "name": "lo0",
+        "internal": true,
+        "mac": "00:00:00:00:00:00",
+        "address": "::1",
+        "netmask": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
+        "family": "IPv6",
+        "scopeid": 0
+      },
+      {
+        "name": "lo0",
+        "internal": true,
+        "mac": "00:00:00:00:00:00",
+        "address": "fe80::1",
+        "netmask": "ffff:ffff:ffff:ffff::",
+        "family": "IPv6",
+        "scopeid": 1
+      },
+      {
+        "name": "anpi0",
+        "internal": false,
+        "mac": "42:10:d5:5b:93:9e",
+        "address": "fe80::4010:d5ff:fe5b:939e",
+        "netmask": "ffff:ffff:ffff:ffff::",
+        "family": "IPv6",
+        "scopeid": 4
+      },
+      {
+        "name": "anpi1",
+        "internal": false,
+        "mac": "42:10:d5:5b:93:9f",
+        "address": "fe80::4010:d5ff:fe5b:939f",
+        "netmask": "ffff:ffff:ffff:ffff::",
+        "family": "IPv6",
+        "scopeid": 5
+      },
+      {
+        "name": "en0",
+        "internal": false,
+        "mac": "9c:3e:53:8e:59:0e",
+        "address": "fe80::2b:dde:e859:46b8",
+        "netmask": "ffff:ffff:ffff:ffff::",
+        "family": "IPv6",
+        "scopeid": 12
+      },
+      {
+        "name": "en0",
+        "internal": false,
+        "mac": "9c:3e:53:8e:59:0e",
+        "address": "192.168.1.154",
+        "netmask": "255.255.255.0",
+        "family": "IPv4"
+      },
+      {
+        "name": "en0",
+        "internal": false,
+        "mac": "9c:3e:53:8e:59:0e",
+        "address": "2600:4040:572e:2f00:18fe:e894:e23c:f4c2",
+        "netmask": "ffff:ffff:ffff:ffff::",
+        "family": "IPv6",
+        "scopeid": 0
+      },
+      {
+        "name": "en0",
+        "internal": false,
+        "mac": "9c:3e:53:8e:59:0e",
+        "address": "2600:4040:572e:2f00:c182:d0ad:6e64:1fae",
+        "netmask": "ffff:ffff:ffff:ffff::",
+        "family": "IPv6",
+        "scopeid": 0
+      },
+      {
+        "name": "awdl0",
+        "internal": false,
+        "mac": "b6:6c:9a:3c:a7:93",
+        "address": "fe80::b46c:9aff:fe3c:a793",
+        "netmask": "ffff:ffff:ffff:ffff::",
+        "family": "IPv6",
+        "scopeid": 13
+      },
+      {
+        "name": "llw0",
+        "internal": false,
+        "mac": "b6:6c:9a:3c:a7:93",
+        "address": "fe80::b46c:9aff:fe3c:a793",
+        "netmask": "ffff:ffff:ffff:ffff::",
+        "family": "IPv6",
+        "scopeid": 14
+      },
+      {
+        "name": "utun0",
+        "internal": false,
+        "mac": "00:00:00:00:00:00",
+        "address": "fe80::be27:76ec:8f11:5429",
+        "netmask": "ffff:ffff:ffff:ffff::",
+        "family": "IPv6",
+        "scopeid": 15
+      },
+      {
+        "name": "utun1",
+        "internal": false,
+        "mac": "00:00:00:00:00:00",
+        "address": "fe80::ce81:b1c:bd2c:69e",
+        "netmask": "ffff:ffff:ffff:ffff::",
+        "family": "IPv6",
+        "scopeid": 16
+      },
+      {
+        "name": "utun2",
+        "internal": false,
+        "mac": "00:00:00:00:00:00",
+        "address": "fe80::7707:8801:aef4:1907",
+        "netmask": "ffff:ffff:ffff:ffff::",
+        "family": "IPv6",
+        "scopeid": 17
+      },
+      {
+        "name": "utun3",
+        "internal": false,
+        "mac": "00:00:00:00:00:00",
+        "address": "fe80::a4f8:a3ce:851e:23dd",
+        "netmask": "ffff:ffff:ffff:ffff::",
+        "family": "IPv6",
+        "scopeid": 18
+      },
+      {
+        "name": "utun4",
+        "internal": false,
+        "mac": "00:00:00:00:00:00",
+        "address": "fe80::8d47:6641:b300:bde8",
+        "netmask": "ffff:ffff:ffff:ffff::",
+        "family": "IPv6",
+        "scopeid": 19
+      },
+      {
+        "name": "utun5",
+        "internal": false,
+        "mac": "00:00:00:00:00:00",
+        "address": "fe80::7c36:c665:d5f7:49d7",
+        "netmask": "ffff:ffff:ffff:ffff::",
+        "family": "IPv6",
+        "scopeid": 20
+      },
+      {
+        "name": "utun6",
+        "internal": false,
+        "mac": "00:00:00:00:00:00",
+        "address": "fe80::10d8:db7a:d714:7746",
+        "netmask": "ffff:ffff:ffff:ffff::",
+        "family": "IPv6",
+        "scopeid": 21
+      }
+    ],
+    "host": "Sophies-MBP"
+  },
+  "javascriptStack": {
+    "message": "No stack.",
+    "stack": [
+      "Unavailable."
+    ]
+  },
+  "nativeStack": [
+    {
+      "pc": "0x000000010015c8ca",
+      "symbol": "report::TriggerNodeReport(v8::Isolate*, node::Environment*, char const*, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&, v8::Local<v8::String>) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]"
+    },
+    {
+      "pc": "0x0000000100080f3e",
+      "symbol": "node::OnFatalError(char const*, char const*) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]"
+    },
+    {
+      "pc": "0x0000000100185467",
+      "symbol": "v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]"
+    },
+    {
+      "pc": "0x0000000100185403",
+      "symbol": "v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]"
+    },
+    {
+      "pc": "0x000000010030b5f5",
+      "symbol": "v8::internal::Heap::FatalProcessOutOfMemory(char const*) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]"
+    },
+    {
+      "pc": "0x000000010030ccc4",
+      "symbol": "v8::internal::Heap::RecomputeLimits(v8::internal::GarbageCollector) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]"
+    },
+    {
+      "pc": "0x0000000100309b37",
+      "symbol": "v8::internal::Heap::PerformGarbageCollection(v8::internal::GarbageCollector, v8::GCCallbackFlags) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]"
+    },
+    {
+      "pc": "0x0000000100307afd",
+      "symbol": "v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]"
+    },
+    {
+      "pc": "0x00000001003132ba",
+      "symbol": "v8::internal::Heap::AllocateRawWithLightRetry(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]"
+    },
+    {
+      "pc": "0x0000000100313341",
+      "symbol": "v8::internal::Heap::AllocateRawWithRetryOrFail(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]"
+    },
+    {
+      "pc": "0x00000001002e065b",
+      "symbol": "v8::internal::Factory::NewFillerObject(int, bool, v8::internal::AllocationType, v8::internal::AllocationOrigin) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]"
+    },
+    {
+      "pc": "0x0000000100618a18",
+      "symbol": "v8::internal::Runtime_AllocateInYoungGeneration(int, unsigned long*, v8::internal::Isolate*) [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]"
+    },
+    {
+      "pc": "0x0000000100950c19",
+      "symbol": "Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]"
+    },
+    {
+      "pc": "0x00000001008d2291",
+      "symbol": "Builtins_FastNewObject [/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node]"
+    }
+  ],
+  "javascriptHeap": {
+    "totalMemory": 2152435712,
+    "totalCommittedMemory": 2150538320,
+    "usedMemory": 2139294040,
+    "availableMemory": 47735856,
+    "memoryLimit": 2197815296,
+    "heapSpaces": {
+      "read_only_space": {
+        "memorySize": 262144,
+        "committedMemory": 33088,
+        "capacity": 32808,
+        "used": 32808,
+        "available": 0
+      },
+      "new_space": {
+        "memorySize": 2097152,
+        "committedMemory": 1236968,
+        "capacity": 1047456,
+        "used": 203328,
+        "available": 844128
+      },
+      "old_space": {
+        "memorySize": 1956003840,
+        "committedMemory": 1955767472,
+        "capacity": 1948209048,
+        "used": 1947744360,
+        "available": 464688
+      },
+      "code_space": {
+        "memorySize": 14061568,
+        "committedMemory": 13610624,
+        "capacity": 12121760,
+        "used": 12121760,
+        "available": 0
+      },
+      "map_space": {
+        "memorySize": 1576960,
+        "committedMemory": 1456120,
+        "capacity": 1257120,
+        "used": 1257120,
+        "available": 0
+      },
+      "large_object_space": {
+        "memorySize": 178384896,
+        "committedMemory": 178384896,
+        "capacity": 177931880,
+        "used": 177931880,
+        "available": 0
+      },
+      "code_large_object_space": {
+        "memorySize": 49152,
+        "committedMemory": 49152,
+        "capacity": 2784,
+        "used": 2784,
+        "available": 0
+      },
+      "new_large_object_space": {
+        "memorySize": 0,
+        "committedMemory": 0,
+        "capacity": 1047456,
+        "used": 0,
+        "available": 1047456
+      }
+    }
+  },
+  "resourceUsage": {
+    "userCpuSeconds": 312.946,
+    "kernelCpuSeconds": 137.981,
+    "cpuConsumptionPercent": 62.0257,
+    "maxRss": 2062897119232,
+    "pageFaults": {
+      "IORequired": 658,
+      "IONotRequired": 43579208
+    },
+    "fsActivity": {
+      "reads": 0,
+      "writes": 0
+    }
+  },
+  "libuv": [
+  ],
+  "environmentVariables": {
+    "npm_config_save_dev": "",
+    "npm_config_legacy_bundling": "",
+    "npm_config_dry_run": "",
+    "npm_package_dependencies_translate_google_api": "^1.0.4",
+    "npm_package_dependencies_request": "^2.88.2",
+    "npm_package_dependencies_express_flash": "0.0.2",
+    "npm_package_dependencies__fortawesome_fontawesome_svg_core": "^6.3.0",
+    "NVM_INC": "/Users/smallwhale/.nvm/versions/node/v12.16.0/include/node",
+    "npm_config_viewer": "man",
+    "npm_config_only": "",
+    "npm_config_commit_hooks": "true",
+    "npm_config_browser": "",
+    "npm_package_gitHead": "e11aa60b774d457cb016bb0f375ce092f0a733af",
+    "npm_package_dependencies_webpack_dev_middleware": "^5.3.1",
+    "npm_package_dependencies_webpack_cli": "^4.10.0",
+    "npm_package_devDependencies_prettier": "^2.7.1",
+    "npm_package_devDependencies_awesome_typescript_loader": "^5.2.1",
+    "npm_package_devDependencies__types_archiver": "^3.1.1",
+    "MANPATH": "/Users/smallwhale/.nvm/versions/node/v12.16.0/share/man:/Users/smallwhale/.nvm/versions/node/v18.16.0/share/man:/opt/homebrew/share/man:/usr/share/man:/usr/local/share/man:/Users/smallwhale/.nvm/versions/node/v18.16.0/share/man:/opt/homebrew/share/man::",
+    "npm_config_also": "",
+    "npm_package_dependencies_react_jsx_parser": "^1.29.0",
+    "npm_package_dependencies_mongoose": "^5.13.14",
+    "npm_package_dependencies_connect_flash": "^0.1.1",
+    "npm_package_browser_child_process": "false",
+    "npm_config_sign_git_commit": "",
+    "npm_config_rollback": "true",
+    "npm_package_dependencies_material_ui": "^0.20.2",
+    "npm_package_devDependencies__types_sharp": "^0.23.1",
+    "npm_package_devDependencies__types_passport_local": "^1.0.34",
+    "npm_package_devDependencies__types_dotenv": "^6.1.1",
+    "npm_package_devDependencies__types_cookie_parser": "^1.4.2",
+    "TERM_PROGRAM": "vscode",
+    "NODE": "/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node",
+    "npm_config_usage": "",
+    "npm_config_audit": "true",
+    "npm_package_dependencies_reveal_js": "^4.3.0",
+    "npm_package_dependencies_process": "^0.11.10",
+    "npm_package_dependencies_pdfjs": "^2.4.7",
+    "npm_package_dependencies_html_to_image": "^0.1.3",
+    "npm_package_devDependencies_file_loader": "^3.0.1",
+    "npm_package_devDependencies__types_express_flash": "0.0.0",
+    "npm_package_scripts_monitor": "cross-env MONITORED=true NODE_OPTIONS=--max_old_space_size=4096 ts-node src/server/index.ts",
+    "INIT_CWD": "/Users/smallwhale/Desktop/Projects/Dash-Web",
+    "npm_package_dependencies_rehype_raw": "^6.1.1",
+    "npm_package_dependencies_react_audio_waveform": "0.0.5",
+    "npm_package_dependencies_path_browserify": "^1.0.1",
+    "npm_package_dependencies_nodemailer": "^5.1.1",
+    "npm_package_dependencies_axios": "^0.19.2",
+    "npm_package_devDependencies_typescript": "^4.7.4",
+    "NVM_CD_FLAGS": "-q",
+    "npm_config_globalignorefile": "/Users/smallwhale/.nvm/versions/node/v12.16.0/etc/npmignore",
+    "npm_package_dependencies_react_grid_layout": "^1.3.4",
+    "npm_package_dependencies_prosemirror_find_replace": "^0.9.0",
+    "npm_package_dependencies_normalize_css": "^8.0.1",
+    "npm_package_devDependencies_mocha": "^5.2.0",
+    "npm_package_devDependencies__types_express_session": "^1.17.5",
+    "TERM": "xterm-256color",
+    "SHELL": "/bin/zsh",
+    "npm_config_shell": "/bin/zsh",
+    "npm_config_maxsockets": "50",
+    "npm_config_init_author_url": "",
+    "npm_package_dependencies_prosemirror_dev_tools": "^3.1.0",
+    "npm_package_dependencies_p_limit": "^2.2.0",
+    "npm_package_dependencies_bson": "^4.6.1",
+    "npm_package_dependencies__types_dom_speech_recognition": "0.0.1",
+    "npm_package_dependencies__emotion_styled": "^11.11.0",
+    "npm_package_devDependencies_style_loader": "^0.23.1",
+    "npm_package_devDependencies__types_react_datepicker": "^3.1.8",
+    "npm_config_shrinkwrap": "true",
+    "npm_config_parseable": "",
+    "npm_config_metrics_registry": "https://registry.npmjs.org/",
+    "npm_package_dependencies_xregexp": "^4.4.1",
+    "npm_package_dependencies_shelljs": "^0.8.5",
+    "npm_package_dependencies_bezier_curve": "^1.0.0",
+    "npm_package_dependencies__mui_icons_material": "^5.11.16",
+    "npm_package_devDependencies_tslint": "^5.20.1",
+    "npm_package_devDependencies__types_react_transition_group": "^4.4.5",
+    "npm_package_scripts_tsc": "tsc",
+    "HOMEBREW_REPOSITORY": "/opt/homebrew",
+    "TMPDIR": "/var/folders/bv/1xck6d7j7bz14bvhgdsllbj40000gn/T/",
+    "npm_config_timing": "",
+    "npm_config_init_license": "ISC",
+    "npm_package_dependencies_socket_io": "^2.5.0",
+    "npm_package_dependencies_probe_image_size": "^4.0.0",
+    "npm_package_dependencies_canvas": "^2.9.3",
+    "npm_package_dependencies__hig_theme_data": "^2.23.1",
+    "npm_package_devDependencies__types_react_select": "^3.1.2",
+    "npm_package_devDependencies__types_prosemirror_model": "^1.16.1",
+    "npm_config_if_present": "",
+    "npm_package_dependencies_typescript_collections": "^1.3.3",
+    "npm_package_dependencies_rimraf": "^3.0.0",
+    "npm_package_dependencies_react_autosuggest": "^9.4.3",
+    "npm_package_dependencies_flexlayout_react": "^0.3.11",
+    "npm_package_dependencies_find_in_files": "^0.5.0",
+    "npm_package_devDependencies__types_chai": "^4.3.0",
+    "TERM_PROGRAM_VERSION": "1.79.2",
+    "npm_package_dependencies_prosemirror_inputrules": "^1.1.3",
+    "npm_package_dependencies_bcrypt_nodejs": "0.0.3",
+    "npm_package_dependencies_async": "^2.6.2",
+    "npm_config_sign_git_tag": "",
+    "npm_config_init_author_email": "",
+    "npm_config_cache_max": "Infinity",
+    "npm_package_dependencies_uuid": "^3.4.0",
+    "npm_package_dependencies_supercluster": "^7.1.4",
+    "npm_package_dependencies_remark_gfm": "^3.0.1",
+    "npm_package_dependencies_connect_mongo": "^2.0.3",
+    "npm_package_dependencies_browser_assert": "^1.2.1",
+    "npm_package_devDependencies_sass_loader": "^7.3.1",
+    "ZDOTDIR": "/Users/smallwhale",
+    "ORIGINAL_XDG_CURRENT_DESKTOP": "undefined",
+    "MallocNanoZone": "0",
+    "npm_config_preid": "",
+    "npm_config_long": "",
+    "npm_config_local_address": "",
+    "npm_config_git_tag_version": "true",
+    "npm_config_cert": "",
+    "npm_package_dependencies_js_datepicker": "^4.6.6",
+    "npm_package_devDependencies__types_webpack_hot_middleware": "^2.25.6",
+    "npm_package_devDependencies__types_mongodb": "^3.6.20",
+    "npm_package_devDependencies__types_mocha": "^5.2.6",
+    "TERM_SESSION_ID": "F2049B14-CDC7-4078-A2C4-82A8C869F43E",
+    "npm_config_registry": "https://registry.npmjs.org/",
+    "npm_config_noproxy": "",
+    "npm_config_fetch_retries": "2",
+    "npm_package_dependencies_react_compound_slider": "^2.5.0",
+    "npm_package_dependencies_prosemirror_history": "^1.2.0",
+    "npm_package_devDependencies__types_react_color": "^2.17.6",
+    "npm_package_devDependencies__types_google_maps_react": "^2.0.5",
+    "npm_package_devDependencies__types_color": "^3.0.3",
+    "npm_package_dependencies_react_dom": "^18.2.0",
+    "npm_package_dependencies_passport_local": "^1.0.0",
+    "npm_package_dependencies__octokit_core": "^4.0.4",
+    "npm_package_devDependencies__types_async": "^2.4.1",
+    "npm_package_scripts_debug": "cross-env NODE_OPTIONS=--max_old_space_size=8192 ts-node-dev --transpile-only --inspect -- src/server/index.ts",
+    "npm_package_scripts_oldstart": "cross-env NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev --debug -- src/server/index.ts",
+    "npm_config_versions": "",
+    "npm_config_message": "%s",
+    "npm_config_key": "",
+    "npm_package_readmeFilename": "README.md",
+    "npm_package_dependencies_react_refresh_typescript": "^2.0.7",
+    "npm_package_dependencies_image_size": "^0.7.5",
+    "npm_package_dependencies_html_to_text": "^5.1.1",
+    "npm_package_dependencies_express_validator": "^5.3.1",
+    "npm_package_devDependencies_eslint_plugin_jsx_a11y": "^6.6.0",
+    "npm_package_node_child_process": "empty",
+    "npm_package_dependencies_react_resizable_rotatable_draggable": "^0.2.0",
+    "npm_package_dependencies_got": "^12.0.1",
+    "npm_package_dependencies__types_d3_color": "^2.0.3",
+    "npm_package_devDependencies_webpack": "^5.69.1",
+    "npm_package_devDependencies__types_nodemailer": "^4.6.6",
+    "npm_package_description": "Install Node.js, then, from the project directory, run",
+    "NVM_DIR": "/Users/smallwhale/.nvm",
+    "USER": "smallwhale",
+    "npm_package_dependencies__types_d3_scale": "^3.3.2",
+    "npm_package_devDependencies_dotenv": "^8.6.0",
+    "npm_package_devDependencies__types_react": "^18.0.15",
+    "npm_package_devDependencies__types_prosemirror_transform": "^1.1.5",
+    "npm_package_devDependencies__types_prosemirror_history": "^1.0.3",
+    "npm_package_dependencies_readline": "^1.3.0",
+    "npm_package_dependencies__types_supercluster": "^7.1.0",
+    "COMMAND_MODE": "unix2003",
+    "npm_config_globalconfig": "/Users/smallwhale/.nvm/versions/node/v12.16.0/etc/npmrc",
+    "npm_package_dependencies_depcheck": "^0.9.2",
+    "npm_package_dependencies__types_web": "0.0.53",
+    "npm_config_prefer_online": "",
+    "npm_config_logs_max": "10",
+    "npm_config_always_auth": "",
+    "npm_package_dependencies_react_icons": "^4.3.1",
+    "npm_package_dependencies_passport_google_oauth20": "^2.0.0",
+    "npm_package_devDependencies_webpack_dev_server": "^3.11.3",
+    "npm_package_devDependencies__types_brotli": "^1.3.1",
+    "npm_package_dependencies_url_loader": "^1.1.2",
+    "npm_package_dependencies_stream_browserify": "^3.0.0",
+    "npm_package_dependencies_prosemirror_transform": "^1.3.4",
+    "npm_package_dependencies_lodash": "^4.17.21",
+    "npm_package_dependencies_i": "^0.3.7",
+    "npm_package_devDependencies_tslint_loader": "^3.6.0",
+    "SSH_AUTH_SOCK": "/private/tmp/com.apple.launchd.iMTrb4REX6/Listeners",
+    "npm_package_dependencies_words_to_numbers": "^1.5.1",
+    "npm_package_dependencies_valid_url": "^1.0.9",
+    "npm_package_dependencies_styled_components": "^4.4.1",
+    "npm_package_dependencies_csv_parser": "^3.0.0",
+    "npm_package_dependencies_class_transformer": "^0.2.0",
+    "npm_package_devDependencies_eslint": "^8.36.0",
+    "npm_package_devDependencies__types_prosemirror_inputrules": "^1.0.4",
+    "npm_package_devDependencies__types_express": "^4.17.13",
+    "__CF_USER_TEXT_ENCODING": "0x1F5:0x0:0x0",
+    "npm_execpath": "/Users/smallwhale/.nvm/versions/node/v12.16.0/lib/node_modules/npm/bin/npm-cli.js",
+    "npm_config_global_style": "",
+    "npm_config_cache_lock_retries": "10",
+    "npm_package_dependencies_wikijs": "^6.3.3",
+    "npm_package_dependencies_bluebird": "^3.7.2",
+    "npm_package_devDependencies__types_react_typist": "^2.0.3",
+    "npm_config_update_notifier": "true",
+    "npm_config_cafile": "",
+    "npm_package_dependencies_util": "^0.12.4",
+    "npm_package_dependencies_raw_loader": "^1.0.0",
+    "npm_package_dependencies_https_browserify": "^1.0.0",
+    "npm_package_dependencies_brotli": "^1.3.3",
+    "npm_package_dependencies__mui_material": "^5.13.1",
+    "npm_package_dependencies__fortawesome_react_fontawesome": "^0.2.0",
+    "npm_package_devDependencies__types_passport_google_oauth20": "^2.0.11",
+    "npm_package_dependencies_cors": "^2.8.5",
+    "npm_package_dependencies_bezier_js": "^4.1.1",
+    "npm_package_dependencies__fortawesome_free_brands_svg_icons": "^6.3.0",
+    "npm_config_heading": "npm",
+    "npm_config_audit_level": "low",
+    "npm_package_dependencies_chrome": "^0.1.0",
+    "npm_package_dependencies__react_three_fiber": "^6.2.3",
+    "npm_package_devDependencies_eslint_plugin_prettier": "^4.2.1",
+    "npm_package_devDependencies_copy_webpack_plugin": "^4.6.0",
+    "npm_package_devDependencies__types_react_measure": "^2.0.8",
+    "npm_package_devDependencies__types_react_dom": "^18.0.6",
+    "npm_package_devDependencies__types_mobile_detect": "^1.3.4",
+    "npm_config_searchlimit": "20",
+    "npm_config_read_only": "",
+    "npm_config_offline": "",
+    "npm_config_fetch_retry_mintimeout": "10000",
+    "npm_package_dependencies_react_typist": "^2.0.5",
+    "npm_package_dependencies_mobx_react_devtools": "^6.1.1",
+    "npm_package_dependencies_md5_file": "^5.0.0",
+    "npm_package_dependencies_forever_agent": "^0.6.1",
+    "npm_package_devDependencies__types_xregexp": "^4.4.0",
+    "npm_package_devDependencies__types_typescript": "^2.0.0",
+    "npm_package_devDependencies__types_request": "^2.48.8",
+    "npm_package_devDependencies__types_prosemirror_commands": "^1.0.4",
+    "npm_config_json": "",
+    "npm_config_access": "",
+    "npm_config_argv": "{\"remain\":[],\"cooked\":[\"start\"],\"original\":[\"start\"]}",
+    "npm_package_dependencies__fortawesome_free_solid_svg_icons": "^6.3.0",
+    "npm_package_devDependencies__types_socket_io": "^2.1.13",
+    "PATH": "/Users/smallwhale/.nvm/versions/node/v12.16.0/lib/node_modules/npm/node_modules/npm-lifecycle/node-gyp-bin:/Users/smallwhale/Desktop/Projects/Dash-Web/node_modules/.bin:/Users/smallwhale/.nvm/versions/node/v12.16.0/bin:/Users/smallwhale/.nvm/versions/node/v18.16.0/bin:/Library/Frameworks/Python.framework/Versions/3.11/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/Users/smallwhale/.nvm/versions/node/v18.16.0/bin:/Library/Frameworks/Python.framework/Versions/3.11/bin:/opt/homebrew/bin:/opt/homebrew/sbin",
+    "npm_config_allow_same_version": "",
+    "npm_package_dependencies_webrtc_adapter": "^7.7.1",
+    "npm_package_dependencies_react_reveal": "^1.2.2",
+    "npm_package_dependencies_prosemirror_schema_list": "^1.1.6",
+    "npm_package_dependencies__material_ui_core": "^4.12.3",
+    "npm_package_devDependencies__types_rimraf": "^2.0.5",
+    "npm_package_devDependencies__types_connect_flash": "0.0.34",
+    "npm_config_https_proxy": "",
+    "npm_config_engine_strict": "",
+    "npm_config_description": "true",
+    "npm_package_dependencies_pug": "^2.0.4",
+    "npm_package_dependencies_prosemirror_keymap": "^1.1.5",
+    "npm_package_dependencies_pdfjs_dist": "^2.14.305",
+    "npm_package_dependencies_mobile_detect": "^1.4.5",
+    "npm_package_dependencies_image_size_stream": "^1.1.0",
+    "npm_package_dependencies_golden_layout": "^1.5.9",
+    "npm_package_dependencies_child_process": "^1.0.2",
+    "npm_package_dependencies__types_d3_axis": "^2.1.3",
+    "_": "/Users/smallwhale/Desktop/Projects/Dash-Web/node_modules/.bin/cross-env",
+    "LaunchInstanceID": "57D8446D-0E37-4DF2-8110-E0AA2FF019D8",
+    "npm_config_userconfig": "/Users/smallwhale/.npmrc",
+    "npm_config_init_module": "/Users/smallwhale/.npm-init.js",
+    "npm_package_dependencies__react_google_maps_api": "^2.7.0",
+    "USER_ZDOTDIR": "/Users/smallwhale",
+    "__CFBundleIdentifier": "com.microsoft.VSCode",
+    "npm_config_cidr": "",
+    "npm_package_dependencies_puppeteer": "^3.3.0",
+    "npm_package_dependencies_prosemirror_view": "^1.26.5",
+    "npm_package_dependencies_mongodb": "^3.7.3",
+    "npm_package_dependencies_google_auth_library": "^4.2.4",
+    "npm_package_dependencies_bootstrap": "^4.6.1",
+    "npm_package_devDependencies_eslint_config_airbnb": "^19.0.4",
+    "PWD": "/Users/smallwhale/Desktop/Projects/Dash-Web",
+    "npm_config_user": "501",
+    "npm_config_node_version": "12.16.0",
+    "npm_package_dependencies_node_sass": "^4.14.1",
+    "npm_package_dependencies_howler": "^2.2.3",
+    "npm_package_dependencies_expressjs": "^1.0.1",
+    "npm_package_dependencies_core_js": "^3.28.0",
+    "npm_package_dependencies_browndash_components": "0.0.82",
+    "npm_package_devDependencies_eslint_plugin_react_hooks": "^4.6.0",
+    "npm_package_devDependencies__types_lodash": "^4.14.179",
+    "npm_lifecycle_event": "start",
+    "npm_package_dependencies_react_table": "^6.11.5",
+    "npm_package_dependencies_react_loading": "^2.0.3",
+    "npm_package_dependencies_mobx": "^5.15.7",
+    "npm_package_dependencies_babel": "^6.23.0",
+    "npm_package_devDependencies_jsdom": "^15.2.1",
+    "npm_package_devDependencies_chai": "^4.3.6",
+    "npm_config_save": "true",
+    "npm_config_ignore_prepublish": "",
+    "npm_config_editor": "vi",
+    "npm_config_auth_type": "legacy",
+    "npm_package_dependencies_npm": "^6.14.18",
+    "npm_package_dependencies_node_stream_zip": "^1.15.0",
+    "npm_package_dependencies_image_data_uri": "^2.0.1",
+    "npm_package_scripts_start_release": "cross-env RELEASE=true NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev -- src/server/index.ts",
+    "npm_package_name": "dash",
+    "LANG": "en_US.UTF-8",
+    "npm_config_tag": "latest",
+    "npm_config_script_shell": "",
+    "npm_package_dependencies_query_string": "^6.14.1",
+    "npm_package_dependencies_mobx_utils": "^5.6.2",
+    "npm_package_dependencies_file_saver": "^2.0.5",
+    "npm_package_dependencies_body_parser": "^1.19.2",
+    "npm_package_dependencies__types_reveal": "^3.3.33",
+    "npm_package_devDependencies_eslint_plugin_import": "^2.26.0",
+    "npm_package_devDependencies__types_prosemirror_view": "^1.23.1",
+    "npm_config_progress": "true",
+    "npm_config_global": "",
+    "npm_config_before": "",
+    "npm_package_dependencies_xoauth2": "^1.2.0",
+    "npm_package_dependencies_standard_http_error": "^2.0.1",
+    "npm_package_dependencies_react_loader_spinner": "^5.3.4",
+    "npm_package_dependencies_http_browserify": "^1.7.0",
+    "npm_package_dependencies__types_d3_selection": "^2.0.1",
+    "npm_package_dependencies__hig_flyout": "^1.3.1",
+    "npm_package_devDependencies_fork_ts_checker_webpack_plugin": "^1.6.0",
+    "npm_package_scripts_build": "cross-env NODE_OPTIONS=--max_old_space_size=8192 webpack --env production",
+    "npm_package_scripts_start": "cross-env NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev --debug --transpile-only -- src/server/index.ts",
+    "npm_config_searchstaleness": "900",
+    "npm_config_optional": "true",
+    "npm_config_ham_it_up": "",
+    "npm_package_dependencies_sharp": "^0.23.4",
+    "npm_package_dependencies_rc_switch": "^1.9.2",
+    "npm_package_dependencies_googlephotos": "^0.2.5",
+    "npm_package_dependencies_exifr": "^7.1.3",
+    "npm_package_dependencies__types_google_maps": "^3.2.3",
+    "npm_package_dependencies__types_bezier_js": "^4.1.0",
+    "npm_package_dependencies__ffmpeg_core": "0.10.0",
+    "npm_package_devDependencies_ts_loader": "^5.3.3",
+    "npm_package_devDependencies__types_bcrypt_nodejs": "0.0.30",
+    "VSCODE_GIT_ASKPASS_EXTRA_ARGS": "--ms-enable-electron-run-as-node",
+    "XPC_FLAGS": "0x0",
+    "npm_config_save_prod": "",
+    "npm_config_force": "",
+    "npm_config_bin_links": "true",
+    "npm_package_devDependencies__types_youtube": "0.0.39",
+    "npm_config_searchopts": "",
+    "npm_package_dependencies_react_beautiful_dnd": "^13.1.0",
+    "npm_package_dependencies_jszip": "^3.7.1",
+    "npm_package_dependencies_csv_stringify": "^6.3.0",
+    "npm_package_devDependencies__types_react_icons": "^3.0.0",
+    "npm_config_node_gyp": "/Users/smallwhale/.nvm/versions/node/v12.16.0/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js",
+    "npm_config_depth": "Infinity",
+    "npm_package_dependencies_google_maps_react": "^2.0.6",
+    "npm_package_dependencies_express_session": "^1.17.2",
+    "npm_package_devDependencies_eslint_plugin_node": "^11.1.0",
+    "npm_package_devDependencies_eslint_config_prettier": "^8.5.0",
+    "npm_package_main": "index.js",
+    "npm_config_sso_poll_frequency": "500",
+    "npm_config_rebuild_bundle": "true",
+    "npm_package_dependencies_chart_js": "^3.8.0",
+    "npm_package_dependencies__emotion_react": "^11.11.0",
+    "npm_package_devDependencies__types_prosemirror_menu": "^1.0.6",
+    "npm_package_devDependencies__types_prosemirror_keymap": "^1.0.4",
+    "npm_package_devDependencies__types_pdfjs_dist": "^2.10.378",
+    "npm_package_devDependencies__types_exif": "^0.6.3",
+    "npm_package_version": "1.0.0",
+    "XPC_SERVICE_NAME": "0",
+    "npm_config_unicode": "true",
+    "VSCODE_INJECTION": "1",
+    "npm_package_dependencies_typescript_language_server": "^0.4.0",
+    "npm_package_dependencies_prosemirror_model": "^1.18.1",
+    "npm_package_dependencies__ffmpeg_ffmpeg": "0.10.0",
+    "SHLVL": "4",
+    "HOME": "/Users/smallwhale",
+    "npm_config_fetch_retry_maxtimeout": "60000",
+    "npm_package_dependencies_request_promise": "^4.2.6",
+    "npm_package_dependencies_react_markdown": "^8.0.3",
+    "npm_package_dependencies__hig_theme_context": "^2.1.3",
+    "npm_package_devDependencies__types_react_autosuggest": "^9.3.14",
+    "npm_package_devDependencies__types_mongoose": "^5.11.97",
+    "npm_package_devDependencies__types_d3": "^7.4.0",
+    "npm_package_devDependencies__types_animejs": "^2.0.2",
+    "npm_package_scripts_test": "mocha -r ts-node/register test/**/*.ts",
+    "VSCODE_GIT_ASKPASS_MAIN": "/Applications/Visual Studio Code.app/Contents/Resources/app/extensions/git/dist/askpass-main.js",
+    "npm_config_tag_version_prefix": "v",
+    "npm_config_strict_ssl": "true",
+    "npm_config_sso_type": "oauth",
+    "npm_config_scripts_prepend_node_path": "warn-only",
+    "npm_config_save_prefix": "^",
+    "npm_config_loglevel": "notice",
+    "npm_config_ca": "",
+    "npm_package_dependencies_three": "^0.127.0",
+    "npm_package_dependencies_openai": "^3.2.1",
+    "npm_package_dependencies_mobx_react": "^5.4.4",
+    "npm_package_dependencies_cookie_parser": "^1.4.6",
+    "npm_package_dependencies_adm_zip": "^0.4.16",
+    "npm_package_devDependencies_eslint_config_node": "^4.1.0",
+    "npm_config_save_exact": "",
+    "npm_config_group": "20",
+    "npm_config_fetch_retry_factor": "10",
+    "npm_config_dev": "",
+    "npm_package_devDependencies_webpack_hot_middleware": "^2.25.1",
+    "npm_package_devDependencies_cross_env": "^5.2.1",
+    "HOMEBREW_PREFIX": "/opt/homebrew",
+    "npm_config_version": "",
+    "npm_config_prefer_offline": "",
+    "npm_config_cache_lock_stale": "60000",
+    "npm_package_devDependencies__types_prosemirror_state": "^1.2.8",
+    "npm_package_devDependencies__types_body_parser": "^1.19.2",
+    "npm_config_otp": "",
+    "npm_config_cache_min": "10",
+    "npm_package_dependencies_react_color": "^2.19.3",
+    "npm_package_dependencies_d3": "^7.6.1",
+    "npm_package_devDependencies_ts_node": "^10.9.1",
+    "npm_package_devDependencies__types_react_grid_layout": "^1.3.2",
+    "npm_config_searchexclude": "",
+    "npm_config_cache": "/Users/smallwhale/.npm",
+    "npm_package_dependencies_tough_cookie": "^4.0.0",
+    "npm_package_dependencies_googleapis": "^40.0.0",
+    "npm_package_devDependencies__types_valid_url": "^1.0.3",
+    "npm_package_devDependencies__types_passport": "^1.0.9",
+    "npm_package_devDependencies__types_adm_zip": "^0.4.34",
+    "LOGNAME": "smallwhale",
+    "npm_lifecycle_script": "cross-env NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev --debug --transpile-only -- src/server/index.ts",
+    "npm_config_color": "true",
+    "npm_package_dependencies_solr_node": "^1.2.1",
+    "npm_package_dependencies_react_transition_group": "^4.4.2",
+    "npm_package_dependencies_iink_js": "^1.5.4",
+    "npm_package_dependencies_html_webpack_plugin": "^5.5.0",
+    "npm_config_proxy": "",
+    "npm_config_package_lock": "true",
+    "npm_package_dependencies_prosemirror_state": "^1.4.1",
+    "npm_package_dependencies_nodemon": "^1.19.4",
+    "npm_package_dependencies_function_plot": "^1.22.8",
+    "npm_package_dependencies_equation_editor_react": "github:bobzel/equation-editor-react#useLocally",
+    "npm_package_devDependencies__types_socket_io_parser": "^3.0.0",
+    "VSCODE_GIT_IPC_HANDLE": "/var/folders/bv/1xck6d7j7bz14bvhgdsllbj40000gn/T/vscode-git-b8c8318074.sock",
+    "npm_config_package_lock_only": "",
+    "npm_config_fund": "true",
+    "npm_package_dependencies_react": "^18.2.0",
+    "npm_package_dependencies_bingmaps_react": "^1.2.10",
+    "npm_package_devDependencies_scss_loader": "0.0.1",
+    "npm_package_devDependencies__types_cookie_session": "^2.0.44",
+    "npm_config_save_optional": "",
+    "npm_package_dependencies_textarea_caret": "^3.1.0",
+    "npm_package_dependencies_react_measure": "^2.5.2",
+    "npm_package_dependencies_exif": "^0.6.0",
+    "NVM_BIN": "/Users/smallwhale/.nvm/versions/node/v12.16.0/bin",
+    "npm_config_ignore_scripts": "",
+    "npm_config_user_agent": "npm/6.13.4 node/v12.16.0 darwin x64",
+    "npm_package_dependencies_react_resizable": "^1.11.1",
+    "npm_package_dependencies_prosemirror_commands": "^1.2.1",
+    "npm_package_dependencies_memorystream": "^0.3.1",
+    "npm_package_dependencies_formidable": "1.2.1",
+    "npm_package_devDependencies__types_uuid": "^3.4.10",
+    "npm_config_cache_lock_wait": "10000",
+    "npm_package_dependencies_socket_io_client": "^2.5.0",
+    "npm_package_dependencies_recharts": "^2.1.12",
+    "npm_package_dependencies_react_chartjs_2": "^4.3.0",
+    "npm_package_dependencies_fluent_ffmpeg": "^2.1.2",
+    "npm_package_dependencies__types_cors": "^2.8.12",
+    "npm_package_devDependencies__types_node": "^10.17.60",
+    "npm_package_devDependencies__types_file_saver": "^2.0.5",
+    "VSCODE_GIT_ASKPASS_NODE": "/Applications/Visual Studio Code.app/Contents/Frameworks/Code Helper (Plugin).app/Contents/MacOS/Code Helper (Plugin)",
+    "GIT_ASKPASS": "/Applications/Visual Studio Code.app/Contents/Resources/app/extensions/git/dist/askpass.sh",
+    "HOMEBREW_CELLAR": "/opt/homebrew/Cellar",
+    "INFOPATH": "/opt/homebrew/share/info:/opt/homebrew/share/info:",
+    "npm_config_production": "",
+    "npm_package_dependencies_jsonschema": "^1.4.0",
+    "npm_package_dependencies_ffmpeg": "0.0.4",
+    "npm_package_dependencies_cookie_session": "^2.0.0",
+    "npm_package_dependencies_color": "^3.2.1",
+    "npm_package_devDependencies__types_webpack": "^4.41.32",
+    "npm_package_devDependencies__types_request_promise": "^4.1.48",
+    "npm_package_devDependencies__types_prosemirror_schema_list": "^1.0.3",
+    "npm_config_send_metrics": "",
+    "npm_config_save_bundle": "",
+    "npm_package_dependencies_web_request": "^1.0.7",
+    "npm_package_dependencies_react_datepicker": "^3.8.0",
+    "npm_package_dependencies_express": "^4.17.3",
+    "npm_package_dependencies_D": "^1.0.0",
+    "npm_package_dependencies__types_formidable": "1.0.31",
+    "npm_package_devDependencies__types_rc_switch": "^1.9.2",
+    "npm_package_devDependencies__types_prosemirror_dev_tools": "^2.1.0",
+    "npm_package_devDependencies__types_jquery": "^3.5.14",
+    "npm_config_umask": "0022",
+    "npm_config_node_options": "",
+    "npm_config_init_version": "1.0.0",
+    "npm_package_dependencies_https": "^1.0.0",
+    "npm_package_dependencies_array_batcher": "^1.2.3",
+    "npm_package_dependencies__fortawesome_free_regular_svg_icons": "^6.3.0",
+    "npm_package_devDependencies__types_shelljs": "^0.8.11",
+    "npm_package_devDependencies__types_libxmljs": "^0.18.7",
+    "npm_package_devDependencies__types_express_validator": "^3.0.0",
+    "npm_package_devDependencies__types_bluebird": "^3.5.36",
+    "npm_config_init_author_name": "",
+    "npm_config_git": "git",
+    "npm_config_scope": "",
+    "npm_package_dependencies_react_select": "^3.2.0",
+    "npm_package_dependencies_pdf_parse": "^1.1.1",
+    "npm_package_dependencies_colors": "^1.4.0",
+    "npm_package_dependencies_archiver": "^3.1.1",
+    "npm_package_devDependencies_css_loader": "^2.1.1",
+    "npm_package_devDependencies__types_socket_io_client": "^1.4.36",
+    "SECURITYSESSIONID": "186a4",
+    "npm_config_unsafe_perm": "true",
+    "npm_config_tmp": "/var/folders/bv/1xck6d7j7bz14bvhgdsllbj40000gn/T",
+    "npm_config_onload_script": "",
+    "npm_package_dependencies_serializr": "^1.5.4",
+    "npm_package_dependencies_fit_curve": "^0.1.7",
+    "npm_package_dependencies__webscopeio_react_textarea_autocomplete": "^4.9.1",
+    "npm_package_dependencies__types_three": "^0.126.2",
+    "npm_package_devDependencies_ts_node_dev": "^2.0.0",
+    "npm_node_execpath": "/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node",
+    "npm_config_prefix": "/Users/smallwhale/.nvm/versions/node/v12.16.0",
+    "npm_config_link": "",
+    "npm_config_format_package_lock": "true",
+    "npm_package_dependencies_passport": "^0.4.0",
+    "npm_package_devDependencies_eslint_plugin_react": "^7.30.1",
+    "npm_package_devDependencies__types_react_table": "^6.8.9",
+    "npm_package_devDependencies__types_react_reconciler": "^0.26.4",
+    "COLORTERM": "truecolor",
+    "NODE_OPTIONS": "--max_old_space_size=4096",
+    "TS_NODE_DEV": "true",
+    "_CLIENT_OPENAI_KEY": "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",
+    "_CLIENT_GITHUB_ACCESS_TOKEN": "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",
+    "VIPSHOME": "/usr/local/Cellar/vips/8.8.1",
+    "TYPESCRIPT_PATH": "/Users/smallwhale/Desktop/Projects/Dash-Web/node_modules/typescript/lib/typescript.js",
+    "TSCONFIG": "/Users/smallwhale/Desktop/Projects/Dash-Web/tsconfig.json",
+    "COMPILER_OPTIONS": "{}",
+    "TSLINT": "true",
+    "CONTEXT": "/Users/smallwhale/Desktop/Projects/Dash-Web",
+    "TSLINTAUTOFIX": "false",
+    "ESLINT": "false",
+    "ESLINT_OPTIONS": "{}",
+    "WATCH": "",
+    "WORK_DIVISION": "1",
+    "MEMORY_LIMIT": "2048",
+    "CHECK_SYNTACTIC_ERRORS": "false",
+    "USE_INCREMENTAL_API": "true",
+    "VUE": "false"
+  },
+  "userLimits": {
+    "core_file_size_blocks": {
+      "soft": 0,
+      "hard": "unlimited"
+    },
+    "data_seg_size_kbytes": {
+      "soft": "unlimited",
+      "hard": "unlimited"
+    },
+    "file_size_blocks": {
+      "soft": "unlimited",
+      "hard": "unlimited"
+    },
+    "max_locked_memory_bytes": {
+      "soft": "unlimited",
+      "hard": "unlimited"
+    },
+    "max_memory_size_kbytes": {
+      "soft": "unlimited",
+      "hard": "unlimited"
+    },
+    "open_files": {
+      "soft": 1048575,
+      "hard": "unlimited"
+    },
+    "stack_size_bytes": {
+      "soft": 8372224,
+      "hard": 67092480
+    },
+    "cpu_time_seconds": {
+      "soft": "unlimited",
+      "hard": "unlimited"
+    },
+    "max_user_processes": {
+      "soft": 1333,
+      "hard": 2000
+    },
+    "virtual_memory_kbytes": {
+      "soft": "unlimited",
+      "hard": "unlimited"
+    }
+  },
+  "sharedObjects": [
+    "/Users/smallwhale/.nvm/versions/node/v12.16.0/bin/node",
+    "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation",
+    "/usr/lib/libobjc.A.dylib",
+    "/System/Library/PrivateFrameworks/CoreServicesInternal.framework/Versions/A/CoreServicesInternal",
+    "/usr/lib/liboah.dylib",
+    "/usr/lib/libfakelink.dylib",
+    "/usr/lib/libicucore.A.dylib",
+    "/usr/lib/libSystem.B.dylib",
+    "/System/Library/PrivateFrameworks/SoftLinking.framework/Versions/A/SoftLinking",
+    "/usr/lib/libc++abi.dylib",
+    "/usr/lib/libc++.1.dylib",
+    "/usr/lib/system/libcache.dylib",
+    "/usr/lib/system/libcommonCrypto.dylib",
+    "/usr/lib/system/libcompiler_rt.dylib",
+    "/usr/lib/system/libcopyfile.dylib",
+    "/usr/lib/system/libcorecrypto.dylib",
+    "/usr/lib/system/libdispatch.dylib",
+    "/usr/lib/system/libdyld.dylib",
+    "/usr/lib/system/libkeymgr.dylib",
+    "/usr/lib/system/libmacho.dylib",
+    "/usr/lib/system/libquarantine.dylib",
+    "/usr/lib/system/libremovefile.dylib",
+    "/usr/lib/system/libsystem_asl.dylib",
+    "/usr/lib/system/libsystem_blocks.dylib",
+    "/usr/lib/system/libsystem_c.dylib",
+    "/usr/lib/system/libsystem_collections.dylib",
+    "/usr/lib/system/libsystem_configuration.dylib",
+    "/usr/lib/system/libsystem_containermanager.dylib",
+    "/usr/lib/system/libsystem_coreservices.dylib",
+    "/usr/lib/system/libsystem_darwin.dylib",
+    "/usr/lib/system/libsystem_dnssd.dylib",
+    "/usr/lib/system/libsystem_featureflags.dylib",
+    "/usr/lib/system/libsystem_info.dylib",
+    "/usr/lib/system/libsystem_m.dylib",
+    "/usr/lib/system/libsystem_malloc.dylib",
+    "/usr/lib/system/libsystem_networkextension.dylib",
+    "/usr/lib/system/libsystem_notify.dylib",
+    "/usr/lib/system/libsystem_sandbox.dylib",
+    "/usr/lib/system/libsystem_secinit.dylib",
+    "/usr/lib/system/libsystem_kernel.dylib",
+    "/usr/lib/system/libsystem_platform.dylib",
+    "/usr/lib/system/libsystem_pthread.dylib",
+    "/usr/lib/system/libsystem_symptoms.dylib",
+    "/usr/lib/system/libsystem_trace.dylib",
+    "/usr/lib/system/libunwind.dylib",
+    "/usr/lib/system/libxpc.dylib",
+    "/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit",
+    "/System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices",
+    "/usr/lib/libDiagnosticMessagesClient.dylib",
+    "/usr/lib/libenergytrace.dylib",
+    "/usr/lib/libbsm.0.dylib",
+    "/usr/lib/libz.1.dylib",
+    "/usr/lib/system/libkxld.dylib",
+    "/System/Library/Frameworks/CFNetwork.framework/Versions/A/CFNetwork",
+    "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/FSEvents.framework/Versions/A/FSEvents",
+    "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/CarbonCore",
+    "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/Metadata.framework/Versions/A/Metadata",
+    "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/OSServices.framework/Versions/A/OSServices",
+    "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SearchKit.framework/Versions/A/SearchKit",
+    "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/AE.framework/Versions/A/AE",
+    "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/LaunchServices",
+    "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/DictionaryServices.framework/Versions/A/DictionaryServices",
+    "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SharedFileList.framework/Versions/A/SharedFileList",
+    "/System/Library/Frameworks/Security.framework/Versions/A/Security",
+    "/System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration",
+    "/usr/lib/libapple_nghttp2.dylib",
+    "/usr/lib/libcompression.dylib",
+    "/usr/lib/libnetwork.dylib",
+    "/usr/lib/libsqlite3.dylib",
+    "/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation",
+    "/System/Library/Frameworks/Network.framework/Versions/A/Network",
+    "/usr/lib/libCoreEntitlements.dylib",
+    "/System/Library/PrivateFrameworks/MessageSecurity.framework/Versions/A/MessageSecurity",
+    "/System/Library/PrivateFrameworks/ProtocolBuffer.framework/Versions/A/ProtocolBuffer",
+    "/usr/lib/libMobileGestalt.dylib",
+    "/System/Library/PrivateFrameworks/AppleFSCompression.framework/Versions/A/AppleFSCompression",
+    "/usr/lib/libcoretls.dylib",
+    "/usr/lib/libcoretls_cfhelpers.dylib",
+    "/usr/lib/libpam.2.dylib",
+    "/usr/lib/libxar.1.dylib",
+    "/System/Library/PrivateFrameworks/CoreAutoLayout.framework/Versions/A/CoreAutoLayout",
+    "/System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration",
+    "/usr/lib/libarchive.2.dylib",
+    "/usr/lib/libxml2.2.dylib",
+    "/usr/lib/liblangid.dylib",
+    "/System/Library/Frameworks/Combine.framework/Versions/A/Combine",
+    "/usr/lib/swift/libswiftCore.dylib",
+    "/usr/lib/swift/libswiftCoreFoundation.dylib",
+    "/usr/lib/swift/libswiftDarwin.dylib",
+    "/usr/lib/swift/libswiftDispatch.dylib",
+    "/usr/lib/swift/libswiftIOKit.dylib",
+    "/usr/lib/swift/libswiftObjectiveC.dylib",
+    "/usr/lib/swift/libswiftXPC.dylib",
+    "/usr/lib/swift/libswift_Concurrency.dylib",
+    "/usr/lib/swift/libswift_StringProcessing.dylib",
+    "/usr/lib/swift/libswiftos.dylib",
+    "/System/Library/PrivateFrameworks/AppleSystemInfo.framework/Versions/A/AppleSystemInfo",
+    "/System/Library/PrivateFrameworks/IOMobileFramebuffer.framework/Versions/A/IOMobileFramebuffer",
+    "/System/Library/Frameworks/IOSurface.framework/Versions/A/IOSurface",
+    "/usr/lib/libpcap.A.dylib",
+    "/usr/lib/libdns_services.dylib",
+    "/usr/lib/liblzma.5.dylib",
+    "/usr/lib/libbz2.1.0.dylib",
+    "/usr/lib/libiconv.2.dylib",
+    "/usr/lib/libcharset.1.dylib",
+    "/usr/lib/swift/libswift_RegexParser.dylib",
+    "/usr/lib/libheimdal-asn1.dylib",
+    "/usr/lib/libCheckFix.dylib",
+    "/System/Library/PrivateFrameworks/TCC.framework/Versions/A/TCC",
+    "/System/Library/PrivateFrameworks/CoreNLP.framework/Versions/A/CoreNLP",
+    "/System/Library/PrivateFrameworks/MetadataUtilities.framework/Versions/A/MetadataUtilities",
+    "/System/Library/Frameworks/Accelerate.framework/Versions/A/Accelerate",
+    "/usr/lib/libmecab.dylib",
+    "/usr/lib/libCRFSuite.dylib",
+    "/usr/lib/libgermantok.dylib",
+    "/usr/lib/libThaiTokenizer.dylib",
+    "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vImage.framework/Versions/A/vImage",
+    "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/vecLib",
+    "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvMisc.dylib",
+    "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvDSP.dylib",
+    "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib",
+    "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLAPACK.dylib",
+    "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLinearAlgebra.dylib",
+    "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparseBLAS.dylib",
+    "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libQuadrature.dylib",
+    "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBNNS.dylib",
+    "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparse.dylib",
+    "/System/Library/PrivateFrameworks/MIL.framework/Versions/A/MIL",
+    "/System/Library/Frameworks/OpenDirectory.framework/Versions/A/Frameworks/CFOpenDirectory.framework/Versions/A/CFOpenDirectory",
+    "/System/Library/Frameworks/OpenDirectory.framework/Versions/A/OpenDirectory",
+    "/System/Library/PrivateFrameworks/APFS.framework/Versions/A/APFS",
+    "/System/Library/Frameworks/SecurityFoundation.framework/Versions/A/SecurityFoundation",
+    "/usr/lib/libutil.dylib",
+    "/System/Library/PrivateFrameworks/InstalledContentLibrary.framework/Versions/A/InstalledContentLibrary",
+    "/System/Library/PrivateFrameworks/CoreServicesStore.framework/Versions/A/CoreServicesStore",
+    "/usr/lib/libapp_launch_measurement.dylib",
+    "/System/Library/PrivateFrameworks/AppleMobileFileIntegrity.framework/Versions/A/AppleMobileFileIntegrity",
+    "/usr/lib/libmis.dylib",
+    "/System/Library/PrivateFrameworks/MobileSystemServices.framework/Versions/A/MobileSystemServices",
+    "/System/Library/PrivateFrameworks/ConfigProfileHelper.framework/Versions/A/ConfigProfileHelper",
+    "/System/Library/PrivateFrameworks/CoreAnalytics.framework/Versions/A/CoreAnalytics",
+    "/System/Library/PrivateFrameworks/AppleSauce.framework/Versions/A/AppleSauce",
+    "/System/Library/PrivateFrameworks/LanguageModeling.framework/Versions/A/LanguageModeling",
+    "/usr/lib/libxslt.1.dylib",
+    "/usr/lib/libcmph.dylib",
+    "/System/Library/PrivateFrameworks/CoreEmoji.framework/Versions/A/CoreEmoji",
+    "/System/Library/PrivateFrameworks/LinguisticData.framework/Versions/A/LinguisticData",
+    "/System/Library/PrivateFrameworks/Lexicon.framework/Versions/A/Lexicon",
+    "/System/Library/PrivateFrameworks/BackgroundTaskManagement.framework/Versions/A/BackgroundTaskManagement",
+    "/usr/lib/libTLE.dylib",
+    "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices",
+    "/System/Library/Frameworks/CoreGraphics.framework/Versions/A/CoreGraphics",
+    "/System/Library/Frameworks/CoreText.framework/Versions/A/CoreText",
+    "/System/Library/Frameworks/ImageIO.framework/Versions/A/ImageIO",
+    "/System/Library/Frameworks/ColorSync.framework/Versions/A/ColorSync",
+    "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/ATS",
+    "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/HIServices",
+    "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/PrintCore.framework/Versions/A/PrintCore",
+    "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/QD.framework/Versions/A/QD",
+    "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ColorSyncLegacy.framework/Versions/A/ColorSyncLegacy",
+    "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/SpeechSynthesis.framework/Versions/A/SpeechSynthesis",
+    "/System/Library/PrivateFrameworks/SkyLight.framework/Versions/A/SkyLight",
+    "/System/Library/PrivateFrameworks/FontServices.framework/libFontParser.dylib",
+    "/System/Library/PrivateFrameworks/RunningBoardServices.framework/Versions/A/RunningBoardServices",
+    "/System/Library/PrivateFrameworks/IOSurfaceAccelerator.framework/Versions/A/IOSurfaceAccelerator",
+    "/System/Library/PrivateFrameworks/WatchdogClient.framework/Versions/A/WatchdogClient",
+    "/System/Library/Frameworks/CoreDisplay.framework/Versions/A/CoreDisplay",
+    "/System/Library/Frameworks/CoreMedia.framework/Versions/A/CoreMedia",
+    "/System/Library/PrivateFrameworks/IOAccelerator.framework/Versions/A/IOAccelerator",
+    "/System/Library/Frameworks/Metal.framework/Versions/A/Metal",
+    "/System/Library/Frameworks/CoreVideo.framework/Versions/A/CoreVideo",
+    "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/MetalPerformanceShaders",
+    "/System/Library/PrivateFrameworks/MultitouchSupport.framework/Versions/A/MultitouchSupport",
+    "/System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore",
+    "/System/Library/Frameworks/VideoToolbox.framework/Versions/A/VideoToolbox",
+    "/System/Library/PrivateFrameworks/BaseBoard.framework/Versions/A/BaseBoard",
+    "/System/Library/PrivateFrameworks/AppleJPEG.framework/Versions/A/AppleJPEG",
+    "/usr/lib/libexpat.1.dylib",
+    "/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libPng.dylib",
+    "/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libTIFF.dylib",
+    "/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libGIF.dylib",
+    "/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJP2.dylib",
+    "/usr/lib/libate.dylib",
+    "/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJPEG.dylib",
+    "/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libRadiance.dylib",
+    "/System/Library/PrivateFrameworks/GPUWrangler.framework/Versions/A/GPUWrangler",
+    "/System/Library/PrivateFrameworks/IOPresentment.framework/Versions/A/IOPresentment",
+    "/System/Library/PrivateFrameworks/DSExternalDisplay.framework/Versions/A/DSExternalDisplay",
+    "/System/Library/PrivateFrameworks/GPUCompiler.framework/Versions/31001/Libraries/libllvm-flatbuffers.dylib",
+    "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreFSCache.dylib",
+    "/System/Library/PrivateFrameworks/GPUCompiler.framework/Versions/31001/Libraries/libGPUCompilerUtils.dylib",
+    "/System/Library/PrivateFrameworks/CMCaptureCore.framework/Versions/A/CMCaptureCore",
+    "/usr/lib/libspindump.dylib",
+    "/System/Library/Frameworks/CoreAudio.framework/Versions/A/CoreAudio",
+    "/System/Library/Frameworks/ExtensionFoundation.framework/Versions/A/ExtensionFoundation",
+    "/System/Library/PrivateFrameworks/CoreTime.framework/Versions/A/CoreTime",
+    "/System/Library/PrivateFrameworks/AppServerSupport.framework/Versions/A/AppServerSupport",
+    "/System/Library/PrivateFrameworks/perfdata.framework/Versions/A/perfdata",
+    "/System/Library/PrivateFrameworks/AudioToolboxCore.framework/Versions/A/AudioToolboxCore",
+    "/System/Library/PrivateFrameworks/caulk.framework/Versions/A/caulk",
+    "/usr/lib/libAudioStatistics.dylib",
+    "/System/Library/PrivateFrameworks/SystemPolicy.framework/Versions/A/SystemPolicy",
+    "/usr/lib/libSMC.dylib",
+    "/System/Library/Frameworks/CoreMIDI.framework/Versions/A/CoreMIDI",
+    "/usr/lib/libAudioToolboxUtility.dylib",
+    "/System/Library/PrivateFrameworks/OSAServicesClient.framework/Versions/A/OSAServicesClient",
+    "/usr/lib/libperfcheck.dylib",
+    "/System/Library/PrivateFrameworks/PlugInKit.framework/Versions/A/PlugInKit",
+    "/System/Library/PrivateFrameworks/AssertionServices.framework/Versions/A/AssertionServices",
+    "/System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL",
+    "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLU.dylib",
+    "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGFXShared.dylib",
+    "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib",
+    "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLImage.dylib",
+    "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCVMSPluginSupport.dylib",
+    "/usr/lib/libRosetta.dylib",
+    "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreVMClient.dylib",
+    "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSCore.framework/Versions/A/MPSCore",
+    "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSImage.framework/Versions/A/MPSImage",
+    "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSNeuralNetwork.framework/Versions/A/MPSNeuralNetwork",
+    "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSMatrix.framework/Versions/A/MPSMatrix",
+    "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSRayIntersector.framework/Versions/A/MPSRayIntersector",
+    "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSNDArray.framework/Versions/A/MPSNDArray",
+    "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSFunctions.framework/Versions/A/MPSFunctions",
+    "/System/Library/PrivateFrameworks/MetalTools.framework/Versions/A/MetalTools",
+    "/System/Library/PrivateFrameworks/AggregateDictionary.framework/Versions/A/AggregateDictionary",
+    "/usr/lib/libIOReport.dylib",
+    "/System/Library/Frameworks/CoreImage.framework/Versions/A/CoreImage",
+    "/System/Library/PrivateFrameworks/PhotosensitivityProcessing.framework/Versions/A/PhotosensitivityProcessing",
+    "/System/Library/Frameworks/OpenCL.framework/Versions/A/OpenCL",
+    "/System/Library/PrivateFrameworks/GraphVisualizer.framework/Versions/A/GraphVisualizer",
+    "/System/Library/PrivateFrameworks/FontServices.framework/Versions/A/FontServices",
+    "/System/Library/Frameworks/UniformTypeIdentifiers.framework/Versions/A/UniformTypeIdentifiers",
+    "/System/Library/PrivateFrameworks/OTSVG.framework/Versions/A/OTSVG",
+    "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/Resources/libFontRegistry.dylib",
+    "/System/Library/PrivateFrameworks/FontServices.framework/libhvf.dylib",
+    "/System/Library/PrivateFrameworks/FontServices.framework/libXTFontStaticRegistryData.dylib",
+    "/System/Library/PrivateFrameworks/VideoToolboxParavirtualizationSupport.framework/Versions/A/VideoToolboxParavirtualizationSupport",
+    "/System/Library/PrivateFrameworks/AppleVA.framework/Versions/A/AppleVA",
+    "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATSUI.framework/Versions/A/ATSUI",
+    "/usr/lib/libcups.2.dylib",
+    "/System/Library/Frameworks/Kerberos.framework/Versions/A/Kerberos",
+    "/System/Library/Frameworks/GSS.framework/Versions/A/GSS",
+    "/usr/lib/libresolv.9.dylib",
+    "/System/Library/PrivateFrameworks/Heimdal.framework/Versions/A/Heimdal",
+    "/System/Library/Frameworks/Kerberos.framework/Versions/A/Libraries/libHeimdalProxy.dylib",
+    "/System/Library/PrivateFrameworks/CommonAuth.framework/Versions/A/CommonAuth",
+    "/System/Library/Frameworks/AVFAudio.framework/Versions/A/AVFAudio",
+    "/System/Library/PrivateFrameworks/AXCoreUtilities.framework/Versions/A/AXCoreUtilities",
+    "/System/Library/Frameworks/AudioToolbox.framework/Versions/A/AudioToolbox",
+    "/System/Library/PrivateFrameworks/AudioSession.framework/Versions/A/AudioSession",
+    "/System/Library/Frameworks/IOBluetooth.framework/Versions/A/IOBluetooth",
+    "/System/Library/PrivateFrameworks/MediaExperience.framework/Versions/A/MediaExperience",
+    "/System/Library/PrivateFrameworks/AudioSession.framework/libSessionUtility.dylib",
+    "/System/Library/PrivateFrameworks/AudioResourceArbitration.framework/Versions/A/AudioResourceArbitration",
+    "/System/Library/PrivateFrameworks/PowerLog.framework/Versions/A/PowerLog",
+    "/System/Library/Frameworks/CoreData.framework/Versions/A/CoreData",
+    "/System/Library/Frameworks/CoreBluetooth.framework/Versions/A/CoreBluetooth",
+    "/System/Library/Frameworks/AudioUnit.framework/Versions/A/AudioUnit",
+    "/System/Library/PrivateFrameworks/CoreUtils.framework/Versions/A/CoreUtils",
+    "/System/Library/PrivateFrameworks/CoreUtilsExtras.framework/Versions/A/CoreUtilsExtras",
+    "/System/Library/PrivateFrameworks/IO80211.framework/Versions/A/IO80211",
+    "/System/Library/PrivateFrameworks/MobileKeyBag.framework/Versions/A/MobileKeyBag",
+    "/System/Library/PrivateFrameworks/MallocStackLogging.framework/Versions/A/MallocStackLogging"
+  ]
+}
\ No newline at end of file
diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts
index 18222b32a..6b4106f56 100644
--- a/src/client/apis/gpt/GPT.ts
+++ b/src/client/apis/gpt/GPT.ts
@@ -14,7 +14,7 @@ type GPTCallOpts = {
 };
 
 const callTypeMap: { [type: string]: GPTCallOpts } = {
-    summary: { model: 'text-davinci-003', maxTokens: 256, temp: 0.5, prompt: 'Summarize this text briefly: ' },
+    summary: { model: 'text-davinci-003', maxTokens: 256, temp: 0.5, prompt: 'Summarize this text in simpler terms: ' },
     edit: { model: 'text-davinci-003', maxTokens: 256, temp: 0.5, prompt: 'Reword this: ' },
     completion: { model: 'text-davinci-003', maxTokens: 256, temp: 0.5, prompt: '' },
 };
@@ -47,7 +47,7 @@ const gptAPICall = async (inputText: string, callType: GPTCallType) => {
     }
 };
 
-const gptImageCall = async (prompt: string) => {
+const gptImageCall = async (prompt: string, n?: number) => {
     try {
         const configuration = new Configuration({
             apiKey: process.env.OPENAI_KEY,
@@ -55,33 +55,15 @@ const gptImageCall = async (prompt: string) => {
         const openai = new OpenAIApi(configuration);
         const response = await openai.createImage({
             prompt: prompt,
-            n: 1,
+            n: n ?? 1,
             size: '1024x1024',
         });
-        return response.data.data[0].url;
+        return response.data.data.map(data => data.url);
+        // return response.data.data[0].url;
     } catch (err) {
         console.error(err);
         return;
     }
 };
 
-// const gptEditCall = async (selectedText: string, fullText: string) => {
-//     try {
-//         const configuration = new Configuration({
-//             apiKey: process.env.OPENAI_KEY,
-//         });
-//         const openai = new OpenAIApi(configuration);
-//         const response = await openai.createCompletion({
-//             model: 'text-davinci-003',
-//             max_tokens: 256,
-//             temperature: 0.1,
-//             prompt: `Replace the phrase ${selectedText} inside of ${fullText}.`,
-//         });
-//         return response.data.choices[0].text.trim();
-//     } catch (err) {
-//         console.log(err);
-//         return 'Error connecting with API.';
-//     }
-// };
-
 export { gptAPICall, gptImageCall, GPTCallType };
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 85101fcab..ac948c7c7 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -195,7 +195,7 @@ export namespace DragManager {
     }
 
     // drag a document and drop it (or make an embed/copy on drop)
-    export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions, dropEvent?: () => any) {
+    export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions, onDropCompleted?: (e?: DragCompleteEvent) => any) {
         const addAudioTag = (dropDoc: any) => {
             dropDoc && !dropDoc.author_date && (dropDoc.author_date = new DateField());
             dropDoc instanceof Doc && DocUtils.MakeLinkToActiveAudio(() => dropDoc);
@@ -203,7 +203,7 @@ export namespace DragManager {
         };
         const finishDrag = async (e: DragCompleteEvent) => {
             const docDragData = e.docDragData;
-            dropEvent?.(); // glr: optional additional function to be called - in this case with presentation trails
+            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 = (
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 67b7b39dd..1ff5688f9 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -137,7 +137,11 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     setupMoveUpEvents(
                         this,
                         e,
-                        e => !e.defaultPrevented && DragManager.StartDocumentDrag([iconWrap], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY),
+                        e =>
+                            !e.defaultPrevented &&
+                            DragManager.StartDocumentDrag([iconWrap], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY, undefined, () => {
+                                CollectionDockingView.CloseSplit(doc);
+                            }),
                         returnFalse,
                         action(e => {
                             if (this.view) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index db000d5de..747ab9249 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -238,11 +238,11 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
             textX,
             textY,
             // fully connected
-            // pt1,
-            // pt2,
+            pt1,
+            pt2,
             // this code adds space between links
-            pt1: [pt1[0] + pt1normalized[0] * 13, pt1[1] + pt1normalized[1] * 13],
-            pt2: [pt2[0] + pt2normalized[0] * 13, pt2[1] + pt2normalized[1] * 13],
+            // pt1: [pt1[0] + pt1normalized[0] * 13, pt1[1] + pt1normalized[1] * 13],
+            // pt2: [pt2[0] + pt2normalized[0] * 13, pt2[1] + pt2normalized[1] * 13],
         };
     }
 
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index b906065a0..2ea8a7846 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -1,26 +1,28 @@
-import { Colors } from "browndash-components";
-import { runInAction, action } from "mobx";
-import { aggregateBounds } from "../../../Utils";
-import { Doc } from "../../../fields/Doc";
-import { Width, Height } from "../../../fields/DocSymbols";
-import { InkTool } from "../../../fields/InkField";
-import { Cast, StrCast, NumCast, BoolCast } from "../../../fields/Types";
-import { WebField } from "../../../fields/URLField";
-import { GestureUtils } from "../../../pen-gestures/GestureUtils";
-import { LinkManager } from "../../util/LinkManager";
-import { ScriptingGlobals } from "../../util/ScriptingGlobals";
-import { SelectionManager } from "../../util/SelectionManager";
-import { UndoManager } from "../../util/UndoManager";
-import { GestureOverlay } from "../GestureOverlay";
-import { InkTranscription } from "../InkTranscription";
-import { ActiveFillColor, SetActiveFillColor, ActiveIsInkMask, SetActiveIsInkMask, ActiveInkWidth, SetActiveInkWidth, ActiveInkColor, SetActiveInkColor } from "../InkingStroke";
-import { CollectionFreeFormView } from "../collections/collectionFreeForm";
-import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
-import { WebBox } from "../nodes/WebBox";
-import { RichTextMenu } from "../nodes/formattedText/RichTextMenu";
-import { DocumentType } from "../../documents/DocumentTypes";
+import { Colors } from 'browndash-components';
+import { runInAction, action } from 'mobx';
+import { aggregateBounds } from '../../../Utils';
+import { Doc } from '../../../fields/Doc';
+import { Width, Height } from '../../../fields/DocSymbols';
+import { InkTool } from '../../../fields/InkField';
+import { Cast, StrCast, NumCast, BoolCast } from '../../../fields/Types';
+import { WebField } from '../../../fields/URLField';
+import { GestureUtils } from '../../../pen-gestures/GestureUtils';
+import { LinkManager } from '../../util/LinkManager';
+import { ScriptingGlobals } from '../../util/ScriptingGlobals';
+import { SelectionManager } from '../../util/SelectionManager';
+import { UndoManager } from '../../util/UndoManager';
+import { GestureOverlay } from '../GestureOverlay';
+import { InkTranscription } from '../InkTranscription';
+import { ActiveFillColor, SetActiveFillColor, ActiveIsInkMask, SetActiveIsInkMask, ActiveInkWidth, SetActiveInkWidth, ActiveInkColor, SetActiveInkColor } from '../InkingStroke';
+import { CollectionFreeFormView } from '../collections/collectionFreeForm';
+import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView';
+import { WebBox } from '../nodes/WebBox';
+import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
+import { DocumentType } from '../../documents/DocumentTypes';
 
-ScriptingGlobals.add(function IsNoneSelected() { return SelectionManager.Views().length <= 0; }, "are no document selected");
+ScriptingGlobals.add(function IsNoneSelected() {
+    return SelectionManager.Views().length <= 0;
+}, 'are no document selected');
 
 // toggle: Set overlay status of selected document
 ScriptingGlobals.add(function setView(view: string) {
@@ -52,7 +54,7 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b
             if (contentFrameNumber !== undefined) {
                 CollectionFreeFormDocumentView.setStringValues(contentFrameNumber, dv.rootDoc, { fieldKey: color });
             } else {
-                console.log('setting color to: ', color)
+                console.log('setting color to: ', color);
                 dv.rootDoc['_' + fieldKey] = color;
             }
         });
@@ -119,7 +121,7 @@ ScriptingGlobals.add(function showFreeform(attr: 'flashcards' | 'grid' | 'snapli
    ]);
 
     if (checkResult) {
-        console.log(attr, map.get(attr)?.checkResult(selected))
+        console.log(attr, map.get(attr)?.checkResult(selected));
         return map.get(attr)?.checkResult(selected);
     }
     const batch = map.get(attr)?.waitForRender ? UndoManager.StartBatch('set freeform attribute') : { end: () => {} };
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 34a1229ba..5cdd6b5f2 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -354,6 +354,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 AnchorMenu.Instance.jumpTo(e.clientX * scale + mainContBounds.translateX, e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._layout_scrollTop) * scale);
                 // Changing which document to add the annotation to (the currently selected WebBox)
                 GPTPopup.Instance.setSidebarId(`${this.props.fieldKey}_${this._urlHash}_sidebar`);
+                const anchor = this._getAnchor(undefined, false);
+                if (anchor) {
+                    console.log(anchor);
+                    GPTPopup.Instance.setTargetAnchor(anchor);
+                }
                 GPTPopup.Instance.addDoc = this.sidebarAddDocument;
             }
         }
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 212d24165..9f4483e8d 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -70,6 +70,7 @@ import { schema } from './schema_rts';
 import { SummaryView } from './SummaryView';
 import applyDevTools = require('prosemirror-dev-tools');
 import React = require('react');
+import { GPTPopup, GPTPopupMode } from '../../pdf/GPTPopup/GPTPopup';
 const translateGoogleApi = require('translate-google-api');
 export const GoogleRef = 'googleDocId';
 type PullHandler = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => void;
@@ -896,12 +897,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         this._downX = this._downY = Number.NaN;
     };
 
-    animateRes = (resIndex: number) => {
-        console.log(this.dataDoc.text);
-        if (resIndex < this.gptRes.length) {
-            this.dataDoc.text = (this.dataDoc.text as RichTextField)?.Text + this.gptRes[resIndex];
+    animateRes = (resIndex: number, newText: string) => {
+        if (resIndex < newText.length) {
+            const marks = this._editorView?.state.storedMarks ?? [];
+            // if (!marks) return;
+            this._editorView?.dispatch(this._editorView.state.tr.setStoredMarks(marks).insertText(newText[resIndex]).setStoredMarks(marks));
             setTimeout(() => {
-                this.animateRes(resIndex + 1);
+                this.animateRes(resIndex + 1, newText);
             }, 20);
         }
     };
@@ -912,47 +914,68 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     // the._editorView.dispatch(state.tr.setSelection(updated).insertText('\n, to))
 
     askGPT = action(async () => {
+        // const state = this._editorView?.state;
+        // if (!state) return;
+        // const to = state.selection.to;
+        // const updated = TextSelection.create(state.doc, to, to);
+        // this._editorView?.dispatch(state.tr.setSelection(updated).insertText('\n', to));
+        // this._editorView?.dispatch(this._editorView.state.tr.setStoredMarks(marks).insertText('\nTesting').setStoredMarks(marks));
+        // console.log('After ', this._editorView?.state.storedMarks);
         try {
             let res = await gptAPICall((this.dataDoc.text as RichTextField)?.Text, GPTCallType.COMPLETION);
-            if (res) {
-                this.gptRes = res;
-                this.animateRes(0);
+            if (!res) {
+                console.error('GPT call failed');
+                this.animateRes(0, 'Something went wrong.');
+            } else {
+                this.animateRes(0, res);
             }
         } catch (err) {
-            console.log(err);
-            this.dataDoc.text = (this.dataDoc.text as RichTextField)?.Text + 'Something went wrong';
+            console.error('GPT call failed');
+            this.animateRes(0, 'Something went wrong.');
         }
     });
 
     generateImage = async () => {
         console.log('Generate image from text: ', (this.dataDoc.text as RichTextField)?.Text);
+        GPTPopup.Instance?.setImgTargetDoc(this.rootDoc);
+        GPTPopup.Instance.setImgUrls([]);
+        GPTPopup.Instance.setMode(GPTPopupMode.IMAGE);
+        GPTPopup.Instance.setVisible(true);
+        GPTPopup.Instance.addToCollection = this.props.addDocument;
+        GPTPopup.Instance.setLoading(true);
+
         try {
-            let image_url = await gptImageCall((this.dataDoc.text as RichTextField)?.Text);
-            if (image_url) {
-                const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [image_url] });
+            // make this support multiple images
+            let image_urls = await gptImageCall((this.dataDoc.text as RichTextField)?.Text);
+            console.log(image_urls);
+            if (image_urls) {
+                const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [image_urls[0]] });
                 const source = Utils.prepend(result.accessPaths.agnostic.client);
-                const newDoc = Docs.Create.ImageDocument(source, {
-                    x: NumCast(this.rootDoc.x) + NumCast(this.layoutDoc._width) + 10,
-                    y: NumCast(this.rootDoc.y),
-                    _height: 200,
-                    _width: 200,
-                    data_nativeWidth: result.nativeWidth,
-                    data_nativeHeight: result.nativeHeight,
-                });
-                if (Doc.IsInMyOverlay(this.rootDoc)) {
-                    newDoc.overlayX = this.rootDoc.x;
-                    newDoc.overlayY = NumCast(this.rootDoc.y) + NumCast(this.rootDoc._height);
-                    Doc.AddToMyOverlay(newDoc);
-                } else {
-                    this.props.addDocument?.(newDoc);
-                }
-                // Create link between prompt and image
-                DocUtils.MakeLink(this.rootDoc, newDoc, { link_relationship: 'Image Prompt' });
+                GPTPopup.Instance.setImgUrls([source]);
+
+                // const newDoc = Docs.Create.ImageDocument(source, {
+                //     x: NumCast(this.rootDoc.x) + NumCast(this.layoutDoc._width) + 10,
+                //     y: NumCast(this.rootDoc.y),
+                //     _height: 200,
+                //     _width: 200,
+                //     data_nativeWidth: result.nativeWidth,
+                //     data_nativeHeight: result.nativeHeight,
+                // });
+                // if (Doc.IsInMyOverlay(this.rootDoc)) {
+                //     newDoc.overlayX = this.rootDoc.x;
+                //     newDoc.overlayY = NumCast(this.rootDoc.y) + NumCast(this.rootDoc._height);
+                //     Doc.AddToMyOverlay(newDoc);
+                // } else {
+                //     this.props.addDocument?.(newDoc);
+                // }
+                // // Create link between prompt and image
+                // DocUtils.MakeLink(this.rootDoc, newDoc, { link_relationship: 'Image Prompt' });
             }
         } catch (err) {
             console.log(err);
             return '';
         }
+        GPTPopup.Instance.setLoading(false);
     };
 
     breakupDictation = () => {
@@ -1249,8 +1272,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 }
                 // Accessing editor and text doc for gpt assisted text edits
                 if (this._editorView && selected) {
-                    AnchorMenu.Instance?.setEditorView(this._editorView);
-                    AnchorMenu.Instance?.setTextDoc(this.dataDoc);
+                    console.log('Setting');
+                    GPTPopup.Instance?.setTextAnchor(this.getAnchor(false));
+                    // AnchorMenu.Instance?.setEditorView(this._editorView);
+                    // AnchorMenu.Instance?.setTextDoc(this.dataDoc);
                 }
             }),
             { fireImmediately: true }
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index e6b9cb382..dd8cf7002 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -131,7 +131,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
     gptSummarize = async (e: React.PointerEvent) => {
         GPTPopup.Instance.setVisible(true);
         this.setHighlightRange(undefined);
-        this.setGPTMode(GPTPopupMode.SUMMARY);
+        GPTPopup.Instance.setMode(GPTPopupMode.SUMMARY);
         GPTPopup.Instance.setLoading(true);
 
         try {
@@ -183,6 +183,10 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
         GPTPopup.Instance.setLoading(false);
     };
 
+    gptImage = async () => {
+        console.log(this.GetAnchor(undefined, false));
+    };
+
     /**
      * Replaces text suggestions from GPT.
      */
@@ -293,21 +297,12 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
                             </button>
                         </Tooltip>
                     )}
-                    {/* <GPTPopup
-                        key="gptpopup"
-                        visible={this.showGPTPopup}
-                        text={this.GPTpopupText}
-                        highlightRange={this.highlightRange}
-                        loading={this.loadingGPT}
-                        callSummaryApi={this.gptSummarize}
-                        callEditApi={this.gptEdit}
-                        replaceText={this.replaceText}
-                        mode={this.GPTMode}
-                    /> */}
                     {AnchorMenu.Instance.OnAudio === unimplementedFunction ? null : (
                         <IconButton tooltip={'Click to Record Annotation'} onPointerDown={this.audioDown} icon={<FontAwesomeIcon icon="microphone" />} color={StrCast(Doc.UserDoc().userColor)} />
                     )}
-                    {this.canEdit() && <IconButton tooltip={'AI edit suggestions'} onPointerDown={this.gptEdit} icon={<FontAwesomeIcon icon="pencil-alt" />} color={StrCast(Doc.UserDoc().userColor)} />}
+                    {/* Removed text editing for now, not quite ready */}
+                    {/* {this.canEdit() && <IconButton tooltip={'AI edit suggestions'} onPointerDown={this.gptEdit} icon={<FontAwesomeIcon icon="pencil-alt" />} color={StrCast(Doc.UserDoc().userColor)} />} */}
+                    {<IconButton tooltip={'Generate DALL-E Image'} onPointerDown={this.gptImage} icon={<FontAwesomeIcon icon="image" />} color={StrCast(Doc.UserDoc().userColor)} />}
                     <Popup tooltip="Find document to link to selected text" type={Type.PRIM} icon={<FontAwesomeIcon icon={'search'} />} popup={<LinkPopup key="popup" linkCreateAnchor={this.onMakeAnchor} />} color={StrCast(Doc.UserDoc().userColor)} />
                     {AnchorMenu.Instance.StartCropDrag === unimplementedFunction ? null : (
                         <IconButton tooltip={'Click/Drag to create cropped image'} onPointerDown={this.cropDown} icon={<FontAwesomeIcon icon="image" />} color={StrCast(Doc.UserDoc().userColor)} />
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.scss b/src/client/views/pdf/GPTPopup/GPTPopup.scss
index 2f0ff83e2..478b7d4ba 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.scss
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.scss
@@ -24,6 +24,7 @@ $highlightedText: #82e0ff;
 
     .summary-heading {
         display: flex;
+        justify-content: space-between;
         align-items: center;
         border-bottom: 1px solid $greyborder;
         padding-bottom: 5px;
@@ -110,6 +111,57 @@ $highlightedText: #82e0ff;
     }
 }
 
+.image-content-wrapper {
+    display: flex;
+    flex-direction: column;
+    align-items: flex-start;
+    gap: 8px;
+    padding-bottom: 16px;
+
+    .img-wrapper {
+        position: relative;
+        cursor: pointer;
+
+        .img-container {
+            position: relative;
+
+            img {
+                position: relative;
+            }
+        }
+
+        .img-container::after {
+            content: '';
+            position: absolute;
+            top: 0;
+            left: 0;
+            width: 100%;
+            height: 100%;
+            background-color: rgba(0, 0, 0, 0.5);
+            opacity: 0;
+            transition: opacity 0.3s ease;
+        }
+
+        .btn-container {
+            position: absolute;
+            right: 8px;
+            bottom: 8px;
+            opacity: 0;
+            transition: opacity 0.3s ease;
+        }
+
+        &:hover {
+            .img-container::after {
+                opacity: 1;
+            }
+
+            .btn-container {
+                opacity: 1;
+            }
+        }
+    }
+}
+
 // Typist CSS
 .Typist .Cursor {
     display: inline-block;
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index 9f28cb5d1..aeee90d16 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -1,4 +1,5 @@
 import React = require('react');
+import './GPTPopup.scss';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, observable } from 'mobx';
 import { observer } from 'mobx-react';
@@ -6,13 +7,14 @@ import ReactLoading from 'react-loading';
 import Typist from 'react-typist';
 import { Doc } from '../../../../fields/Doc';
 import { DocUtils, Docs } from '../../../documents/Documents';
-import './GPTPopup.scss';
-import { Button, IconButton } from 'browndash-components';
-import { StrCast } from '../../../../fields/Types';
+import { Button, IconButton, Type } from 'browndash-components';
+import { NumCast, StrCast } from '../../../../fields/Types';
+import { CgClose } from 'react-icons/cg';
 
 export enum GPTPopupMode {
     SUMMARY,
     EDIT,
+    IMAGE,
 }
 
 interface GPTPopupProps {}
@@ -39,6 +41,14 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
     public setText = (text: string) => {
         this.text = text;
     };
+
+    @observable
+    public imageUrls: string[] = [];
+    @action
+    public setImgUrls = (imgs: string[]) => {
+        this.imageUrls = imgs;
+    };
+
     @observable
     public mode: GPTPopupMode = GPTPopupMode.SUMMARY;
     @action
@@ -58,6 +68,8 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
     public setDone = (done: boolean) => {
         this.done = done;
     };
+
+    // change what can be a ref into a ref
     @observable
     private sidebarId: string = '';
     @action
@@ -65,19 +77,30 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
         this.sidebarId = id;
     };
 
-    // reference
-    // if (focusNode) {
-    //     const anchor = srcWeb?.ComponentView?.getAnchor?.(true);
-    //     anchor && DocUtils.MakeLink(htmlDoc, anchor, {});
-    // }
+    // pdfs and webpages
+    @observable
+    private targetAnchor: Doc | undefined;
+    @action
+    public setTargetAnchor = (anchor: Doc) => {
+        this.targetAnchor = anchor;
+    };
+
     @observable
-    private pdfAnchor: Doc | undefined;
+    private imgTargetDoc: Doc | undefined;
     @action
-    public setPdfAnchor = (anchor: Doc) => {
-        this.pdfAnchor = anchor;
+    public setImgTargetDoc = (anchor: Doc) => {
+        this.imgTargetDoc = anchor;
+    };
+
+    @observable
+    private textAnchor: Doc | undefined;
+    @action
+    public setTextAnchor = (anchor: Doc) => {
+        this.textAnchor = anchor;
     };
 
     public addDoc: (doc: Doc | Doc[], sidebarKey?: string | undefined) => boolean = () => false;
+    public addToCollection: ((doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) | undefined;
 
     /**
      * Transfers the summarization text to a sidebar annotation text document.
@@ -90,13 +113,42 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
             _layout_autoHeight: true,
         });
         this.addDoc(newDoc, this.sidebarId);
-        if (this.pdfAnchor) {
-            DocUtils.MakeLink(newDoc, this.pdfAnchor, {
+        if (this.targetAnchor) {
+            DocUtils.MakeLink(newDoc, this.targetAnchor, {
                 link_relationship: 'GPT Summary',
             });
         }
     };
 
+    /**
+     * Transfers the image urls to actual image docs
+     */
+    private transferToImage = (source: string) => {
+        console.log('Text Anchor', this.textAnchor);
+        console.log('Whole doc anchor', this.imgTargetDoc);
+        const textAnchor = this.textAnchor ?? this.imgTargetDoc;
+        if (!textAnchor) return;
+        const newDoc = Docs.Create.ImageDocument(source, {
+            x: NumCast(textAnchor.x) + NumCast(textAnchor._width) + 10,
+            y: NumCast(textAnchor.y),
+            _height: 200,
+            _width: 200,
+            data_nativeWidth: 1024,
+            data_nativeHeight: 1024,
+        });
+        if (Doc.IsInMyOverlay(textAnchor)) {
+            newDoc.overlayX = textAnchor.x;
+            newDoc.overlayY = NumCast(textAnchor.y) + NumCast(textAnchor._height);
+            Doc.AddToMyOverlay(newDoc);
+        } else {
+            this.addToCollection?.(newDoc);
+        }
+        // Create link between prompt and image
+        DocUtils.MakeLink(textAnchor, newDoc, { link_relationship: 'Image Prompt' });
+    };
+
+    private getPreviewUrl = (source: string) => source.split('.').join('_m.');
+
     constructor(props: GPTPopupProps) {
         super(props);
         GPTPopup.Instance = this;
@@ -108,6 +160,26 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
         }
     };
 
+    imageBox = () => {
+        return (
+            <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
+                {this.heading('GENERATED IMAGE')}
+                <div className="image-content-wrapper">
+                    {this.imageUrls.map(rawSrc => (
+                        <div className="img-wrapper">
+                            <div className="img-container">
+                                <img key={rawSrc} src={this.getPreviewUrl(rawSrc)} width={150} height={150} alt="dalle generation" />
+                            </div>
+                            <div className="btn-container">
+                                <Button text="Save Image" onClick={() => this.transferToImage(rawSrc)} color={StrCast(Doc.UserDoc().userColor)} type={Type.TERT} />
+                            </div>
+                        </div>
+                    ))}
+                </div>
+            </div>
+        );
+    };
+
     summaryBox = () => (
         <>
             <div>
@@ -135,30 +207,21 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
                 <div className="btns-wrapper">
                     {this.done ? (
                         <>
-                            {/* <button className="icon-btn" onClick={this.callSummaryApi}>
-                                <FontAwesomeIcon icon="redo-alt" size="lg" />
-                            </button>
-                            <button
-                                className="text-btn"
-                                onClick={e => {
-                                    this.transferToText();
-                                }}>
-                                Transfer to Text
-                            </button> */}
-                            <IconButton onClick={this.callSummaryApi} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(Doc.UserDoc().userVariantColor)} />
-                            <Button text="Transfer To Text" onClick={this.transferToText} color={StrCast(Doc.UserDoc().userVariantColor)} />
+                            <IconButton tooltip="Generate Again" onClick={this.callSummaryApi} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(Doc.UserDoc().userVariantColor)} />
+                            <Button tooltip="Transfer to text" text="Transfer To Text" onClick={this.transferToText} color={StrCast(Doc.UserDoc().userVariantColor)} type={Type.TERT} />
                         </>
                     ) : (
                         <div className="summarizing">
                             <span>Summarizing</span>
                             <ReactLoading type="bubbles" color="#bcbcbc" width={20} height={20} />
-                            <button
-                                className="btn-secondary"
-                                onClick={e => {
+                            <Button
+                                text="Stop Animation"
+                                onClick={() => {
                                     this.setDone(true);
-                                }}>
-                                Stop Animation
-                            </button>
+                                }}
+                                color={StrCast(Doc.UserDoc().userVariantColor)}
+                                type={Type.TERT}
+                            />
                         </div>
                     )}
                 </div>
@@ -216,14 +279,14 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
     heading = (headingText: string) => (
         <div className="summary-heading">
             <label className="summary-text">{headingText}</label>
-            {this.loading && <ReactLoading type="spin" color="#bcbcbc" width={14} height={14} />}
+            {this.loading ? <ReactLoading type="spin" color="#bcbcbc" width={14} height={14} /> : <IconButton color={StrCast(Doc.UserDoc().userColor)} tooltip="close" icon={<CgClose size="16px" />} onClick={() => this.setVisible(false)} />}
         </div>
     );
 
     render() {
         return (
             <div className="summary-box" style={{ display: this.visible ? 'flex' : 'none' }}>
-                {this.mode === GPTPopupMode.SUMMARY ? this.summaryBox() : this.editBox()}
+                {this.mode === GPTPopupMode.SUMMARY ? this.summaryBox() : this.mode === GPTPopupMode.IMAGE ? this.imageBox() : this.editBox()}
             </div>
         );
     }
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 4fc31ffe3..1d1c34f4f 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -425,8 +425,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         GPTPopup.Instance.setSidebarId('data_sidebar');
         const anchor = this._getAnchor(undefined, false);
         if (anchor) {
-            console.log(anchor);
-            GPTPopup.Instance.setPdfAnchor(anchor);
+            GPTPopup.Instance.setTargetAnchor(anchor);
         }
         GPTPopup.Instance.addDoc = this.props.sidebarAddDoc;
     };
-- 
cgit v1.2.3-70-g09d2


From 314f62bb5335e3fb3f25501823d41cf0cdc53cac Mon Sep 17 00:00:00 2001
From: Sophie Zhang <sophie_zhang@brown.edu>
Date: Sun, 13 Aug 2023 17:02:58 -0400
Subject: update

---
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  18 +++-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  15 +--
 .../views/nodes/generativeFill/GenerativeFill.scss |   1 +
 .../views/nodes/generativeFill/GenerativeFill.tsx  | 103 +++++++++---------
 .../generativeFillUtils/BrushHandler.ts            | 116 +++++++--------------
 .../generativeFillUtils/ImageHandler.ts            |   2 +-
 .../generativeFillUtils/generativeFillConstants.ts |   4 +-
 src/client/views/pdf/GPTPopup/GPTPopup.tsx         |   4 +-
 8 files changed, 118 insertions(+), 145 deletions(-)

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

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index ba31916a7..34f707b30 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -237,7 +237,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     onChildClickHandler = () => this.props.childClickScript || ScriptCast(this.Document.onChildClick);
     onChildDoubleClickHandler = () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick);
     elementFunc = () => this._layoutElements;
-    shrinkWrap = () => {
+    fitContentOnce = () => {
         if (this.props.DocumentView?.().nativeWidth) return;
         const vals = this.fitToContentVals;
         this.layoutDoc._freeform_panX = vals.bounds.cx;
@@ -1592,6 +1592,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                     elements => (this._layoutElements = elements || []),
                     { fireImmediately: true, name: 'doLayout' }
                 );
+
+                this._disposers.fitContent = reaction(
+                    () => this.rootDoc.fitContentOnce,
+                    fitContentOnce => {
+                        if (fitContentOnce) this.fitContentOnce();
+                    },
+                    { fireImmediately: true, name: 'fitContent' }
+                );
             })
         );
     }
@@ -1782,6 +1790,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             !Doc.noviceMode &&
             optionItems.push({ description: (this._showAnimTimeline ? 'Close' : 'Open') + ' Animation Timeline', event: action(() => (this._showAnimTimeline = !this._showAnimTimeline)), icon: 'eye' });
         this.props.renderDepth && optionItems.push({ description: 'Use Background Color as Default', event: () => (Cast(Doc.UserDoc().emptyCollection, Doc, null)._backgroundColor = StrCast(this.layoutDoc._backgroundColor)), icon: 'palette' });
+        this.props.renderDepth &&
+            optionItems.push({
+                description: 'Fit Content Once',
+                event: () => {
+                    this.fitContentOnce();
+                },
+                icon: 'object-group',
+            });
         if (!Doc.noviceMode) {
             optionItems.push({ description: (!Doc.NativeWidth(this.layoutDoc) || !Doc.NativeHeight(this.layoutDoc) ? 'Freeze' : 'Unfreeze') + ' Aspect', event: this.toggleNativeDimensions, icon: 'snowflake' });
         }
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 9f4483e8d..072977150 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -937,6 +937,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
 
     generateImage = async () => {
         console.log('Generate image from text: ', (this.dataDoc.text as RichTextField)?.Text);
+        GPTPopup.Instance?.setTextAnchor(this.getAnchor(false));
         GPTPopup.Instance?.setImgTargetDoc(this.rootDoc);
         GPTPopup.Instance.setImgUrls([]);
         GPTPopup.Instance.setMode(GPTPopupMode.IMAGE);
@@ -1270,13 +1271,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props);
                     setTimeout(this.autoLink, 20);
                 }
-                // Accessing editor and text doc for gpt assisted text edits
-                if (this._editorView && selected) {
-                    console.log('Setting');
-                    GPTPopup.Instance?.setTextAnchor(this.getAnchor(false));
-                    // AnchorMenu.Instance?.setEditorView(this._editorView);
-                    // AnchorMenu.Instance?.setTextDoc(this.dataDoc);
-                }
+                // // Accessing editor and text doc for gpt assisted text edits
+                // if (this._editorView && selected) {
+                //     console.log('Setting');
+                //     GPTPopup.Instance?.setTextAnchor(this.getAnchor(false));
+                //     // AnchorMenu.Instance?.setEditorView(this._editorView);
+                //     // AnchorMenu.Instance?.setTextDoc(this.dataDoc);
+                // }
             }),
             { fireImmediately: true }
         );
diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.scss b/src/client/views/nodes/generativeFill/GenerativeFill.scss
index b1e570cf1..c2669a950 100644
--- a/src/client/views/nodes/generativeFill/GenerativeFill.scss
+++ b/src/client/views/nodes/generativeFill/GenerativeFill.scss
@@ -16,6 +16,7 @@ $scale: 0.5;
     .generativeFillControls {
         flex-shrink: 0;
         height: $navHeight;
+        color: #000000;
         background-color: #ffffff;
         z-index: 999;
         width: 100%;
diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.tsx b/src/client/views/nodes/generativeFill/GenerativeFill.tsx
index 2b3cfb920..f8f9fe077 100644
--- a/src/client/views/nodes/generativeFill/GenerativeFill.tsx
+++ b/src/client/views/nodes/generativeFill/GenerativeFill.tsx
@@ -3,21 +3,19 @@ import React = require('react');
 import { useEffect, useRef, useState } from 'react';
 import { APISuccess, ImageUtility } from './generativeFillUtils/ImageHandler';
 import { BrushHandler } from './generativeFillUtils/BrushHandler';
-import { IconButton, TextField } from '@mui/material';
+import { Box, IconButton, Slider, TextField } from '@mui/material';
 import { CursorData, Point } from './generativeFillUtils/generativeFillInterfaces';
 import { activeColor, canvasSize, eraserColor, freeformRenderSize, newCollectionSize, offsetDistanceY, offsetX } from './generativeFillUtils/generativeFillConstants';
 import { PointerHandler } from './generativeFillUtils/PointerHandler';
-import { BsBrush, BsEraser, BsX } from 'react-icons/bs';
-import { AiOutlineUpload } from 'react-icons/ai';
+import { BsEraser, BsX } from 'react-icons/bs';
 import { CiUndo, CiRedo } from 'react-icons/ci';
 import Buttons from './GenerativeFillButtons';
-import { EditableText } from 'browndash-components';
 import { MainView } from '../../MainView';
 import { Doc } from '../../../../fields/Doc';
 import { Networking } from '../../../Network';
 import { Utils } from '../../../../Utils';
 import { DocUtils, Docs } from '../../../documents/Documents';
-import { DocCast, NumCast } from '../../../../fields/Types';
+import { Cast, DocCast, NumCast } from '../../../../fields/Types';
 import { CollectionDockingView } from '../../collections/CollectionDockingView';
 import { OpenWhere, OpenWhereMod } from '../DocumentView';
 import { Oval } from 'react-loader-spinner';
@@ -25,12 +23,11 @@ import { Oval } from 'react-loader-spinner';
 /**
  * For images not 1024x1024 fill in the rest in solid black, or a
  * reflected version of the image.
+ *
+ * add a branch from image directly checkbox
  */
 
 /**
- * TODO: Look into img onload, sometimes the canvas doesn't update properly
- *
- * Ref:
  *
  *
  * CollectionDockingView.AddSplit(Doc.MakeCopy(DocCast(Doc.UserDoc().emptyPane)), OpenWhereMod.right);
@@ -88,12 +85,13 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
     const freeformPosition = useRef<number[]>([0, 0]);
 
     // which urls were already saved to canvas
-    const savedSrcs = useRef<string[]>([]);
+    const savedSrcs = useRef<Set<string>>(new Set());
 
     // references to keep track of tree structure
     const newCollectionRef = useRef<Doc | null>(null);
     const parentDoc = useRef<Doc | null>(null);
     const childrenDocs = useRef<Doc[]>([]);
+    const addToExistingCollection = useRef<boolean>(false);
 
     // Undo and Redo
     const handleUndo = () => {
@@ -250,18 +248,6 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
         }));
     };
 
-    // File upload
-    const uploadImg = (e: React.ChangeEvent<HTMLInputElement>) => {
-        if (e.target.files) {
-            const file = e.target.files[0];
-            const image = new Image();
-            const imgUrl = URL.createObjectURL(file);
-            image.src = imgUrl;
-            ImageUtility.drawImgToCanvas(image, canvasRef);
-            currImg.current = image;
-        }
-    };
-
     // Get AI Edit
     const getEdit = async () => {
         const img = currImg.current;
@@ -280,6 +266,8 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
 
             // create first image
             if (!newCollectionRef.current) {
+                if (addToExistingCollection.current) {
+                }
                 if (!(originalImg.current && imageRootDoc)) return;
                 console.log('creating first image');
                 // create new collection and add it to the view
@@ -292,7 +280,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
                 });
                 DocUtils.MakeLink(imageRootDoc, newCollectionRef.current, { link_relationship: 'Image Edit Version History', link_displayLine: false });
                 // add the doc to the main freeform
-                addDoc?.(newCollectionRef.current);
+                // addDoc?.(newCollectionRef.current);
                 await createNewImgDoc(originalImg.current, true);
             } else {
                 parentDoc.current = childrenDocs.current[childrenDocs.current.length - 1];
@@ -321,7 +309,6 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
         if (!parentDoc.current) return;
         const startY = NumCast(parentDoc.current.y);
         const len = childrenDocs.current.length;
-        console.log(len);
         let initialYPositions: number[] = [];
         for (let i = 0; i < len; i++) {
             initialYPositions.push(startY + i * offsetDistanceY);
@@ -398,9 +385,10 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
     };
 
     const handleViewClose = () => {
-        // if (newCollectionRef.current) {
-        //     CollectionDockingView.AddSplit(newCollectionRef.current, OpenWhereMod.right);
-        // }
+        if (newCollectionRef.current) {
+            newCollectionRef.current.fitContentOnce = true;
+            CollectionDockingView.AddSplit(newCollectionRef.current, OpenWhereMod.right);
+        }
         MainView.Instance.setImageEditorOpen(false);
         MainView.Instance.setImageEditorSource('');
         setEdits([]);
@@ -409,12 +397,17 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
     return (
         <div className="generativeFillContainer" style={{ display: imageEditorOpen ? 'flex' : 'none' }}>
             <div className="generativeFillControls">
-                <h1>Generative Fill</h1>
+                <h1>AI Image Editor</h1>
                 <div style={{ display: 'flex', alignItems: 'center', gap: '1.5rem' }}>
                     <Buttons canvasRef={canvasRef} currImg={currImg} getEdit={getEdit} loading={loading} onSave={onSave} onReset={handleReset} />
                     <IconButton onClick={handleViewClose}>
                         <BsX color={activeColor} />
                     </IconButton>
+                    {saveLoading && (
+                        <span style={{ height: '100%', display: 'flex', alignItems: 'center', gap: '8px' }}>
+                            Saving image... <Oval height={20} width={20} color="#000000" visible={true} ariaLabel="oval-loading" secondaryColor="#ffffff89" strokeWidth={3} strokeWidthSecondary={3} />
+                        </span>
+                    )}
                 </div>
             </div>
             {/* Main canvas for editing */}
@@ -439,20 +432,11 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
                 </div>
                 {/* Icons */}
                 <div className="iconContainer">
-                    <input ref={fileRef} type="file" accept="image/*" onChange={uploadImg} style={{ display: 'none' }} />
-                    <IconButton
-                        onClick={() => {
-                            if (fileRef.current) {
-                                fileRef.current.click();
-                            }
-                        }}>
-                        <AiOutlineUpload />
-                    </IconButton>
                     <IconButton
                         onClick={() => {
                             setBrushStyle(BrushStyle.ADD);
                         }}>
-                        <BsBrush color={brushStyle === BrushStyle.ADD ? activeColor : 'inherit'} />
+                        <BsEraser color={brushStyle === BrushStyle.ADD ? activeColor : 'inherit'} />
                     </IconButton>
                     {/* Undo and Redo */}
                     <IconButton
@@ -475,6 +459,28 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
                         }}>
                         <CiRedo />
                     </IconButton>
+                    <Box
+                        sx={{
+                            height: 225,
+                            width: '100%',
+                            display: 'flex',
+                            justifyContent: 'center',
+                        }}>
+                        <Slider
+                            sx={{
+                                '& input[type="range"]': {
+                                    WebkitAppearance: 'slider-vertical',
+                                },
+                            }}
+                            orientation="vertical"
+                            min={10}
+                            max={500}
+                            defaultValue={150}
+                            onChange={(e, val) => {
+                                setCursorData(prev => ({ ...prev, width: val as number }));
+                            }}
+                        />
+                    </Box>
                 </div>
                 {/* Edits thumbnails*/}
                 <div className="editsBox">
@@ -484,12 +490,14 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
                             width={100}
                             height={100}
                             src={edit}
-                            onClick={() => {
+                            onClick={async () => {
+                                // if (savedSrcs.current.has(edit)) return;
                                 const img = new Image();
                                 img.src = edit;
                                 ImageUtility.drawImgToCanvas(img, canvasRef);
                                 currImg.current = img;
-                                onSave();
+                                savedSrcs.current.add(edit);
+                                await onSave();
                             }}
                         />
                     ))}
@@ -521,14 +529,6 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
                                     currImg.current = img;
                                 }}
                             />
-                            <div
-                                style={{
-                                    position: 'absolute',
-                                    top: 10,
-                                    right: 10,
-                                }}>
-                                {saveLoading && <Oval height={20} width={20} color="#ffffff" visible={true} ariaLabel="oval-loading" secondaryColor="#ffffff89" strokeWidth={3} strokeWidthSecondary={3} />}
-                            </div>
                         </div>
                     )}
                 </div>
@@ -541,15 +541,14 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
                     type="text"
                     label="Prompt"
                     placeholder="Prompt..."
-                    InputLabelProps={{ style: { fontSize: '1.5rem' } }}
-                    inputProps={{ style: { fontSize: '1.5rem' } }}
+                    InputLabelProps={{ style: { fontSize: '16px' } }}
+                    inputProps={{ style: { fontSize: '16px' } }}
                     sx={{
                         backgroundColor: '#ffffff',
                         position: 'absolute',
-                        bottom: '1rem',
+                        bottom: '16px',
                         transform: 'translateX(calc(50vw - 50%))',
-                        width: 'calc(100vw - 4rem)',
-                        scale: 1.2,
+                        width: 'calc(100vw - 64px)',
                     }}
                 />
             </div>
diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/BrushHandler.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/BrushHandler.ts
index c2716e083..f84f04190 100644
--- a/src/client/views/nodes/generativeFill/generativeFillUtils/BrushHandler.ts
+++ b/src/client/views/nodes/generativeFill/generativeFillUtils/BrushHandler.ts
@@ -1,87 +1,45 @@
-import { GenerativeFillMathHelpers } from "./GenerativeFillMathHelpers";
-import { eraserColor } from "./generativeFillConstants";
-import { Point } from "./generativeFillInterfaces";
+import { GenerativeFillMathHelpers } from './GenerativeFillMathHelpers';
+import { eraserColor } from './generativeFillConstants';
+import { Point } from './generativeFillInterfaces';
 
 export class BrushHandler {
-  static brushCircle = (
-    x: number,
-    y: number,
-    brushRadius: number,
-    ctx: CanvasRenderingContext2D
-  ) => {
-    ctx.globalCompositeOperation = "destination-out";
-    ctx.shadowColor = "#ffffffeb";
-    ctx.shadowBlur = 5;
-    ctx.beginPath();
-    ctx.arc(x, y, brushRadius, 0, 2 * Math.PI);
-    ctx.fill();
-    ctx.closePath();
-  };
+    static brushCircle = (x: number, y: number, brushRadius: number, ctx: CanvasRenderingContext2D) => {
+        ctx.globalCompositeOperation = 'destination-out';
+        ctx.shadowColor = '#ffffffeb';
+        ctx.shadowBlur = 5;
+        ctx.beginPath();
+        ctx.arc(x, y, brushRadius, 0, 2 * Math.PI);
+        ctx.fill();
+        ctx.closePath();
+    };
 
-  static brushCircleOverlay = (
-    x: number,
-    y: number,
-    brushRadius: number,
-    ctx: CanvasRenderingContext2D,
-    fillColor: string,
-    erase: boolean
-  ) => {
-    ctx.globalCompositeOperation = "destination-out";
-    // ctx.globalCompositeOperation = erase ? "destination-out" : "source-over";
-    ctx.fillStyle = fillColor;
-    ctx.shadowColor = eraserColor;
-    ctx.shadowBlur = 5;
-    ctx.beginPath();
-    ctx.arc(x, y, brushRadius, 0, 2 * Math.PI);
-    ctx.fill();
-    ctx.closePath();
-  };
+    static brushCircleOverlay = (x: number, y: number, brushRadius: number, ctx: CanvasRenderingContext2D, fillColor: string, erase: boolean) => {
+        ctx.globalCompositeOperation = 'destination-out';
+        // ctx.globalCompositeOperation = erase ? "destination-out" : "source-over";
+        ctx.fillStyle = fillColor;
+        ctx.shadowColor = eraserColor;
+        ctx.shadowBlur = 5;
+        ctx.beginPath();
+        ctx.arc(x, y, brushRadius, 0, 2 * Math.PI);
+        ctx.fill();
+        ctx.closePath();
+    };
 
-  static createBrushPath = (
-    startPoint: Point,
-    endPoint: Point,
-    brushRadius: number,
-    ctx: CanvasRenderingContext2D
-  ) => {
-    const dist = GenerativeFillMathHelpers.distanceBetween(
-      startPoint,
-      endPoint
-    );
+    static createBrushPath = (startPoint: Point, endPoint: Point, brushRadius: number, ctx: CanvasRenderingContext2D) => {
+        const dist = GenerativeFillMathHelpers.distanceBetween(startPoint, endPoint);
 
-    for (let i = 0; i < dist; i += 5) {
-      const s = i / dist;
-      BrushHandler.brushCircle(
-        startPoint.x * (1 - s) + endPoint.x * s,
-        startPoint.y * (1 - s) + endPoint.y * s,
-        brushRadius,
-        ctx
-      );
-    }
-  };
+        for (let i = 0; i < dist; i += 5) {
+            const s = i / dist;
+            BrushHandler.brushCircle(startPoint.x * (1 - s) + endPoint.x * s, startPoint.y * (1 - s) + endPoint.y * s, brushRadius, ctx);
+        }
+    };
 
-  static createBrushPathOverlay = (
-    startPoint: Point,
-    endPoint: Point,
-    brushRadius: number,
-    ctx: CanvasRenderingContext2D,
-    fillColor: string,
-    erase: boolean
-  ) => {
-    const dist = GenerativeFillMathHelpers.distanceBetween(
-      startPoint,
-      endPoint
-    );
+    static createBrushPathOverlay = (startPoint: Point, endPoint: Point, brushRadius: number, ctx: CanvasRenderingContext2D, fillColor: string, erase: boolean) => {
+        const dist = GenerativeFillMathHelpers.distanceBetween(startPoint, endPoint);
 
-    for (let i = 0; i < dist; i += 5) {
-      const s = i / dist;
-      BrushHandler.brushCircleOverlay(
-        startPoint.x * (1 - s) + endPoint.x * s,
-        startPoint.y * (1 - s) + endPoint.y * s,
-        brushRadius,
-        ctx,
-        fillColor,
-        erase
-      );
-    }
-  };
+        for (let i = 0; i < dist; i += 5) {
+            const s = i / dist;
+            BrushHandler.brushCircleOverlay(startPoint.x * (1 - s) + endPoint.x * s, startPoint.y * (1 - s) + endPoint.y * s, brushRadius, ctx, fillColor, erase);
+        }
+    };
 }
diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts
index 48055903c..45cf7196b 100644
--- a/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts
+++ b/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts
@@ -28,7 +28,7 @@ export class ImageUtility {
         fd.append('image', imgBlob, 'image.png');
         fd.append('mask', maskBlob, 'mask.png');
         fd.append('prompt', prompt);
-        fd.append('size', '1024x1024');
+        fd.append('size', '512x512');
         fd.append('n', n ? JSON.stringify(n) : '1');
         fd.append('response_format', 'b64_json');
 
diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillConstants.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillConstants.ts
index 5a8d33742..412a4d238 100644
--- a/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillConstants.ts
+++ b/src/client/views/nodes/generativeFill/generativeFillUtils/generativeFillConstants.ts
@@ -1,9 +1,9 @@
 // constants
-export const canvasSize = 1024;
+export const canvasSize = 512;
 export const freeformRenderSize = 300;
 export const offsetDistanceY = freeformRenderSize + 200;
 export const offsetX = 200;
-export const newCollectionSize = 1000;
+export const newCollectionSize = 500;
 
 export const activeColor = '#1976d2';
 export const eraserColor = '#e1e9ec';
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index aeee90d16..fc6fc1af8 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -124,9 +124,7 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
      * Transfers the image urls to actual image docs
      */
     private transferToImage = (source: string) => {
-        console.log('Text Anchor', this.textAnchor);
-        console.log('Whole doc anchor', this.imgTargetDoc);
-        const textAnchor = this.textAnchor ?? this.imgTargetDoc;
+        const textAnchor = this.imgTargetDoc;
         if (!textAnchor) return;
         const newDoc = Docs.Create.ImageDocument(source, {
             x: NumCast(textAnchor.x) + NumCast(textAnchor._width) + 10,
-- 
cgit v1.2.3-70-g09d2


From 0eb9d37a9dd1a6539f331f953d5f20c761d5f940 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 17 Aug 2023 10:00:10 -0400
Subject: fixed brushing of pinned viewports for chrome to not flash. converted
 pres<Property> to config_<property> and treeView<Property> to
 treeView_Property.  fixed text toolbar to show/set text properties based on
 insertion point.

---
 src/client/documents/Documents.ts                  | 101 ++--
 src/client/util/CurrentUserUtils.ts                |  38 +-
 src/client/util/DocumentManager.ts                 |   6 +-
 src/client/views/DashboardView.tsx                 |  15 +-
 src/client/views/DocumentButtonBar.tsx             |   6 +-
 src/client/views/InkStroke.scss                    |   1 +
 src/client/views/InkingStroke.tsx                  |   6 +-
 src/client/views/MainView.tsx                      |   4 +-
 src/client/views/MarqueeAnnotator.tsx              |   2 +-
 src/client/views/PropertiesView.scss               |  88 ++--
 src/client/views/PropertiesView.tsx                |  32 +-
 src/client/views/StyleProvider.tsx                 |   8 +-
 src/client/views/TemplateMenu.tsx                  |   2 +-
 .../views/collections/CollectionTimeView.tsx       |   6 +-
 .../views/collections/CollectionTreeView.tsx       |  22 +-
 src/client/views/collections/CollectionView.tsx    |   2 +-
 src/client/views/collections/TabDocView.tsx        |  45 +-
 src/client/views/collections/TreeView.tsx          |  90 ++--
 .../collectionFreeForm/CollectionFreeFormView.scss |   7 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  46 +-
 src/client/views/linking/LinkMenuItem.tsx          |   2 +-
 src/client/views/nodes/DataVizBox/DataVizBox.tsx   | 111 ++--
 .../nodes/DataVizBox/components/Histogram.tsx      | 534 ++++++++++---------
 .../nodes/DataVizBox/components/LineChart.tsx      | 105 ++--
 .../views/nodes/DataVizBox/components/PieChart.tsx | 401 ++++++++-------
 .../views/nodes/DataVizBox/components/TableBox.tsx |  87 ++--
 src/client/views/nodes/DocumentView.tsx            |  17 +-
 src/client/views/nodes/FontIconBox/FontIconBox.tsx |   4 +-
 src/client/views/nodes/FunctionPlotBox.tsx         |   4 +-
 src/client/views/nodes/ImageBox.tsx                |  14 +-
 src/client/views/nodes/KeyValueBox.tsx             |  17 +-
 src/client/views/nodes/LinkBox.tsx                 |   2 +-
 src/client/views/nodes/MapBox/MapBox.tsx           |   2 +-
 src/client/views/nodes/MapBox/MapBox2.tsx          |   2 +-
 src/client/views/nodes/PDFBox.tsx                  |   6 +-
 src/client/views/nodes/WebBox.tsx                  |  16 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  29 +-
 .../formattedText/ProsemirrorExampleTransfer.ts    |   2 +-
 .../views/nodes/formattedText/RichTextMenu.tsx     |   2 +-
 src/client/views/nodes/trails/PresBox.tsx          | 567 +++++++++++----------
 src/client/views/nodes/trails/PresElementBox.tsx   |  50 +-
 src/client/views/pdf/Annotation.tsx                |   4 +-
 src/client/views/pdf/PDFViewer.tsx                 |   8 +-
 src/fields/Doc.ts                                  |   8 +-
 src/fields/documentSchemas.ts                      |  10 +-
 45 files changed, 1334 insertions(+), 1197 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 5c33e319d..96a983153 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -288,12 +288,12 @@ export class DocumentOptions {
     _isTimelineLabel?: BOOLt = new BoolInfo('is document a timeline label');
     _isLightbox?: BOOLt = new BoolInfo('whether a collection acts as a lightbox by opening lightbox links by hiding all other documents in collection besides link target');
 
-    presPanX?: NUMt = new NumInfo('panX saved as a view spec');
-    presPanY?: NUMt = new NumInfo('panY saved as a view spec');
-    presViewScale?: NUMt = new NumInfo('viewScale saved as a view Spec');
-    presTransition?: NUMt = new NumInfo('the time taken for the transition TO a document');
-    presDuration?: NUMt = new NumInfo('the duration of the slide in presentation view');
-    presZoomText?: BOOLt = new BoolInfo('whether text anchors should shown in a larger box when following links to make them stand out');
+    config_panX?: NUMt = new NumInfo('panX saved as a view spec');
+    config_panY?: NUMt = new NumInfo('panY saved as a view spec');
+    config_viewScale?: NUMt = new NumInfo('viewScale saved as a view Spec');
+    presentation_transition?: NUMt = new NumInfo('the time taken for the transition TO a document');
+    presentation_duration?: NUMt = new NumInfo('the duration of the slide in presentation view');
+    presentation_zoomText?: BOOLt = new BoolInfo('whether text anchors should shown in a larger box when following links to make them stand out');
 
     data?: any;
     data_useCors?: BOOLt = new BoolInfo('whether CORS protocol should be used for web page');
@@ -378,21 +378,21 @@ export class DocumentOptions {
     onDragStart?: ScriptField; //script to execute at start of drag operation --  e.g., when a "creator" button is dragged this script generates a different document to drop
     target?: Doc; // available for use in scripts.  used to provide a document parameter to the script (Note, this is a convenience entry since any field could be used for parameterizing a script)
 
-    treeViewHideTitle?: BOOLt = new BoolInfo('whether to hide the top document title of a tree view');
-    treeViewHideUnrendered?: BOOLt = new BoolInfo("tells tree view not to display documents that have an 'layout_unrendered' tag unless they also have a treeViewFieldKey tag (presBox)");
-    treeViewHideHeaderIfTemplate?: BOOLt = new BoolInfo('whether to hide the header for a document in a tree view only if a childLayoutTemplate is provided (presBox)');
-    treeViewHideHeader?: BOOLt = new BoolInfo('whether to hide the header for a document in a tree view');
-    treeViewHideHeaderFields?: BOOLt = new BoolInfo('whether to hide the drop down options for tree view items.');
-    treeViewChildDoubleClick?: ScriptField; //
-    treeViewOpenIsTransient?: BOOLt = new BoolInfo("ignores the treeViewOpen Doc flag, allowing a treeViewItem's expand/collapse state to be independent of other views of the same document in the same or any other tree view");
-    treeViewOpen?: BOOLt = new BoolInfo('whether this document is expanded in a tree view');
-    treeViewExpandedView?: string; // which field/thing is displayed when this item is opened in tree view
-    treeViewExpandedViewLock?: BOOLt = new BoolInfo('whether the expanded view can be changed');
-    treeViewChecked?: ScriptField; // script to call when a tree view checkbox is checked
-    treeViewTruncateTitleWidth?: NUMt = new NumInfo('maximum width of a treew view title before truncation');
-    treeViewHasOverlay?: BOOLt = new BoolInfo('whether the treeview has an overlay for freeform annotations');
-    treeViewType?: string; // whether treeview is a Slide, file system, or (default) collection hierarchy
-    treeViewFreezeChildren?: STRt = new StrInfo('set (add, remove, add|remove) to disable adding, removing or both from collection');
+    treeView_HideTitle?: BOOLt = new BoolInfo('whether to hide the top document title of a tree view');
+    treeView_HideUnrendered?: BOOLt = new BoolInfo("tells tree view not to display documents that have an 'layout_unrendered' tag unless they also have a treeView_FieldKey tag (presBox)");
+    treeView_HideHeaderIfTemplate?: BOOLt = new BoolInfo('whether to hide the header for a document in a tree view only if a childLayoutTemplate is provided (presBox)');
+    treeView_HideHeader?: BOOLt = new BoolInfo('whether to hide the header for a document in a tree view');
+    treeView_HideHeaderFields?: BOOLt = new BoolInfo('whether to hide the drop down options for tree view items.');
+    treeView_ChildDoubleClick?: ScriptField; //
+    treeView_OpenIsTransient?: BOOLt = new BoolInfo("ignores the treeView_Open Doc flag, allowing a treeView_Item's expand/collapse state to be independent of other views of the same document in the same or any other tree view");
+    treeView_Open?: BOOLt = new BoolInfo('whether this document is expanded in a tree view');
+    treeView_ExpandedView?: string; // which field/thing is displayed when this item is opened in tree view
+    treeView_ExpandedViewLock?: BOOLt = new BoolInfo('whether the expanded view can be changed');
+    treeView_Checked?: ScriptField; // script to call when a tree view checkbox is checked
+    treeView_TruncateTitleWidth?: NUMt = new NumInfo('maximum width of a treew view title before truncation');
+    treeView_HasOverlay?: BOOLt = new BoolInfo('whether the treeview has an overlay for freeform annotations');
+    treeView_Type?: string; // whether treeview is a Slide, file system, or (default) collection hierarchy
+    treeView_FreezeChildren?: STRt = new StrInfo('set (add, remove, add|remove) to disable adding, removing or both from collection');
 
     sidebar_color?: string; // background color of text sidebar
     sidebar_collectionType?: string; // collection type of text sidebar
@@ -635,7 +635,7 @@ export namespace Docs {
                 DocumentType.CONFIG,
                 {
                     layout: { view: CollectionView, dataField: defaultDataKey },
-                    options: { layout_hideLinkButton: true, layout_unrendered: true },
+                    options: { config: '', layout_hideLinkButton: true, layout_unrendered: true },
                 },
             ],
             [
@@ -679,7 +679,7 @@ export namespace Docs {
                 DocumentType.DATAVIZ,
                 {
                     layout: { view: DataVizBox, dataField: defaultDataKey },
-                    options: { _layout_fitWidth: true, nativeDimModifiable: true },
+                    options: { dataViz_title: '', _layout_fitWidth: true, nativeDimModifiable: true },
                 },
             ],
             [
@@ -845,7 +845,7 @@ export namespace Docs {
          * only when creating a DockDocument from the current user's already existing
          * main document.
          */
-        function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = 'data', protoId?: string, placeholderDoc?: Doc) {
+        function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = 'data', protoId?: string, placeholderDoc?: Doc, noView?: boolean) {
             const viewKeys = ['x', 'y', 'isSystem']; // keys that should be addded to the view document even though they don't begin with an "_"
             const { omit: dataProps, extract: viewProps } = OmitKeys(options, viewKeys, '^_');
 
@@ -872,29 +872,34 @@ export namespace Docs {
                 dataDoc.proto = proto;
             }
 
-            const viewFirstProps: { [id: string]: any } = { author: Doc.CurrentUserEmail };
-            viewFirstProps['acl-Guest'] = options['_acl-Guest'] ?? (Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.View);
-            let viewDoc: Doc;
-            // determines whether viewDoc should be created using placeholder Doc or default
-            if (placeholderDoc) {
-                placeholderDoc._height = options._height !== undefined ? Number(options._height) : undefined;
-                placeholderDoc._width = options._width !== undefined ? Number(options._width) : undefined;
-                viewDoc = Doc.assign(placeholderDoc, viewFirstProps, true, true);
-                Array.from(Object.keys(placeholderDoc))
-                    .filter(key => key.startsWith('acl'))
-                    .forEach(key => (dataDoc[key] = viewDoc[key] = placeholderDoc[key]));
-            } else {
-                viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewFirstProps, true, true);
-            }
-            Doc.assign(viewDoc, viewProps, true, true);
-            if (![DocumentType.LINK, DocumentType.CONFIG, DocumentType.LABEL].includes(viewDoc.type as any)) {
-                DocUtils.MakeLinkToActiveAudio(() => viewDoc);
+            if (!noView) {
+                const viewFirstProps: { [id: string]: any } = { author: Doc.CurrentUserEmail };
+                viewFirstProps['acl-Guest'] = options['_acl-Guest'] ?? (Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.View);
+                let viewDoc: Doc;
+                // determines whether viewDoc should be created using placeholder Doc or default
+                if (placeholderDoc) {
+                    placeholderDoc._height = options._height !== undefined ? Number(options._height) : undefined;
+                    placeholderDoc._width = options._width !== undefined ? Number(options._width) : undefined;
+                    viewDoc = Doc.assign(placeholderDoc, viewFirstProps, true, true);
+                    Array.from(Object.keys(placeholderDoc))
+                        .filter(key => key.startsWith('acl'))
+                        .forEach(key => (dataDoc[key] = viewDoc[key] = placeholderDoc[key]));
+                } else {
+                    viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewFirstProps, true, true);
+                }
+                Doc.assign(viewDoc, viewProps, true, true);
+                if (![DocumentType.LINK, DocumentType.CONFIG, DocumentType.LABEL].includes(viewDoc.type as any)) {
+                    DocUtils.MakeLinkToActiveAudio(() => viewDoc);
+                }
+                updateCachedAcls(dataDoc);
+                updateCachedAcls(viewDoc);
+
+                return viewDoc;
             }
 
             updateCachedAcls(dataDoc);
-            updateCachedAcls(viewDoc);
 
-            return viewDoc;
+            return dataDoc;
         }
 
         export function ImageDocument(url: string | ImageField, options: DocumentOptions = {}, overwriteDoc?: Doc) {
@@ -1065,7 +1070,7 @@ export namespace Docs {
         }
 
         export function ConfigDocument(options: DocumentOptions, id?: string) {
-            return InstanceFromProto(Prototypes.get(DocumentType.CONFIG), options?.data, options, id);
+            return InstanceFromProto(Prototypes.get(DocumentType.CONFIG), options?.data, options, id, '', undefined, undefined, true);
         }
 
         export function HTMLMarkerDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
@@ -1105,7 +1110,9 @@ export namespace Docs {
         }
 
         export function TreeDocument(documents: Array<Doc>, options: DocumentOptions, id?: string, protoId?: string) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _xMargin: 5, _yMargin: 5, ...options, _type_collection: CollectionViewType.Tree }, id, undefined, protoId);
+            const doc = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _xMargin: 5, _yMargin: 5, ...options, _type_collection: CollectionViewType.Tree }, id, undefined, protoId);
+            Doc.GetProto(doc).treeView = ''; /// not really needed, but makes keyvalue pane look better
+            return doc;
         }
 
         export function StackingDocument(documents: Array<Doc>, options: DocumentOptions, id?: string, protoId?: string) {
@@ -1163,11 +1170,11 @@ export namespace Docs {
         }
 
         export function DataVizDocument(url: string, options?: DocumentOptions, overwriteDoc?: Doc) {
-            return InstanceFromProto(Prototypes.get(DocumentType.DATAVIZ), new CsvField(url), { title: 'Data Viz', type: 'dataviz',  ...options }, undefined, undefined, undefined, overwriteDoc);
+            return InstanceFromProto(Prototypes.get(DocumentType.DATAVIZ), new CsvField(url), { title: 'Data Viz', type: 'dataviz', ...options }, undefined, undefined, undefined, overwriteDoc);
         }
 
         export function DockDocument(documents: Array<Doc>, config: string, options: DocumentOptions, id?: string) {
-            const ret = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { treeViewFreezeChildren: 'remove|add', ...options, type_collection: CollectionViewType.Docking, dockingConfig: config }, id);
+            const ret = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { treeView_FreezeChildren: 'remove|add', ...options, type_collection: CollectionViewType.Docking, dockingConfig: config }, id);
             documents.map(c => Doc.SetContainer(c, ret));
             return ret;
         }
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 47cd866cb..af43ef16a 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -278,12 +278,12 @@ export class CurrentUserUtils {
             {key: "Button",     creator: Docs.Create.ButtonDocument,                        opts: { _width: 150, _height: 50, _xPadding: 10, _yPadding: 10}, scripts: {onClick: FollowLinkScript()?.script.originalScript ?? ""}},
             {key: "Script",     creator: opts => Docs.Create.ScriptingDocument(null, opts), opts: { _width: 200, _height: 250, }},
             {key: "DataViz",    creator: opts => Docs.Create.DataVizDocument("/users/rz/Downloads/addresses.csv", opts),   opts: { _width: 300, _height: 300 }},
-            {key: "Header",     creator: headerTemplate,                                    opts: { _width: 300, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _layout_autoHeight: true, treeViewHideUnrendered: true}},
-            {key: "Trail",      creator: Docs.Create.PresDocument,                          opts: { _width: 400, _height: 30, _type_collection: CollectionViewType.Stacking, dropAction: "embed" as dropActionType, treeViewHideTitle: true, _layout_fitWidth:true, _chromeHidden: true, layout_boxShadow: "0 0" }},
+            {key: "Header",     creator: headerTemplate,                                    opts: { _width: 300, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _layout_autoHeight: true, treeView_HideUnrendered: true}},
+            {key: "Trail",      creator: Docs.Create.PresDocument,                          opts: { _width: 400, _height: 30, _type_collection: CollectionViewType.Stacking, dropAction: "embed" as dropActionType, treeView_HideTitle: true, _layout_fitWidth:true, _chromeHidden: true, layout_boxShadow: "0 0" }},
             {key: "Tab",        creator: opts => Docs.Create.FreeformDocument([], opts),    opts: { _width: 500, _height: 800, _layout_fitWidth: true, _freeform_backgroundGrid: true, }},
             {key: "Slide",      creator: opts => Docs.Create.TreeDocument([], opts),        opts: { _width: 300, _height: 200, _type_collection: CollectionViewType.Tree, 
-                                                                                                treeViewHasOverlay: true, _text_fontSize: "20px", _layout_autoHeight: true, 
-                                                                                                dropAction:'move', treeViewType: TreeViewType.outline, 
+                                                                                                treeView_HasOverlay: true, _text_fontSize: "20px", _layout_autoHeight: true, 
+                                                                                                dropAction:'move', treeView_Type: TreeViewType.outline, 
                                                                                                 backgroundColor: "white", _xMargin: 0, _yMargin: 0, _createDocOnCR: true 
                                                                                             }, funcs: {title: 'self.text?.Text'}},
             ];
@@ -493,8 +493,8 @@ export class CurrentUserUtils {
         const childContextMenuLabels =  ["Toggle Dark Theme", "Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard", "Reset Dashboard"];// entries must be kept in synch with childContextMenuScripts, childContextMenuIcons, and childContextMenuFilters
         const childContextMenuIcons = ["chalkboard", "tv", "camera", "users", "times", "trash"]; // entries must be kept in synch with childContextMenuScripts, childContextMenuLabels, and childContextMenuFilters
         const reqdOpts:DocumentOptions = {
-            title: "My Dashboards", childHideLinkButton: true, treeViewFreezeChildren: "remove|add", treeViewHideTitle: true, layout_boxShadow: "0 0", childDontRegisterViews: true,
-            dropAction: "same", treeViewType: TreeViewType.fileSystem, isFolder: true, isSystem: true,  treeViewTruncateTitleWidth: 350, ignoreClick: true, 
+            title: "My Dashboards", childHideLinkButton: true, treeView_FreezeChildren: "remove|add", treeView_HideTitle: true, layout_boxShadow: "0 0", childDontRegisterViews: true,
+            dropAction: "same", treeView_Type: TreeViewType.fileSystem, isFolder: true, isSystem: true,  treeView_TruncateTitleWidth: 350, ignoreClick: true, 
             layout_headerButton: newDashboardButton, childDragAction: "none",
             _layout_showTitle: "title", _height: 400, _gridGap: 5, _forceActive: true, _lockedPosition: true,
             contextMenuLabels:new List<string>(contextMenuLabels),
@@ -522,16 +522,16 @@ export class CurrentUserUtils {
         
         const newFolder = `TreeView_addNewFolder()`;
         const newFolderOpts: DocumentOptions = {
-            _forceActive: true, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, _width: 30, _height: 30, undoIgnoreFields:new List<string>(['treeViewSortCriterion']),
+            _forceActive: true, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, _width: 30, _height: 30, undoIgnoreFields:new List<string>(['treeView_SortCriterion']),
             title: "New folder", color: Colors.BLACK, btnType: ButtonType.ClickButton, toolTip: "Create new folder", buttonText: "New folder", icon: "folder-plus", isSystem: true
         };
         const newFolderScript = { onClick: newFolder};
         const newFolderButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myFilesystem?.layout_headerButton), newFolderOpts) ?? Docs.Create.FontIconDocument(newFolderOpts), newFolderScript);
         
         const reqdOpts:DocumentOptions = { _layout_showTitle: "title", _height: 100, _gridGap: 5, _forceActive: true, _lockedPosition: true, 
-            title: "My Documents", layout_headerButton: newFolderButton, treeViewHideTitle: true, dropAction: 'add', isSystem: true,
-            isFolder: true, treeViewType: TreeViewType.fileSystem, childHideLinkButton: true, layout_boxShadow: "0 0", childDontRegisterViews: true, 
-            treeViewTruncateTitleWidth: 350, ignoreClick: true, childDragAction: "embed",
+            title: "My Documents", layout_headerButton: newFolderButton, treeView_HideTitle: true, dropAction: 'add', isSystem: true,
+            isFolder: true, treeView_Type: TreeViewType.fileSystem, childHideLinkButton: true, layout_boxShadow: "0 0", childDontRegisterViews: true, 
+            treeView_TruncateTitleWidth: 350, ignoreClick: true, childDragAction: "embed",
             childContextMenuLabels: new List<string>(["Create new folder"]),
             childContextMenuIcons: new List<string>(["plus"]),
             layout_explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard."
@@ -548,8 +548,8 @@ export class CurrentUserUtils {
     /// initializes the panel displaying docs that have been recently closed
     static setupRecentlyClosed(doc: Doc, field:string) {
         const reqdOpts:DocumentOptions = { _layout_showTitle: "title", _lockedPosition: true, _gridGap: 5, _forceActive: true, isFolder: true,
-            title: "My Recently Closed", childHideLinkButton: true, treeViewHideTitle: true, childDragAction: "move", isSystem: true,
-            treeViewTruncateTitleWidth: 350, ignoreClick: true,  layout_boxShadow: "0 0", childDontRegisterViews: true, dropAction: "same",
+            title: "My Recently Closed", childHideLinkButton: true, treeView_HideTitle: true, childDragAction: "move", isSystem: true,
+            treeView_TruncateTitleWidth: 350, ignoreClick: true,  layout_boxShadow: "0 0", childDontRegisterViews: true, dropAction: "same",
             contextMenuLabels: new List<string>(["Empty recently closed"]),
             contextMenuIcons:new List<string>(["trash"]),
             layout_explainer: "Recently closed documents appear in this menu. They will only be deleted if you explicity empty this list."
@@ -573,9 +573,9 @@ export class CurrentUserUtils {
         const reqdOpts:DocumentOptions = {
             _lockedPosition: true, _gridGap: 5, _forceActive: true, title: Doc.CurrentUserEmail +"-view",
             layout_boxShadow: "0 0", childDontRegisterViews: true, dropAction: "same", ignoreClick: true, isSystem: true,
-            treeViewHideTitle: true, treeViewTruncateTitleWidth: 350
+            treeView_HideTitle: true, treeView_TruncateTitleWidth: 350
         };
-        if (!doc[field]) DocUtils.AssignOpts(doc, {treeViewOpen: true, treeViewExpandedView: "fields" });
+        if (!doc[field]) DocUtils.AssignOpts(doc, {treeView_Open: true, treeView_ExpandedView: "fields" });
         return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, [doc]);
     }
 
@@ -789,19 +789,19 @@ export class CurrentUserUtils {
     static setupSharedDocs(doc: Doc, sharingDocumentId: string) {
         const dblClkScript = "{scriptContext.openLevel(documentView); addDocToList(scriptContext.props.treeView.props.Document, 'viewed', documentView.rootDoc);}";
         
-        const sharedScripts = {  treeViewChildDoubleClick: dblClkScript,  }
+        const sharedScripts = {  treeView_ChildDoubleClick: dblClkScript,  }
         const sharedDocOpts:DocumentOptions = {
             title: "My Shared Docs",
             userColor: "rgb(202, 202, 202)",
-            isFolder:true, undoIgnoreFields:new List<string>(['treeViewSortCriterion']),
+            isFolder:true, undoIgnoreFields:new List<string>(['treeView_SortCriterion']),
             // childContextMenuFilters: new List<ScriptField>([dashboardFilter!,]),
             // childContextMenuScripts: new List<ScriptField>([addToDashboards!,]),
             // childContextMenuLabels: new List<string>(["Add to Dashboards",]),
             // childContextMenuIcons: new List<string>(["user-plus",]),
             "acl-Guest": SharingPermissions.Augment, "_acl-Guest": SharingPermissions.Augment,
             childDragAction: "embed", isSystem: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 0, _gridGap: 15, childDontRegisterViews:true,
-            // NOTE: treeViewHideTitle & _layout_showTitle is for a TreeView's editable title, _layout_showTitle is for DocumentViews title bar
-            _layout_showTitle: "title", treeViewHideTitle: true, ignoreClick: true, _lockedPosition: true, layout_boxShadow: "0 0", _chromeHidden: true, dontRegisterView: true, 
+            // NOTE: treeView_HideTitle & _layout_showTitle is for a TreeView's editable title, _layout_showTitle is for DocumentViews title bar
+            _layout_showTitle: "title", treeView_HideTitle: true, ignoreClick: true, _lockedPosition: true, layout_boxShadow: "0 0", _chromeHidden: true, dontRegisterView: true, 
             layout_explainer: "This is where documents or dashboards that other users have shared with you will appear. To share a document or dashboard right click and select 'Share'"
         };
         
@@ -864,7 +864,7 @@ export class CurrentUserUtils {
         doc.userColor ?? (doc.userColor = Colors.LIGHT_GRAY);
         doc.userTheme ?? (doc.userTheme = ColorScheme.Dark);
         doc.filterDocCount = 0;
-        doc.treeViewFreezeChildren = "remove|add";
+        doc.treeView_FreezeChildren = "remove|add";
         doc.activePage = doc.activeDashboard === undefined ? 'home': doc.activePage;
         this.setupLinkDocs(doc, linkDatabaseId);
         this.setupSharedDocs(doc, sharingDocumentId);  // sets up the right sidebar collection for mobile upload documents and sharing
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 42132c2d7..9779639f4 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -315,10 +315,10 @@ export class DocumentManager {
             if (options.toggleTarget && (!options.didMove || docView.rootDoc.hidden)) docView.rootDoc.hidden = !docView.rootDoc.hidden;
             if (options.effect) docView.rootDoc[Animation] = options.effect;
 
-            if (options.zoomTextSelections && Doc.UnhighlightTimer && contextView && viewSpec.textHtml) {
+            if (options.zoomTextSelections && Doc.UnhighlightTimer && contextView && viewSpec.text_html) {
                 // if the docView is a text anchor, the contextView is the PDF/Web/Text doc
-                contextView.htmlOverlayEffect = StrCast(options?.effect?.presEffect, StrCast(options?.effect?.followLinkAnimEffect));
-                contextView.textHtmlOverlay = StrCast(targetDoc.textHtml);
+                contextView.htmlOverlayEffect = StrCast(options?.effect?.presentation_effect, StrCast(options?.effect?.followLinkAnimEffect));
+                contextView.textHtmlOverlay = StrCast(targetDoc.text_html);
                 DocumentManager._overlayViews.add(contextView);
             }
             Doc.AddUnHighlightWatcher(() => {
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 21808d6e0..3e4827c83 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -108,7 +108,14 @@ export class DashboardView extends React.Component {
                 }}>
                 <div className="header">Create New Dashboard</div>
                 <EditableText formLabel="Title" placeholder={placeholder} type={Type.SEC} color={StrCast(Doc.UserDoc().userColor)} setVal={val => this.setNewDashboardName(val as string)} fillWidth />
-                <ColorPicker formLabel="Background" colorPickerType="github" type={Type.TERT} selectedColor={this.newDashboardColor} setSelectedColor={this.setNewDashboardColor} />
+                <ColorPicker
+                    formLabel="Background" //
+                    colorPickerType="github"
+                    type={Type.TERT}
+                    selectedColor={this.newDashboardColor}
+                    setFinalColor={this.setNewDashboardColor}
+                    setSelectedColor={this.setNewDashboardColor}
+                />
                 <div className="button-bar">
                     <Button text="Cancel" color={StrCast(Doc.UserDoc().userColor)} onClick={this.abortCreateNewDashboard} />
                     <Button type={Type.TERT} text="Create" color={StrCast(Doc.UserDoc().userVariantColor)} onClick={() => this.createNewDashboard(this.newDashboardName!, this.newDashboardColor)} />
@@ -437,12 +444,12 @@ export class DashboardView extends React.Component {
             title: 'My Trails',
             _layout_showTitle: 'title',
             _height: 100,
-            treeViewHideTitle: true,
+            treeView_HideTitle: true,
             _layout_fitWidth: true,
             _gridGap: 5,
             _forceActive: true,
             childDragAction: 'embed',
-            treeViewTruncateTitleWidth: 150,
+            treeView_TruncateTitleWidth: 150,
             ignoreClick: true,
             layout_headerButton: myTrailsBtn,
             contextMenuIcons: new List<string>(['plus']),
@@ -454,7 +461,7 @@ export class DashboardView extends React.Component {
             isSystem: true,
             layout_explainer: 'All of the trails that you have created will appear here.',
         };
-        const myTrails = DocUtils.AssignScripts(Docs.Create.TreeDocument([], reqdOpts), { treeViewChildDoubleClick: 'openPresentation(documentView.rootDoc)' });
+        const myTrails = DocUtils.AssignScripts(Docs.Create.TreeDocument([], reqdOpts), { treeView_ChildDoubleClick: 'openPresentation(documentView.rootDoc)' });
         dashboardDoc.myTrails = new PrefetchProxy(myTrails);
 
         const contextMenuScripts = [reqdBtnScript.onClick];
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 6f5e9f5c0..345135a1a 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -563,10 +563,10 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
         const rootDoc = rootView?.rootDoc;
         if (rootDoc) {
             const anchor = rootView.ComponentView?.getAnchor?.(false) ?? rootDoc;
-            const trail = DocCast(anchor.presTrail) ?? Doc.MakeCopy(DocCast(Doc.UserDoc().emptyTrail), true);
-            if (trail !== anchor.presTrail) {
+            const trail = DocCast(anchor.presentationTrail) ?? Doc.MakeCopy(DocCast(Doc.UserDoc().emptyTrail), true);
+            if (trail !== anchor.presentationTrail) {
                 DocUtils.MakeLink(anchor, trail, { link_relationship: 'link trail' });
-                anchor.presTrail = trail;
+                anchor.presentationTrail = trail;
             }
             Doc.ActivePresentation = trail;
             this.props.views().lastElement()?.props.addDocTab(trail, OpenWhere.replaceRight);
diff --git a/src/client/views/InkStroke.scss b/src/client/views/InkStroke.scss
index f504890a5..c672824bf 100644
--- a/src/client/views/InkStroke.scss
+++ b/src/client/views/InkStroke.scss
@@ -17,6 +17,7 @@
     display: flex;
     align-items: center;
     height: 100%;
+    width: 100%;
     transition: inherit;
     .inkStroke {
         mix-blend-mode: multiply;
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 93163c1a3..b3647249a 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -91,8 +91,8 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
         const anchor = Docs.Create.ConfigDocument({
             title: 'Ink anchor:' + this.rootDoc.title,
             // set presentation timing for restoring shape
-            presDuration: 1100,
-            presTransition: 1000,
+            presentation_duration: 1100,
+            presentation_transition: 1000,
             annotationOn: this.rootDoc,
         });
         if (anchor) {
@@ -362,7 +362,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
 
     _subContentView: DocComponentView | undefined;
     setSubContentView = (doc: DocComponentView) => (this._subContentView = doc);
-    @computed get fillColor() { 
+    @computed get fillColor() {
         const isInkMask = BoolCast(this.layoutDoc.stroke_isInkMask);
         return isInkMask ? DashColor(StrCast(this.layoutDoc.fillColor, 'transparent')).blacken(0).rgb().toString() : this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FillColor) ?? 'transparent';
     }
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index dc236b603..e376c4fdf 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -156,8 +156,8 @@ export class MainView extends React.Component {
             DocServer.setLivePlaygroundFields([
                 'dataTransition',
                 'viewTransition',
-                'treeViewOpen',
-                'treeViewExpandedView',
+                'treeView_Open',
+                'treeView_ExpandedView',
                 'carousel_index',
                 'itemIndex', // for changing slides in presentations
                 'layout_sidebarWidthPercent',
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index a4a2c1df9..4aabb83dc 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -201,7 +201,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
         textRegionAnnoProto.height = Math.max(maxY, 0) - Math.max(minY, 0);
         textRegionAnnoProto.width = Math.max(maxX, 0) - Math.max(minX, 0);
         // mainAnnoDocProto.text = this._selectionText;
-        textRegionAnnoProto.textInlineAnnotations = new List<Doc>(annoDocs);
+        textRegionAnnoProto.text_inlineAnnotations = new List<Doc>(annoDocs);
         savedAnnoMap.clear();
         return textRegionAnno;
     };
diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss
index b79486167..1a7c9eb64 100644
--- a/src/client/views/PropertiesView.scss
+++ b/src/client/views/PropertiesView.scss
@@ -26,36 +26,34 @@
         padding: 5px 10px;
     }
 
-
     .propertiesView-sharing {
         //border-bottom: 1px solid black;
         //padding: 8.5px;
 
+        .propertiesView-buttonContainer {
+            float: right;
+            display: flex;
 
-            .propertiesView-buttonContainer {
-                float: right;
-                display: flex;
-
-                button {
-                    width: 15;
-                    height: 15;
-                    padding: 0;
-                    margin-top: -5;
-                }
+            button {
+                width: 15;
+                height: 15;
+                padding: 0;
+                margin-top: -5;
             }
+        }
 
-            .change-buttons {
-                display: flex;
+        .change-buttons {
+            display: flex;
 
-                button {
-                    width: 5;
-                    height: 5;
-                }
+            button {
+                width: 5;
+                height: 5;
+            }
 
-                input {
-                    width: 100%;
-                }
+            input {
+                width: 100%;
             }
+        }
     }
 
     .propertiesView-acls-checkbox {
@@ -63,11 +61,11 @@
         margin-left: 50px;
     }
 
-    .propertiesView-shareDropDown{
+    .propertiesView-shareDropDown {
         margin-right: 10px;
         min-width: 65px;
-        
-        & .propertiesView-shareDropDownNone{
+
+        & .propertiesView-shareDropDownNone {
             padding: 0px;
             padding-left: 3px;
             padding-right: 3px;
@@ -77,7 +75,7 @@
             border: 1px solid rgb(71, 71, 71);
         }
         & .propertiesView-shareDropDownEdit,
-        .propertiesView-shareDropDownAdmin{
+        .propertiesView-shareDropDownAdmin {
             padding: 0px;
             padding-left: 3px;
             padding-right: 3px;
@@ -86,17 +84,16 @@
             border-radius: 6px;
             border: 1px solid rgb(75, 75, 5);
         }
-        & .propertiesView-shareDropDownAugment{
+        & .propertiesView-shareDropDownAugment {
             padding: 0px;
             padding-left: 3px;
             padding-right: 3px;
             background: rgb(208, 255, 208);
-            color:rgb(19, 80, 19);
+            color: rgb(19, 80, 19);
             border-radius: 6px;
             border: 1px solid rgb(19, 80, 19);
-
         }
-        & .propertiesView-shareDropDownView{
+        & .propertiesView-shareDropDownView {
             padding: 0px;
             padding-left: 3px;
             padding-right: 3px;
@@ -105,7 +102,7 @@
             border-radius: 6px;
             border: 1px solid rgb(25, 25, 101);
         }
-        & .propertiesView-shareDropDownNot-Shared{
+        & .propertiesView-shareDropDownNot-Shared {
             padding: 0px;
             padding-left: 3px;
             padding-right: 3px;
@@ -245,15 +242,12 @@
             cursor: auto;
         }
     }
+}
 
-    }
-
-    .propertiesView-presTrails {
-        //border-bottom: 1px solid black;
-        //padding: 8.5px;
-
-      
-    }
+.propertiesView-presentationTrails {
+    //border-bottom: 1px solid black;
+    //padding: 8.5px;
+}
 
 .inking-button {
     display: flex;
@@ -515,9 +509,9 @@
     }
 }
 
-.propertiesView-wordTitle{
-    color:darkslategray;
-    font-weight:200;
+.propertiesView-wordTitle {
+    color: darkslategray;
+    font-weight: 200;
 }
 
 .editable-title {
@@ -532,15 +526,15 @@
     }
 }
 
-.propertiesView-wordType{
-    color:darkslategray;
-    font-weight:200;
+.propertiesView-wordType {
+    color: darkslategray;
+    font-weight: 200;
 }
 
-.currentType{
+.currentType {
     text-decoration: underline;
     display: flex;
-    align-items:center;
+    align-items: center;
     // border: solid 1px #323232;
     // padding-left: 5px;
     // padding-top: 4px;
@@ -548,8 +542,8 @@
     // height: fit-content;
 }
 
-.currentType-icon{
-    margin-right:5px;
+.currentType-icon {
+    margin-right: 5px;
 }
 
 .properties-flyout {
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 872f1c6ab..1b6037ed9 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -1700,50 +1700,50 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                             </div>
                         </div>
                         {!selectedItem ? null : (
-                            <div className="propertiesView-presTrails">
-                                <div className="propertiesView-presTrails-title" onPointerDown={action(() => (this.openPresTransitions = !this.openPresTransitions))} style={{ backgroundColor: this.openPresTransitions ? 'black' : '' }}>
+                            <div className="propertiesView-presentationTrails">
+                                <div className="propertiesView-presentationTrails-title" onPointerDown={action(() => (this.openPresTransitions = !this.openPresTransitions))} style={{ backgroundColor: this.openPresTransitions ? 'black' : '' }}>
                                     &nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={'rocket'} /> &nbsp; Transitions
-                                    <div className="propertiesView-presTrails-title-icon">
+                                    <div className="propertiesView-presentationTrails-title-icon">
                                         <FontAwesomeIcon icon={this.openPresTransitions ? 'caret-down' : 'caret-right'} size="lg" />
                                     </div>
                                 </div>
-                                {this.openPresTransitions ? <div className="propertiesView-presTrails-content">{PresBox.Instance.transitionDropdown}</div> : null}
+                                {this.openPresTransitions ? <div className="propertiesView-presentationTrails-content">{PresBox.Instance.transitionDropdown}</div> : null}
                             </div>
                         )}
                         {!selectedItem ? null : (
-                            <div className="propertiesView-presTrails">
+                            <div className="propertiesView-presentationTrails">
                                 <div
-                                    className="propertiesView-presTrails-title"
+                                    className="propertiesView-presentationTrails-title"
                                     onPointerDown={action(() => (this.openPresVisibilityAndDuration = !this.openPresVisibilityAndDuration))}
                                     style={{ backgroundColor: this.openPresTransitions ? 'black' : '' }}>
                                     &nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={'rocket'} /> &nbsp; Visibilty
-                                    <div className="propertiesView-presTrails-title-icon">
+                                    <div className="propertiesView-presentationTrails-title-icon">
                                         <FontAwesomeIcon icon={this.openPresVisibilityAndDuration ? 'caret-down' : 'caret-right'} size="lg" />
                                     </div>
                                 </div>
-                                {this.openPresVisibilityAndDuration ? <div className="propertiesView-presTrails-content">{PresBox.Instance.visibiltyDurationDropdown}</div> : null}
+                                {this.openPresVisibilityAndDuration ? <div className="propertiesView-presentationTrails-content">{PresBox.Instance.visibiltyDurationDropdown}</div> : null}
                             </div>
                         )}
                         {!selectedItem ? null : (
-                            <div className="propertiesView-presTrails">
-                                <div className="propertiesView-presTrails-title" onPointerDown={action(() => (this.openPresProgressivize = !this.openPresProgressivize))} style={{ backgroundColor: this.openPresTransitions ? 'black' : '' }}>
+                            <div className="propertiesView-presentationTrails">
+                                <div className="propertiesView-presentationTrails-title" onPointerDown={action(() => (this.openPresProgressivize = !this.openPresProgressivize))} style={{ backgroundColor: this.openPresTransitions ? 'black' : '' }}>
                                     &nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={'rocket'} /> &nbsp; Progressivize
-                                    <div className="propertiesView-presTrails-title-icon">
+                                    <div className="propertiesView-presentationTrails-title-icon">
                                         <FontAwesomeIcon icon={this.openPresProgressivize ? 'caret-down' : 'caret-right'} size="lg" />
                                     </div>
                                 </div>
-                                {this.openPresProgressivize ? <div className="propertiesView-presTrails-content">{PresBox.Instance.progressivizeDropdown}</div> : null}
+                                {this.openPresProgressivize ? <div className="propertiesView-presentationTrails-content">{PresBox.Instance.progressivizeDropdown}</div> : null}
                             </div>
                         )}
                         {!selectedItem || (type !== DocumentType.VID && type !== DocumentType.AUDIO) ? null : (
-                            <div className="propertiesView-presTrails">
-                                <div className="propertiesView-presTrails-title" onPointerDown={action(() => (this.openSlideOptions = !this.openSlideOptions))} style={{ backgroundColor: this.openSlideOptions ? 'black' : '' }}>
+                            <div className="propertiesView-presentationTrails">
+                                <div className="propertiesView-presentationTrails-title" onPointerDown={action(() => (this.openSlideOptions = !this.openSlideOptions))} style={{ backgroundColor: this.openSlideOptions ? 'black' : '' }}>
                                     &nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={type === DocumentType.AUDIO ? 'file-audio' : 'file-video'} /> &nbsp; {type === DocumentType.AUDIO ? 'Audio Options' : 'Video Options'}
-                                    <div className="propertiesView-presTrails-title-icon">
+                                    <div className="propertiesView-presentationTrails-title-icon">
                                         <FontAwesomeIcon icon={this.openSlideOptions ? 'caret-down' : 'caret-right'} size="lg" />
                                     </div>
                                 </div>
-                                {this.openSlideOptions ? <div className="propertiesView-presTrails-content">{PresBox.Instance.mediaOptionsDropdown}</div> : null}
+                                {this.openSlideOptions ? <div className="propertiesView-presentationTrails-content">{PresBox.Instance.mediaOptionsDropdown}</div> : null}
                             </div>
                         )}
                     </div>
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 63ff348e3..a757a0715 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -27,8 +27,8 @@ import './StyleProvider.scss';
 import React = require('react');
 
 export enum StyleProp {
-    TreeViewIcon = 'treeViewIcon',
-    TreeViewSortings = 'treeViewSortings', // options for how to sort tree view items
+    TreeViewIcon = 'treeView_Icon',
+    TreeViewSortings = 'treeView_Sortings', // options for how to sort tree view items
     DocContents = 'docContents', // when specified, the JSX returned will replace the normal rendering of the document view
     Opacity = 'opacity', // opacity of the document view
     BoxShadow = 'boxShadow', // box shadow - used for making collections standout and for showing clusters in free form views
@@ -133,7 +133,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
             return undefined;
         case StyleProp.DocContents:return undefined;
         case StyleProp.WidgetColor:return isAnnotated ? Colors.LIGHT_BLUE : darkScheme() ? 'lightgrey' : 'dimgrey';
-        case StyleProp.Opacity:    return props?.LayoutTemplateString?.includes(KeyValueBox.name) ? 1 : doc?.textInlineAnnotations ? 0 :  Cast(doc?._opacity, "number", Cast(doc?.opacity, 'number', null));
+        case StyleProp.Opacity:    return props?.LayoutTemplateString?.includes(KeyValueBox.name) ? 1 : doc?.text_inlineAnnotations ? 0 :  Cast(doc?._opacity, "number", Cast(doc?.opacity, 'number', null));
         case StyleProp.HideLinkBtn:return props?.hideLinkButton || (!selected && doc?.layout_hideLinkButton);
         case StyleProp.FontSize:   return StrCast(doc?.[fieldKey + 'fontSize'],   StrCast(doc?._text_fontSize,   StrCast(Doc.UserDoc().fontSize)));
         case StyleProp.FontFamily: return StrCast(doc?.[fieldKey + 'fontFamily'], StrCast(doc?._text_fontFamily, StrCast(Doc.UserDoc().fontFamily)));
@@ -144,7 +144,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
         case StyleProp.ShowTitle:
             return (
                 (doc &&
-                    !doc.presentationTargetDoc &&
+                    !doc.presentation_targetDoc &&
                     !props?.LayoutTemplateString?.includes(KeyValueBox.name) &&
                     props?.layout_showTitle?.() !== '' &&
                     StrCast(
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 5c2ab3f70..a3884c9eb 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -102,7 +102,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
         const templateMenu: Array<JSX.Element> = [];
         this.props.templates?.forEach((checked, template) => templateMenu.push(<TemplateToggle key={template} template={template} checked={checked} toggle={this.toggleTemplate} />));
         templateMenu.push(<OtherToggle key={'default'} name={'Default'} checked={templateName === 'layout'} toggle={this.toggleDefault} />);
-        addedTypes.concat(noteTypes).map(template => (template.treeViewChecked = this.templateIsUsed(firstDoc, template)));
+        addedTypes.concat(noteTypes).map(template => (template.treeView_Checked = this.templateIsUsed(firstDoc, template)));
         this._addedKeys &&
             Array.from(this._addedKeys)
                 .filter(key => !noteTypes.some(nt => nt.title === key))
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 192d48c64..a8f5345b7 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -65,9 +65,9 @@ export class CollectionTimeView extends CollectionSubView() {
     @action
     scrollPreview = (docView: DocumentView, anchor: Doc, focusSpeed: number, options: DocFocusOptions) => {
         // if in preview, then override document's fields with view spec
-        this._focusFilters = StrListCast(anchor.presDocFilters);
-        this._focusRangeFilters = StrListCast(anchor.presPinDocRangeFilters);
-        this._focusPivotField = StrCast(anchor.presPivotField);
+        this._focusFilters = StrListCast(anchor.config_docFilters);
+        this._focusRangeFilters = StrListCast(anchor.config_docRangeFilters);
+        this._focusPivotField = StrCast(anchor.config_pivotField);
         return undefined;
     };
 
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index ea3b5065f..b5c7d3f5d 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -66,17 +66,17 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
         return this.props.DataDoc || this.doc;
     }
     @computed get treeViewtruncateTitleWidth() {
-        return NumCast(this.doc.treeViewTruncateTitleWidth, this.panelWidth());
+        return NumCast(this.doc.treeView_TruncateTitleWidth, this.panelWidth());
     }
     @computed get treeChildren() {
         TraceMobx();
         return this.props.childDocuments || this.childDocs;
     }
     @computed get outlineMode() {
-        return this.doc.treeViewType === TreeViewType.outline;
+        return this.doc.treeView_Type === TreeViewType.outline;
     }
     @computed get fileSysMode() {
-        return this.doc.treeViewType === TreeViewType.fileSystem;
+        return this.doc.treeView_Type === TreeViewType.fileSystem;
     }
     @computed get dashboardMode() {
         return this.doc === Doc.MyDashboards;
@@ -191,9 +191,9 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
         // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
         if (!Doc.noviceMode) {
             const layoutItems: ContextMenuProps[] = [];
-            layoutItems.push({ description: 'Make tree state ' + (this.doc.treeViewOpenIsTransient ? 'persistent' : 'transient'), event: () => (this.doc.treeViewOpenIsTransient = !this.doc.treeViewOpenIsTransient), icon: 'paint-brush' });
-            layoutItems.push({ description: (this.doc.treeViewHideHeaderFields ? 'Show' : 'Hide') + ' Header Fields', event: () => (this.doc.treeViewHideHeaderFields = !this.doc.treeViewHideHeaderFields), icon: 'paint-brush' });
-            layoutItems.push({ description: (this.doc.treeViewHideTitle ? 'Show' : 'Hide') + ' Title', event: () => (this.doc.treeViewHideTitle = !this.doc.treeViewHideTitle), icon: 'paint-brush' });
+            layoutItems.push({ description: 'Make tree state ' + (this.doc.treeView_OpenIsTransient ? 'persistent' : 'transient'), event: () => (this.doc.treeView_OpenIsTransient = !this.doc.treeView_OpenIsTransient), icon: 'paint-brush' });
+            layoutItems.push({ description: (this.doc.treeView_HideHeaderFields ? 'Show' : 'Hide') + ' Header Fields', event: () => (this.doc.treeView_HideHeaderFields = !this.doc.treeView_HideHeaderFields), icon: 'paint-brush' });
+            layoutItems.push({ description: (this.doc.treeView_HideTitle ? 'Show' : 'Hide') + ' Title', event: () => (this.doc.treeView_HideTitle = !this.doc.treeView_HideTitle), icon: 'paint-brush' });
             ContextMenu.Instance.addItem({ description: 'Options...', subitems: layoutItems, icon: 'eye' });
             const existingOnClick = ContextMenu.Instance.findByDescription('OnClick...');
             const onClicks: ContextMenuProps[] = existingOnClick && 'subitems' in existingOnClick ? existingOnClick.subitems : [];
@@ -215,7 +215,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                 height={'auto'}
                 GetValue={() => StrCast(this.dataDoc.title)}
                 SetValue={undoBatch((value: string, shift: boolean, enter: boolean) => {
-                    if (enter && this.props.Document.treeViewType === TreeViewType.outline) this.makeTextCollection(this.treeChildren);
+                    if (enter && this.props.Document.treeView_Type === TreeViewType.outline) this.makeTextCollection(this.treeChildren);
                     this.dataDoc.title = value;
                     return true;
                 })}
@@ -261,7 +261,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
         const icons = StrListCast(this.doc.childContextMenuIcons);
         return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label }));
     };
-    headerFields = () => this.props.treeViewHideHeaderFields || BoolCast(this.doc.treeViewHideHeaderFields);
+    headerFields = () => this.props.treeViewHideHeaderFields || BoolCast(this.doc.treeView_HideHeaderFields);
     @observable _renderCount = 1;
     @computed get treeViewElements() {
         TraceMobx();
@@ -389,7 +389,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
         const background = () => this.props.styleProvider?.(this.doc, this.props, StyleProp.BackgroundColor);
         const color = () => this.props.styleProvider?.(this.doc, this.props, StyleProp.Color);
         const pointerEvents = () => (this.props.isContentActive() === false ? 'none' : undefined);
-        const titleBar = this.props.treeViewHideTitle || this.doc.treeViewHideTitle ? null : this.titleBar;
+        const titleBar = this.props.treeViewHideTitle || this.doc.treeView_HideTitle ? null : this.titleBar;
         return (
             <div style={{ display: 'flex', flexDirection: 'column', height: '100%', pointerEvents: 'all' }}>
                 {!this.buttonMenu && !this.noviceExplainer ? null : (
@@ -401,7 +401,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                 <div
                     className="collectionTreeView-contents"
                     key="tree"
-                    ref={r => !this.doc.treeViewHasOverlay && r && this.createTreeDropTarget(r)}
+                    ref={r => !this.doc.treeView_HasOverlay && r && this.createTreeDropTarget(r)}
                     style={{
                         ...(!titleBar ? { marginLeft: this.marginX(), paddingTop: this.marginTop() } : {}),
                         color: color(),
@@ -440,7 +440,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
         const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1) || 1;
         return (
             <div style={{ transform: `scale(${scale})`, transformOrigin: 'top left', width: `${100 / scale}%`, height: `${100 / scale}%` }}>
-                {!(this.doc instanceof Doc) || !this.treeChildren ? null : this.doc.treeViewHasOverlay ? (
+                {!(this.doc instanceof Doc) || !this.treeChildren ? null : this.doc.treeView_HasOverlay ? (
                     <CollectionFreeFormView
                         {...this.props}
                         setContentView={emptyFunction}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index c33519afb..88f892efc 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -41,7 +41,7 @@ interface CollectionViewProps_ extends FieldViewProps {
     isAnnotationOverlayScrollable?: boolean; // whether the annotation overlay can be vertically scrolled (just for tree views, currently)
     layoutEngine?: () => string;
     setPreviewCursor?: (func: (x: number, y: number, drag: boolean, hide: boolean) => void) => void;
-    setBrushViewer?: (func?: (view: { width: number; height: number; panX: number; panY: number }) => void) => void;
+    setBrushViewer?: (func?: (view: { width: number; height: number; panX: number; panY: number }, transTime: number) => void) => void;
     ignoreUnrendered?: boolean;
 
     // property overrides for child documents
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index d787f5262..ea473d5cf 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -256,46 +256,47 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 return;
             }
             const anchorDoc = DocumentManager.Instance.getDocumentView(doc)?.ComponentView?.getAnchor?.(false, pinProps);
-            const pinDoc = Doc.MakeDelegate(anchorDoc && anchorDoc !== doc ? anchorDoc : doc);
-            pinDoc.presentationTargetDoc = anchorDoc ?? doc;
+            const pinDoc = anchorDoc?.type === DocumentType.CONFIG ? anchorDoc : Doc.MakeDelegate(anchorDoc && anchorDoc !== doc ? anchorDoc : doc);
+            pinDoc.presentation_targetDoc = anchorDoc ?? doc;
             pinDoc.title = doc.title + ' - Slide';
             pinDoc.data = new List<Doc>(); // the children of the embedding's layout are the presentation slide children.  the embedding's data field might be children of a collection, PDF data, etc -- in any case we don't want the tree view to "see" this data
-            pinDoc.presMovement = doc.type === DocumentType.SCRIPTING || pinProps?.pinDocLayout ? PresMovement.None : PresMovement.Zoom;
-            pinDoc.presDuration = pinDoc.presDuration ?? 1000;
-            pinDoc.groupWithUp = false;
+            pinDoc.presentation_movement = doc.type === DocumentType.SCRIPTING || pinProps?.pinDocLayout ? PresMovement.None : PresMovement.Zoom;
+            pinDoc.presentation_duration = pinDoc.presentation_duration ?? 1000;
+            pinDoc.presentation_groupWithUp = false;
             Doc.SetContainer(pinDoc, curPres);
             // these should potentially all be props passed down by the CollectionTreeView to the TreeView elements.  That way the PresBox could configure all of its children at render time
-            pinDoc.treeViewRenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area
-            pinDoc.treeViewHeaderWidth = '100%'; // forces the header to grow to be the same size as its largest sibling.
-            pinDoc.treeViewChildrenOnRoot = true; // tree view will look for hierarchical children on the root doc, not the data doc.
-            pinDoc.treeViewFieldKey = '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.treeViewExpandedView = '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.treeViewHideHeaderIfTemplate = true; // this will force the document to render itself as the tree view header
+            pinDoc.treeView = ''; // not really needed, but makes key value pane look better
+            pinDoc.treeView_RenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area
+            pinDoc.treeView_HeaderWidth = '100%'; // forces the header to grow to be the same size as its largest sibling.
+            pinDoc.treeView_ChildrenOnRoot = true; // tree view will look for hierarchical children on the root doc, not the data doc.
+            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);
 
             if (pinProps.pinViewport) PresBox.pinDocView(pinDoc, pinProps, anchorDoc ?? doc);
             if (!pinProps?.audioRange && duration !== undefined) {
                 pinDoc.mediaStart = 'manual';
                 pinDoc.mediaStop = 'manual';
-                pinDoc.presStartTime = NumCast(doc.clipStart);
-                pinDoc.presEndTime = NumCast(doc.clipEnd, duration);
+                pinDoc.config_clipStart = NumCast(doc.clipStart);
+                pinDoc.config_clipEnd = NumCast(doc.clipEnd, duration);
             }
             if (pinProps?.activeFrame !== undefined) {
-                pinDoc.presActiveFrame = pinProps?.activeFrame;
+                pinDoc.config_activeFrame = pinProps?.activeFrame;
                 pinDoc.title = doc.title + ' (move)';
-                pinDoc.presMovement = PresMovement.Pan;
+                pinDoc.presentation_movement = PresMovement.Pan;
             }
             if (pinProps?.currentFrame !== undefined) {
-                pinDoc.presCurrentFrame = pinProps?.currentFrame;
+                pinDoc.config_currentFrame = pinProps?.currentFrame;
                 pinDoc.title = doc.title + ' (move)';
-                pinDoc.presMovement = PresMovement.Pan;
+                pinDoc.presentation_movement = PresMovement.Pan;
             }
             if (pinDoc.stroke_isInkMask) {
-                pinDoc.presHideAfter = true;
-                pinDoc.presHideBefore = true;
-                pinDoc.presMovement = PresMovement.None;
+                pinDoc.presentation_hideAfter = true;
+                pinDoc.presentation_hideBefore = true;
+                pinDoc.presentation_movement = PresMovement.None;
             }
-            if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true;
+            if (curPres.expandBoolean) pinDoc.presentation_expandInlineButton = true;
             Doc.AddDocToList(curPres, 'data', pinDoc, PresBox.Instance?.sortArray()?.lastElement());
             PresBox.Instance?.clearSelectedArray();
             pinDoc && PresBox.Instance?.addToSelectedArray(pinDoc); //Update selected array
@@ -393,7 +394,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     };
 
     getCurrentFrame = () => {
-        return NumCast(Cast(PresBox.Instance.activeItem.presentationTargetDoc, Doc, null)._currentFrame);
+        return NumCast(Cast(PresBox.Instance.activeItem.presentation_targetDoc, Doc, null)._currentFrame);
     };
     static Activate = (tabDoc: Doc) => {
         const tab = Array.from(CollectionDockingView.Instance?.tabMap!).find(tab => tab.DashDoc === tabDoc && !tab.contentItem.config.props.keyValue);
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 25a547066..27f9ebc49 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -87,8 +87,8 @@ export enum TreeSort {
  * Renders a treeView of a collection of documents
  *
  * special fields:
- * treeViewOpen : flag denoting whether the documents sub-tree (contents) is visible or hidden
- * treeViewExpandedView : name of field whose contents are being displayed as the document's subtree
+ * treeView_Open : flag denoting whether the documents sub-tree (contents) is visible or hidden
+ * treeView_ExpandedView : name of field whose contents are being displayed as the document's subtree
  */
 @observer
 export class TreeView extends React.Component<TreeViewProps> {
@@ -104,16 +104,16 @@ export class TreeView extends React.Component<TreeViewProps> {
     private _treedropDisposer?: DragManager.DragDropDisposer;
 
     get treeViewOpenIsTransient() {
-        return this.props.treeView.doc.treeViewOpenIsTransient || Doc.IsDataProto(this.doc);
+        return this.props.treeView.doc.treeView_OpenIsTransient || Doc.IsDataProto(this.doc);
     }
     set treeViewOpen(c: boolean) {
         if (this.treeViewOpenIsTransient) this._transientOpenState = c;
         else {
-            this.doc.treeViewOpen = c;
+            this.doc.treeView_Open = c;
             this._transientOpenState = false;
         }
     }
-    @observable _transientOpenState = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state
+    @observable _transientOpenState = false; // override of the treeView_Open field allowing the display state to be independent of the document's state
     @observable _editTitle: boolean = false;
     @observable _dref: DocumentView | undefined | null;
     get displayName() {
@@ -132,29 +132,29 @@ export class TreeView extends React.Component<TreeViewProps> {
             ? this.fieldKey
             : Doc.noviceMode
             ? 'layout'
-            : StrCast(this.props.treeView.doc.treeViewExpandedView, 'fields');
+            : StrCast(this.props.treeView.doc.treeView_ExpandedView, 'fields');
     }
 
     @computed get doc() {
         return this.props.document;
     }
     @computed get treeViewOpen() {
-        return (!this.treeViewOpenIsTransient && Doc.GetT(this.doc, 'treeViewOpen', 'boolean', true)) || this._transientOpenState;
+        return (!this.treeViewOpenIsTransient && Doc.GetT(this.doc, 'treeView_Open', 'boolean', true)) || this._transientOpenState;
     }
     @computed get treeViewExpandedView() {
-        return this.validExpandViewTypes.includes(StrCast(this.doc.treeViewExpandedView)) ? StrCast(this.doc.treeViewExpandedView) : this.defaultExpandedView;
+        return this.validExpandViewTypes.includes(StrCast(this.doc.treeView_ExpandedView)) ? StrCast(this.doc.treeView_ExpandedView) : this.defaultExpandedView;
     }
     @computed get MAX_EMBED_HEIGHT() {
         return NumCast(this.props.treeViewParent.maxEmbedHeight, 200);
     }
     @computed get dataDoc() {
-        return this.props.document.treeViewChildrenOnRoot ? this.doc : this.doc[DocData];
+        return this.props.document.treeView_ChildrenOnRoot ? this.doc : this.doc[DocData];
     }
     @computed get layoutDoc() {
         return Doc.Layout(this.doc);
     }
     @computed get fieldKey() {
-        return StrCast(this.doc._treeViewFieldKey, Doc.LayoutFieldKey(this.doc));
+        return StrCast(this.doc._treeView_FieldKey, Doc.LayoutFieldKey(this.doc));
     }
     @computed get childDocs() {
         return this.childDocList(this.fieldKey);
@@ -308,13 +308,13 @@ export class TreeView extends React.Component<TreeViewProps> {
         const bullet = Docs.Create.TextDocument('', {
             layout: CollectionView.LayoutString('data'),
             title: '-title-',
-            treeViewExpandedViewLock: true,
-            treeViewExpandedView: 'data',
+            treeView_ExpandedViewLock: true,
+            treeView_ExpandedView: 'data',
             _type_collection: CollectionViewType.Tree,
             layout_hideLinkButton: true,
             _layout_showSidebar: true,
             _layout_fitWidth: true,
-            treeViewType: TreeViewType.outline,
+            treeView_Type: TreeViewType.outline,
             x: 0,
             y: 0,
             _xMargin: 0,
@@ -395,7 +395,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         };
         const addDoc = inside ? localAdd : parentAddDoc;
         const move = (!dropAction || dropAction === 'proto' || dropAction === 'move' || dropAction === 'same') && moveDocument;
-        const canAdd = (!this.props.treeView.outlineMode && !StrCast((inside ? this.props.document : this.props.treeViewParent)?.treeViewFreezeChildren).includes('add')) || forceAdd;
+        const canAdd = (!this.props.treeView.outlineMode && !StrCast((inside ? this.props.document : this.props.treeViewParent)?.treeView_FreezeChildren).includes('add')) || forceAdd;
         if (canAdd) {
             this.props.parentTreeView instanceof TreeView && (this.props.parentTreeView.dropping = true);
             const res = droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) || (dropAction === 'proto' ? addDoc(d) : false) : addDoc(d)) || added, false);
@@ -438,7 +438,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         doc && Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key));
 
         for (const key of Object.keys(ids).slice().sort()) {
-            if (this.props.skipFields?.includes(key) || key === 'title' || key === 'treeViewOpen') continue;
+            if (this.props.skipFields?.includes(key) || key === 'title' || key === 'treeView_Open') continue;
             const contents = doc[key];
             let contentElement: (JSX.Element | null)[] | JSX.Element = [];
 
@@ -538,7 +538,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         const expandKey = this.treeViewExpandedView;
         const sortings = (this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewSortings) as { [key: string]: { color: string; icon: JSX.Element | string } }) ?? {};
         if (['links', 'annotations', 'embeddings', this.fieldKey].includes(expandKey)) {
-            const sorting = StrCast(this.doc.treeViewSortCriterion, TreeSort.None);
+            const sorting = StrCast(this.doc.treeView_SortCriterion, TreeSort.None);
             const sortKeys = Object.keys(sortings);
             const curSortIndex = Math.max(
                 0,
@@ -550,7 +550,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             const localAdd = (doc: Doc, addBefore?: Doc, before?: boolean) => {
                 // if there's a sort ordering specified that can be modified on drop (eg, zorder can be modified, alphabetical can't),
                 // then the modification would be done here
-                const ordering = StrCast(this.doc.treeViewSortCriterion);
+                const ordering = StrCast(this.doc.treeView_SortCriterion);
                 if (ordering === TreeSort.Zindex) {
                     const docs = TreeView.sortDocs(this.childDocs || ([] as Doc[]), ordering);
                     doc.zIndex = addBefore ? NumCast(addBefore.zIndex) + (before ? -0.5 : 0.5) : 1000;
@@ -590,7 +590,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                                 }}
                                 onClick={undoable(e => {
                                     if (this.props.isContentActive() && Math.abs(e.clientX - downX) < 3 && Math.abs(e.clientY - downY) < 3) {
-                                        !this.props.treeView.outlineMode && (this.doc.treeViewSortCriterion = sortKeys[(curSortIndex + 1) % sortKeys.length]);
+                                        !this.props.treeView.outlineMode && (this.doc.treeView_SortCriterion = sortKeys[(curSortIndex + 1) % sortKeys.length]);
                                         e.stopPropagation();
                                     }
                                 }, 'sort order')}
@@ -601,7 +601,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                         style={{ cursor: 'inherit' }}
                         key={expandKey + 'more'}
                         title="click to change sort order"
-                        className={''} //this.doc.treeViewHideTitle ? 'no-indent' : ''}
+                        className={''} //this.doc.treeView_HideTitle ? 'no-indent' : ''}
                         onPointerDown={e => {
                             downX = e.clientX;
                             downY = e.clientY;
@@ -609,7 +609,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                         }}
                         onClick={undoable(e => {
                             if (this.props.isContentActive() && Math.abs(e.clientX - downX) < 3 && Math.abs(e.clientY - downY) < 3) {
-                                !this.props.treeView.outlineMode && (this.doc.treeViewSortCriterion = sortKeys[(curSortIndex + 1) % sortKeys.length]);
+                                !this.props.treeView.outlineMode && (this.doc.treeView_SortCriterion = sortKeys[(curSortIndex + 1) % sortKeys.length]);
                                 e.stopPropagation();
                             }
                         }, 'sort order')}>
@@ -683,7 +683,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                 {
                     this: this.doc.isTemplateForField && this.props.dataDoc ? this.props.dataDoc : this.doc,
                     heading: this.props.treeViewParent.title,
-                    checked: this.doc.treeViewChecked === 'check' ? 'x' : this.doc.treeViewChecked === 'x' ? 'remove' : 'check',
+                    checked: this.doc.treeView_Checked === 'check' ? 'x' : this.doc.treeView_Checked === 'x' ? 'remove' : 'check',
                     containingTreeView: this.props.treeView.props.Document,
                 },
                 console.log
@@ -698,7 +698,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         TraceMobx();
         const iconType = this.props.treeView.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewIcon + (this.treeViewOpen ? ':open' : !this.childDocs.length ? ':empty' : '')) || 'question';
         const color = StrCast(Doc.UserDoc().userColor);
-        const checked = this.onCheckedClick ? this.doc.treeViewChecked ?? 'unchecked' : undefined;
+        const checked = this.onCheckedClick ? this.doc.treeView_Checked ?? 'unchecked' : undefined;
         return (
             <div
                 className={`bullet${this.props.treeView.outlineMode ? '-outline' : ''}`}
@@ -748,9 +748,9 @@ export class TreeView extends React.Component<TreeViewProps> {
     }
     @action
     expandNextviewType = () => {
-        if (this.treeViewOpen && !this.doc.isFolder && !this.props.treeView.outlineMode && !this.doc.treeViewExpandedViewLock) {
+        if (this.treeViewOpen && !this.doc.isFolder && !this.props.treeView.outlineMode && !this.doc.treeView_ExpandedViewLock) {
             const next = (modes: any[]) => modes[(modes.indexOf(StrCast(this.treeViewExpandedView)) + 1) % modes.length];
-            this.doc.treeViewExpandedView = next(this.validExpandViewTypes);
+            this.doc.treeView_ExpandedView = next(this.validExpandViewTypes);
         }
         this.treeViewOpen = true;
     };
@@ -759,7 +759,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     @computed get titleButtons() {
         const customHeaderButtons = this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.Decorations);
         const color = StrCast(Doc.UserDoc().userColor);
-        return this.props.treeViewHideHeaderFields() || this.doc.treeViewHideHeaderFields ? null : (
+        return this.props.treeViewHideHeaderFields() || this.doc.treeView_HideHeaderFields ? null : (
             <>
                 {customHeaderButtons} {/* e.g.,. hide button is set by dashboardStyleProvider */}
                 <IconButton
@@ -771,7 +771,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                         e.stopPropagation();
                     }}
                 />
-                {Doc.noviceMode ? null : this.doc.treeViewExpandedViewLock || Doc.IsSystem(this.doc) ? null : (
+                {Doc.noviceMode ? null : this.doc.treeView_ExpandedViewLock || Doc.IsSystem(this.doc) ? null : (
                     <span className="collectionTreeView-keyHeader" title="type of expanded data" key={this.treeViewExpandedView} onPointerDown={this.expandNextviewType}>
                         {this.treeViewExpandedView}
                     </span>
@@ -815,7 +815,7 @@ export class TreeView extends React.Component<TreeViewProps> {
 
     onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!);
 
-    onChildDoubleClick = () => ScriptCast(this.props.treeView.Document.treeViewChildDoubleClick, !this.props.treeView.outlineMode ? this._openScript?.() : null);
+    onChildDoubleClick = () => ScriptCast(this.props.treeView.Document.treeView_ChildDoubleClick, !this.props.treeView.outlineMode ? this._openScript?.() : null);
 
     refocus = () => this.props.treeView.props.focus(this.props.treeView.props.Document, {});
     ignoreEvent = (e: any) => {
@@ -861,7 +861,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         return this.props?.treeView?.props.styleProvider?.(doc, props, property); // properties are inherited from the CollectionTreeView, not the hierarchical parent in the treeView
     };
     onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
-        if (this.doc.treeViewHideHeader || (this.doc.treeViewHideHeaderIfTemplate && this.props.treeView.props.childLayoutTemplate?.()) || this.props.treeView.outlineMode) {
+        if (this.doc.treeView_HideHeader || (this.doc.treeView_HideHeaderIfTemplate && this.props.treeView.props.childLayoutTemplate?.()) || this.props.treeView.outlineMode) {
             switch (e.key) {
                 case 'Tab':
                     e.stopPropagation?.();
@@ -1068,7 +1068,7 @@ export class TreeView extends React.Component<TreeViewProps> {
 
     // renders the text version of a document as the header.  This is used in the file system mode and in other vanilla tree views.
     @computed get renderTitleAsHeader() {
-        return this.props.treeView.Document.treeViewHideUnrendered && this.doc.layout_unrendered && !this.doc.treeViewFieldKey ? (
+        return this.props.treeView.Document.treeView_HideUnrendered && this.doc.layout_unrendered && !this.doc.treeView_FieldKey ? (
             <div></div>
         ) : (
             <>
@@ -1089,7 +1089,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     };
 
     @computed get renderBorder() {
-        const sorting = StrCast(this.doc.treeViewSortCriterion, TreeSort.None);
+        const sorting = StrCast(this.doc.treeView_SortCriterion, TreeSort.None);
         const sortings = (this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewSortings) ?? {}) as { [key: string]: { color: string; label: string } };
         return (
             <div className={`treeView-border${this.props.treeView.outlineMode ? TreeViewType.outline : ''}`} style={{ borderColor: sortings[sorting]?.color }}>
@@ -1109,7 +1109,7 @@ export class TreeView extends React.Component<TreeViewProps> {
 
     render() {
         TraceMobx();
-        const hideTitle = this.doc.treeViewHideHeader || (this.doc.treeViewHideHeaderIfTemplate && this.props.treeView.props.childLayoutTemplate?.()) || this.props.treeView.outlineMode;
+        const hideTitle = this.doc.treeView_HideHeader || (this.doc.treeView_HideHeaderIfTemplate && this.props.treeView.props.childLayoutTemplate?.()) || this.props.treeView.outlineMode;
         return this.props.renderedIds?.indexOf(this.doc[Id]) !== -1 ? (
             '<' + this.doc.title + '>' // just print the title of documents we've previously rendered in this hierarchical path to avoid cycles
         ) : (
@@ -1121,9 +1121,9 @@ export class TreeView extends React.Component<TreeViewProps> {
                 // onKeyDown={this.onKeyDown}
             >
                 <li className="collection-child">
-                    {hideTitle && this.doc.type !== DocumentType.RTF && !this.doc.treeViewRenderAsBulletHeader // should test for prop 'treeViewRenderDocWithBulletAsHeader"
+                    {hideTitle && this.doc.type !== DocumentType.RTF && !this.doc.treeView_RenderAsBulletHeader // should test for prop 'treeView_RenderDocWithBulletAsHeader"
                         ? this.renderEmbeddedDocument(false, returnFalse)
-                        : this.renderBulletHeader(hideTitle ? this.renderDocumentAsHeader(!this.doc.treeViewRenderAsBulletHeader) : this.renderTitleAsHeader, this._editTitle)}
+                        : this.renderBulletHeader(hideTitle ? this.renderDocumentAsHeader(!this.doc.treeView_RenderAsBulletHeader) : this.renderTitleAsHeader, this._editTitle)}
                 </li>
             </div>
         );
@@ -1162,7 +1162,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         childDocs: Doc[],
         treeView: CollectionTreeView,
         parentTreeView: CollectionTreeView | TreeView | undefined,
-        treeViewParent: Doc,
+        treeView_Parent: Doc,
         dataDoc: Doc | undefined,
         parentCollectionDoc: Doc | undefined,
         containerPrevSibling: Doc | undefined,
@@ -1176,7 +1176,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         isContentActive: (outsideReaction?: boolean) => boolean,
         panelWidth: () => number,
         renderDepth: number,
-        treeViewHideHeaderFields: () => boolean,
+        treeView_HideHeaderFields: () => boolean,
         renderedIds: string[],
         onCheckedClick: undefined | (() => ScriptField),
         onChildClick: undefined | (() => ScriptField),
@@ -1193,19 +1193,19 @@ export class TreeView extends React.Component<TreeViewProps> {
         hierarchyIndex?: number[],
         renderCount?: number
     ) {
-        const viewSpecScript = Cast(treeViewParent.viewSpecScript, ScriptField);
+        const viewSpecScript = Cast(treeView_Parent.viewSpecScript, ScriptField);
         if (viewSpecScript) {
             childDocs = childDocs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result);
         }
 
-        const docs = TreeView.sortDocs(childDocs, StrCast(treeViewParent.treeViewSortCriterion, TreeSort.None));
+        const docs = TreeView.sortDocs(childDocs, StrCast(treeView_Parent.treeView_SortCriterion, TreeSort.None));
         const rowWidth = () => panelWidth() - treeBulletWidth() * (treeView.props.NativeDimScaling?.() || 1);
-        const treeViewRefs = new Map<Doc, TreeView | undefined>();
+        const treeView_Refs = new Map<Doc, TreeView | undefined>();
         return docs
             .filter(child => child instanceof Doc)
             .map((child, i) => {
                 if (renderCount && i > renderCount) return null;
-                const pair = Doc.GetLayoutDataDocPair(treeViewParent, dataDoc, child);
+                const pair = Doc.GetLayoutDataDocPair(treeView_Parent, dataDoc, child);
                 if (!pair.layout || pair.data instanceof Promise) {
                     return null;
                 }
@@ -1218,11 +1218,11 @@ export class TreeView extends React.Component<TreeViewProps> {
                         FormattedTextBox.SelectOnLoad = child[Id];
                         TreeView._editTitleOnLoad = editTitle ? { id: child[Id], parent } : undefined;
                         Doc.AddDocToList(newParent, fieldKey, child, addAfter, false);
-                        newParent.treeViewOpen = true;
+                        newParent.treeView_Open = true;
                         Doc.SetContainer(child, treeView.Document);
                     }
                 };
-                const indent = i === 0 ? undefined : (editTitle: boolean) => dentDoc(editTitle, docs[i - 1], undefined, treeViewRefs.get(docs[i - 1]));
+                const indent = i === 0 ? undefined : (editTitle: boolean) => dentDoc(editTitle, docs[i - 1], undefined, treeView_Refs.get(docs[i - 1]));
                 const outdent = !parentCollectionDoc ? undefined : (editTitle: boolean) => dentDoc(editTitle, parentCollectionDoc, containerPrevSibling, parentTreeView instanceof TreeView ? parentTreeView.props.parentTreeView : undefined);
                 const addDocument = (doc: Doc | Doc[], annotationKey?: string, relativeTo?: Doc, before?: boolean) => add(doc, relativeTo ?? docs[i], before !== undefined ? before : false);
                 const childLayout = Doc.Layout(pair.layout);
@@ -1233,10 +1233,10 @@ export class TreeView extends React.Component<TreeViewProps> {
                 return (
                     <TreeView
                         key={child[Id]}
-                        ref={r => treeViewRefs.set(child, r ? r : undefined)}
+                        ref={r => treeView_Refs.set(child, r ? r : undefined)}
                         document={pair.layout}
                         dataDoc={pair.data}
-                        treeViewParent={treeViewParent}
+                        treeViewParent={treeView_Parent}
                         prevSibling={docs[i]}
                         // TODO: [AL] add these
                         hierarchyIndex={hierarchyIndex ? [...hierarchyIndex, i + 1] : undefined}
@@ -1248,7 +1248,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                         onCheckedClick={onCheckedClick}
                         onChildClick={onChildClick}
                         renderDepth={renderDepth}
-                        removeDoc={StrCast(treeViewParent.treeViewFreezeChildren).includes('remove') ? undefined : remove}
+                        removeDoc={StrCast(treeView_Parent.treeView_FreezeChildren).includes('remove') ? undefined : remove}
                         addDocument={addDocument}
                         styleProvider={styleProvider}
                         panelWidth={rowWidth}
@@ -1259,7 +1259,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                         addDocTab={addDocTab}
                         ScreenToLocalTransform={screenToLocalXf}
                         isContentActive={isContentActive}
-                        treeViewHideHeaderFields={treeViewHideHeaderFields}
+                        treeViewHideHeaderFields={treeView_HideHeaderFields}
                         renderedIds={renderedIds}
                         skipFields={skipFields}
                         firstLevel={firstLevel}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index c6419885b..e4ae251c8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -153,7 +153,12 @@
     overflow-y: auto;
     overflow-x: hidden;
 }
-
+.collectionFreeFormView-brushView {
+    pointer-events: none;
+    position: absolute;
+    transition: opacity 0.5s;
+    z-index: 1000;
+}
 .collectionfreeformview-container {
     // touch action none means that the browser will handle none of the touch actions. this allows us to implement our own actions.
     touch-action: none;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index e1455525e..ffcf0999c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -319,7 +319,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         // focus on the document in the collection
         const didMove = !cantTransform && !anchor.z && (panX !== savedState.panX || panY !== savedState.panY || scale !== savedState.scale);
         if (didMove) options.didMove = true;
-        // glr: freeform transform speed can be set by adjusting presTransition field - needs a way of knowing when presentation is not active...
+        // glr: freeform transform speed can be set by adjusting presentation_transition field - needs a way of knowing when presentation is not active...
         if (didMove) {
             const focusTime = options?.instant ? 0 : options.zoomTime ?? 500;
             (options.zoomScale ?? options.willZoomCentered) && scale && (this.Document[this.scaleFieldKey] = scale);
@@ -681,6 +681,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             case GestureUtils.Gestures.Stroke:
                 const points = ge.points;
                 const B = this.getTransform().transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
+                console.log(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
                 const inkDoc = Docs.Create.InkDocument(
                     ActiveInkColor(),
                     ActiveInkWidth() * this.props.ScreenToLocalTransform().Scale,
@@ -1050,7 +1051,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     onPointerWheel = (e: React.WheelEvent): void => {
         if (this.Document._isGroup || !this.isContentActive()) return; // group style collections neither pan nor zoom
         PresBox.Instance?.pauseAutoPres();
-        if (this.layoutDoc._Transform || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return;
+        if (this.layoutDoc._Transform || this.props.Document.treeView_OutlineMode === TreeViewType.outline) return;
         e.stopPropagation();
         const docHeight = NumCast(this.rootDoc[Doc.LayoutFieldKey(this.rootDoc) + '_nativeHeight'], this.nativeHeight);
         const scrollable = NumCast(this.layoutDoc[this.scaleFieldKey], 1) === 1 && docHeight > this.props.PanelHeight() / this.nativeDimScaling + 1e-4;
@@ -1318,7 +1319,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 layout_showTitle={this.props.childlayout_showTitle}
                 dontRegisterView={this.props.dontRenderDocuments || this.props.dontRegisterView}
                 pointerEvents={this.pointerEvents}
-                //fitContentsToBox={this.props.fitContentsToBox || BoolCast(this.props.treeViewFreezeChildDimensions)} // bcz: check this
+                //fitContentsToBox={this.props.fitContentsToBox || BoolCast(this.props.treeView_FreezeChildDimensions)} // bcz: check this
             />
         );
     }
@@ -1537,7 +1538,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
         // create an anchor that saves information about the current state of the freeform view (pan, zoom, view type)
-        const anchor = Docs.Create.ConfigDocument({ title: 'ViewSpec - ' + StrCast(this.layoutDoc._type_collection), layout_unrendered: true, presTransition: 500, annotationOn: this.rootDoc });
+        const anchor = Docs.Create.ConfigDocument({ title: 'ViewSpec - ' + StrCast(this.layoutDoc._type_collection), layout_unrendered: true, presentation_transition: 500, annotationOn: this.rootDoc });
         PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), pannable: !this.Document._isGroup, type_collection: true, filters: true } }, this.rootDoc);
 
         if (addAsAnnotation) {
@@ -1866,6 +1867,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     showPresPaths = () => (CollectionFreeFormView.ShowPresPaths ? PresBox.Instance.getPaths(this.rootDoc) : null);
 
+    brushedView = () => this._brushedView;
     @computed get marqueeView() {
         TraceMobx();
         return (
@@ -1908,13 +1910,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                         </div>
                     ) : null}
                     <CollectionFreeFormViewPannableContents
-                        brushView={this._brushedView}
+                        brushedView={this.brushedView}
                         isAnnotationOverlay={this.isAnnotationOverlay}
                         isAnnotationOverlayScrollable={this.props.isAnnotationOverlayScrollable}
                         transform={this.contentTransform}
                         zoomScaling={this.zoomScaling}
                         presPaths={this.showPresPaths}
-                        presPinView={BoolCast(this.Document.presPinView)}
+                        presPinView={BoolCast(this.Document.config_pinView)}
                         transition={this._panZoomTransition ? `transform ${this._panZoomTransition}ms` : Cast(this.layoutDoc._viewTransition, 'string', Cast(this.props.DocumentView?.()?.rootDoc._viewTransition, 'string', null))}
                         viewDefDivClick={this.props.viewDefDivClick}>
                         {this.children}
@@ -1945,19 +1947,19 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     };
 
     @action
-    brushView = (viewport: { width: number; height: number; panX: number; panY: number }) => {
-        this._brushedView = { ...viewport, panX: viewport.panX - viewport.width / 2, panY: viewport.panY - viewport.height / 2, opacity: 1 };
+    brushView = (viewport: { width: number; height: number; panX: number; panY: number }, transTime: number) => {
         this._brushtimer1 && clearTimeout(this._brushtimer1);
         this._brushtimer && clearTimeout(this._brushtimer);
+        this._brushedView = { width: 0, height: 0, panX: 0, panY: 0, opacity: 0 };
         this._brushtimer1 = setTimeout(
             action(() => {
-                this._brushedView.opacity = 0;
+                this._brushedView = { ...viewport, panX: viewport.panX - viewport.width / 2, panY: viewport.panY - viewport.height / 2, opacity: 1 };
                 this._brushtimer = setTimeout(
-                    action(() => (this._brushedView = { width: 0, height: 0, panX: 0, panY: 0, opacity: 0 })),
-                    500
+                    action(() => (this._brushedView.opacity = 0)),
+                    2500
                 );
             }),
-            1000
+            transTime + 1
         );
     };
     lightboxPanelWidth = () => Math.max(0, this.props.PanelWidth() - 30);
@@ -2084,7 +2086,7 @@ interface CollectionFreeFormViewPannableContentsProps {
     presPinView?: boolean;
     isAnnotationOverlay: boolean | undefined;
     isAnnotationOverlayScrollable: boolean | undefined;
-    brushView: { panX: number; panY: number; width: number; height: number; opacity: number };
+    brushedView: () => { panX: number; panY: number; width: number; height: number; opacity: number };
 }
 
 @observer
@@ -2156,6 +2158,7 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
     }
 
     render() {
+        const brushedView = this.props.brushedView();
         return (
             <div
                 className={'collectionfreeformview' + (this.props.viewDefDivClick ? '-viewDef' : '-none')}
@@ -2173,21 +2176,18 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
                     //willChange: "transform"
                 }}>
                 {this.props.children}
-                {!this.props.brushView.width ? null : (
+                {
                     <div
                         className="collectionFreeFormView-brushView"
                         style={{
-                            zIndex: 1000,
-                            opacity: this.props.brushView.opacity,
-                            border: 'orange solid 2px',
-                            position: 'absolute',
-                            transform: `translate(${this.props.brushView.panX}px, ${this.props.brushView.panY}px)`,
-                            width: this.props.brushView.width,
-                            height: this.props.brushView.height,
-                            transition: 'opacity 2s',
+                            opacity: brushedView.opacity,
+                            transform: `translate(${brushedView.panX}px, ${brushedView.panY}px)`,
+                            width: brushedView.width,
+                            height: brushedView.height,
+                            border: `orange solid ${brushedView.width * 0.005}px`,
                         }}
                     />
-                )}
+                }
                 {this.presPaths}
             </div>
         );
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 737d675aa..a81707ea4 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -84,7 +84,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
             },
             emptyFunction,
             action(() => {
-                const trail = DocCast(this.props.docView.rootDoc.presTrail);
+                const trail = DocCast(this.props.docView.rootDoc.presentationTrail);
                 if (trail) {
                     Doc.ActivePresentation = trail;
                     DocumentViewInternal.addDocTabFunc(trail, OpenWhere.replaceRight);
diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
index 0cc73f32f..059c39c68 100644
--- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx
+++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
@@ -20,7 +20,7 @@ export enum DataVizView {
     TABLE = 'table',
     LINECHART = 'lineChart',
     HISTOGRAM = 'histogram',
-    PIECHART = 'pieChart'
+    PIECHART = 'pieChart',
 }
 
 @observer
@@ -39,20 +39,22 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
 
     // current displayed chart type
     @computed get dataVizView(): DataVizView {
-        return StrCast(this.layoutDoc._dataVizView, 'table') as DataVizView;
+        return StrCast(this.layoutDoc._dataViz, 'table') as DataVizView;
     }
 
     @action // pinned / linked anchor doc includes selected rows, graph titles, and graph colors
     restoreView = (data: Doc) => {
-        const changedView = this.dataVizView !== data.presDataVizView && (this.layoutDoc._dataVizView = data.presDataVizView);
-        const changedAxes = this.axes.join('') !== StrListCast(data.presDataVizAxes).join('') && (this.layoutDoc._data_vizAxes = new List<string>(StrListCast(data.presDataVizAxes)));
-        this.layoutDoc.selected = Field.Copy(data.selected);
+        const changedView = this.dataVizView !== data.config_dataViz && (this.layoutDoc._dataViz = data.config_dataViz);
+        const changedAxes = this.axes.join('') !== StrListCast(data.config_dataVizAxes).join('') && (this.layoutDoc._dataViz_axes = new List<string>(StrListCast(data.config_dataVizAxes)));
+        this.layoutDoc.dataViz_selectedRows = Field.Copy(data.dataViz_selectedRows);
         this.layoutDoc.histogramBarColors = Field.Copy(data.histogramBarColors);
         this.layoutDoc.defaultHistogramColor = data.defaultHistogramColor;
         this.layoutDoc.pieSliceColors = Field.Copy(data.pieSliceColors);
         Object.keys(this.layoutDoc).map(key => {
-            if (key.startsWith('histogram-title') || key.startsWith('lineChart-title') || key.startsWith('pieChart-title')){ this.layoutDoc['_'+key] = data[key]; }
-        })
+            if (key.startsWith('histogram_title') || key.startsWith('lineChart_title') || key.startsWith('pieChart_title')) {
+                this.layoutDoc['_' + key] = data[key];
+            }
+        });
         const func = () => this._chartRenderer?.restoreView(data);
         if (changedView || changedAxes) {
             setTimeout(func, 100);
@@ -69,23 +71,25 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                   // this is for when we want the whole doc (so when the chartBox getAnchor returns without a marker)
                   /*put in some options*/
               });
-        anchor.presDataVizView = this.dataVizView;
-        anchor.presDataVizAxes = this.axes.length ? new List<string>(this.axes) : undefined;
-        anchor.selected = Field.Copy(this.layoutDoc.selected);
+        anchor.config_dataViz = this.dataVizView;
+        anchor.config_dataVizAxes = this.axes.length ? new List<string>(this.axes) : undefined;
+        anchor.dataViz_selectedRows = Field.Copy(this.layoutDoc.dataViz_selectedRows);
         anchor.histogramBarColors = Field.Copy(this.layoutDoc.histogramBarColors);
         anchor.defaultHistogramColor = this.layoutDoc.defaultHistogramColor;
         anchor.pieSliceColors = Field.Copy(this.layoutDoc.pieSliceColors);
         Object.keys(this.layoutDoc).map(key => {
-            if (key.startsWith('histogram-title') || key.startsWith('lineChart-title') || key.startsWith('pieChart-title')){ anchor[key] = this.layoutDoc[key]; }
-        })
+            if (key.startsWith('histogram_title') || key.startsWith('lineChart_title') || key.startsWith('pieChart_title')) {
+                anchor[key] = this.layoutDoc[key];
+            }
+        });
         this.addDocument(anchor);
         return anchor;
     };
 
     @computed.struct get axes() {
-        return StrListCast(this.layoutDoc.data_vizAxes);
+        return StrListCast(this.layoutDoc.dataViz_axes);
     }
-    selectAxes = (axes: string[]) => (this.layoutDoc.data_vizAxes = new List<string>(axes));
+    selectAxes = (axes: string[]) => (this.layoutDoc.dataViz_axes = new List<string>(axes));
 
     // toggles for user to decide which chart type to view the data in
     @computed get selectView() {
@@ -94,10 +98,53 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
         const margin = { top: 10, right: 25, bottom: 75, left: 45 };
         if (!this.pairs) return 'no data';
         switch (this.dataVizView) {
-            case DataVizView.TABLE:     return <TableBox layoutDoc={this.layoutDoc} pairs={this.pairs} axes={this.axes} height={height} width={width} margin={margin} rootDoc={this.rootDoc} docView={this.props.DocumentView} selectAxes={this.selectAxes}/>;
-            case DataVizView.LINECHART: return <LineChart layoutDoc={this.layoutDoc} ref={r => (this._chartRenderer = r ?? undefined)} height={height} width={width} fieldKey={this.fieldKey} margin={margin} rootDoc={this.rootDoc} axes={this.axes} pairs={this.pairs} dataDoc={this.dataDoc} />;
-            case DataVizView.HISTOGRAM: return <Histogram layoutDoc={this.layoutDoc} ref={r => (this._chartRenderer = r ?? undefined)} height={height} width={width} fieldKey={this.fieldKey} margin={margin} rootDoc={this.rootDoc} axes={this.axes} pairs={this.pairs} dataDoc={this.dataDoc} />;
-            case DataVizView.PIECHART: return <PieChart layoutDoc={this.layoutDoc} ref={r => (this._chartRenderer = r ?? undefined)} height={height} width={width} fieldKey={this.fieldKey} margin={margin} rootDoc={this.rootDoc} axes={this.axes} pairs={this.pairs} dataDoc={this.dataDoc} />;
+            case DataVizView.TABLE:
+                return <TableBox layoutDoc={this.layoutDoc} pairs={this.pairs} axes={this.axes} height={height} width={width} margin={margin} rootDoc={this.rootDoc} docView={this.props.DocumentView} selectAxes={this.selectAxes} />;
+            case DataVizView.LINECHART:
+                return (
+                    <LineChart
+                        layoutDoc={this.layoutDoc}
+                        ref={r => (this._chartRenderer = r ?? undefined)}
+                        height={height}
+                        width={width}
+                        fieldKey={this.fieldKey}
+                        margin={margin}
+                        rootDoc={this.rootDoc}
+                        axes={this.axes}
+                        pairs={this.pairs}
+                        dataDoc={this.dataDoc}
+                    />
+                );
+            case DataVizView.HISTOGRAM:
+                return (
+                    <Histogram
+                        layoutDoc={this.layoutDoc}
+                        ref={r => (this._chartRenderer = r ?? undefined)}
+                        height={height}
+                        width={width}
+                        fieldKey={this.fieldKey}
+                        margin={margin}
+                        rootDoc={this.rootDoc}
+                        axes={this.axes}
+                        pairs={this.pairs}
+                        dataDoc={this.dataDoc}
+                    />
+                );
+            case DataVizView.PIECHART:
+                return (
+                    <PieChart
+                        layoutDoc={this.layoutDoc}
+                        ref={r => (this._chartRenderer = r ?? undefined)}
+                        height={height}
+                        width={width}
+                        fieldKey={this.fieldKey}
+                        margin={margin}
+                        rootDoc={this.rootDoc}
+                        axes={this.axes}
+                        pairs={this.pairs}
+                        dataDoc={this.dataDoc}
+                    />
+                );
         }
     }
 
@@ -118,14 +165,10 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     }
 
     render() {
-        if (!this.layoutDoc._dataVizView) this.layoutDoc._dataVizView = this.dataVizView;
+        if (!this.layoutDoc._dataViz) this.layoutDoc._dataViz = this.dataVizView;
         return !this.pairs?.length ? (
             // displays how to get data into the DataVizBox if its empty
-            <div className="start-message">
-                To create a DataViz box, either import / drag a CSV file into your canvas
-                or copy a data table and use the command 'ctrl + p' to bring the data table
-                to your canvas.
-            </div>
+            <div className="start-message">To create a DataViz box, either import / drag a CSV file into your canvas or copy a data table and use the command 'ctrl + p' to bring the data table to your canvas.</div>
         ) : (
             <div
                 className="dataViz"
@@ -141,22 +184,10 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                     )
                 }>
                 <div className={'datatype-button'}>
-                    <Toggle text={"TABLE"} toggleType={ToggleType.BUTTON} type={Type.SEC} color={"black"} 
-                        onClick={e => this.layoutDoc._dataVizView = DataVizView.TABLE} 
-                        toggleStatus={this.layoutDoc._dataVizView == DataVizView.TABLE} 
-                    />
-                    <Toggle text={"LINECHART"} toggleType={ToggleType.BUTTON} type={Type.SEC} color={"black"} 
-                        onClick={e => this.layoutDoc._dataVizView = DataVizView.LINECHART} 
-                        toggleStatus={this.layoutDoc._dataVizView == DataVizView.LINECHART} 
-                    />
-                    <Toggle text={"HISTOGRAM"} toggleType={ToggleType.BUTTON} type={Type.SEC} color={"black"} 
-                        onClick={e => this.layoutDoc._dataVizView = DataVizView.HISTOGRAM} 
-                        toggleStatus={this.layoutDoc._dataVizView == DataVizView.HISTOGRAM} 
-                    />
-                    <Toggle text={"PIE CHART"} toggleType={ToggleType.BUTTON} type={Type.SEC} color={"black"} 
-                        onClick={e => this.layoutDoc._dataVizView = DataVizView.PIECHART} 
-                        toggleStatus={this.layoutDoc._dataVizView == DataVizView.PIECHART} 
-                    />
+                    <Toggle text={'TABLE'} toggleType={ToggleType.BUTTON} type={Type.SEC} color={'black'} onClick={e => (this.layoutDoc._dataViz = DataVizView.TABLE)} toggleStatus={this.layoutDoc._dataViz == DataVizView.TABLE} />
+                    <Toggle text={'LINECHART'} toggleType={ToggleType.BUTTON} type={Type.SEC} color={'black'} onClick={e => (this.layoutDoc._dataViz = DataVizView.LINECHART)} toggleStatus={this.layoutDoc._dataViz == DataVizView.LINECHART} />
+                    <Toggle text={'HISTOGRAM'} toggleType={ToggleType.BUTTON} type={Type.SEC} color={'black'} onClick={e => (this.layoutDoc._dataViz = DataVizView.HISTOGRAM)} toggleStatus={this.layoutDoc._dataViz == DataVizView.HISTOGRAM} />
+                    <Toggle text={'PIE CHART'} toggleType={ToggleType.BUTTON} type={Type.SEC} color={'black'} onClick={e => (this.layoutDoc._dataViz = DataVizView.PIECHART)} toggleStatus={this.layoutDoc._dataViz == DataVizView.PIECHART} />
                 </div>
                 {this.selectView}
             </div>
diff --git a/src/client/views/nodes/DataVizBox/components/Histogram.tsx b/src/client/views/nodes/DataVizBox/components/Histogram.tsx
index df6aac6bc..086db2a47 100644
--- a/src/client/views/nodes/DataVizBox/components/Histogram.tsx
+++ b/src/client/views/nodes/DataVizBox/components/Histogram.tsx
@@ -1,20 +1,20 @@
-import { observer } from "mobx-react";
-import { Doc, StrListCast } from "../../../../../fields/Doc";
+import { observer } from 'mobx-react';
+import { Doc, StrListCast } from '../../../../../fields/Doc';
 import * as React from 'react';
 import * as d3 from 'd3';
-import { IReactionDisposer, action, computed, observable, reaction } from "mobx";
-import { LinkManager } from "../../../../util/LinkManager";
-import { Cast, DocCast, StrCast} from "../../../../../fields/Types";
-import { PinProps, PresBox } from "../../trails";
-import { Docs } from "../../../../documents/Documents";
-import { List } from "../../../../../fields/List";
+import { IReactionDisposer, action, computed, observable, reaction } from 'mobx';
+import { LinkManager } from '../../../../util/LinkManager';
+import { Cast, DocCast, StrCast } from '../../../../../fields/Types';
+import { PinProps, PresBox } from '../../trails';
+import { Docs } from '../../../../documents/Documents';
+import { List } from '../../../../../fields/List';
 import './Chart.scss';
-import { ColorPicker, EditableText, IconButton, Size, Type } from "browndash-components";
-import { FaFillDrip } from "react-icons/fa";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { listSpec } from "../../../../../fields/Schema";
-import { scaleCreatorNumerical, yAxisCreator } from "../utils/D3Utils";
-import { undoBatch, undoable } from "../../../../util/UndoManager";
+import { ColorPicker, EditableText, IconButton, Size, Type } from 'browndash-components';
+import { FaFillDrip } from 'react-icons/fa';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { listSpec } from '../../../../../fields/Schema';
+import { scaleCreatorNumerical, yAxisCreator } from '../utils/D3Utils';
+import { undoBatch, undoable } from '../../../../util/UndoManager';
 
 export interface HistogramProps {
     rootDoc: Doc;
@@ -35,7 +35,6 @@ export interface HistogramProps {
 
 @observer
 export class Histogram extends React.Component<HistogramProps> {
-
     private _disposers: { [key: string]: IReactionDisposer } = {};
     private _histogramRef: React.RefObject<HTMLDivElement> = React.createRef();
     private _histogramSvg: d3.Selection<SVGGElement, unknown, null, undefined> | undefined;
@@ -49,32 +48,37 @@ export class Histogram extends React.Component<HistogramProps> {
 
     // filters all data to just display selected data if brushed (created from an incoming link)
     @computed get _histogramData() {
-        var guids = StrListCast(this.props.layoutDoc.rowGuids);
+        var guids = StrListCast(this.props.layoutDoc.dataViz_rowGuids);
         if (this.props.axes.length < 1) return [];
         if (this.props.axes.length < 2) {
             var ax0 = this.props.axes[0];
-            if (/\d/.test(this.props.pairs[0][ax0])){ this.numericalXData = true }
+            if (/\d/.test(this.props.pairs[0][ax0])) {
+                this.numericalXData = true;
+            }
             return this.props.pairs
-                ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.selected && StrListCast(this.incomingLinks[0].selected).includes(guids[this.props.pairs.indexOf(pair)])))
-                .map(pair => ({ [ax0]: (pair[this.props.axes[0]])}))
-        };
+                ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.dataViz_selectedRows && StrListCast(this.incomingLinks[0].dataViz_selectedRows).includes(guids[this.props.pairs.indexOf(pair)])))
+                .map(pair => ({ [ax0]: pair[this.props.axes[0]] }));
+        }
         var ax0 = this.props.axes[0];
         var ax1 = this.props.axes[1];
-        if (/\d/.test(this.props.pairs[0][ax0])) { this.numericalXData = true;}
-        if (/\d/.test(this.props.pairs[0][ax1]) ) { this.numericalYData = true;}
+        if (/\d/.test(this.props.pairs[0][ax0])) {
+            this.numericalXData = true;
+        }
+        if (/\d/.test(this.props.pairs[0][ax1])) {
+            this.numericalYData = true;
+        }
         return this.props.pairs
-            ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.selected && StrListCast(this.incomingLinks[0].selected).includes(guids[this.props.pairs.indexOf(pair)])))
-            .map(pair => ({ [ax0]: (pair[this.props.axes[0]]), [ax1]: (pair[this.props.axes[1]]) }))
+            ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.dataViz_selectedRows && StrListCast(this.incomingLinks[0].dataViz_selectedRows).includes(guids[this.props.pairs.indexOf(pair)])))
+            .map(pair => ({ [ax0]: pair[this.props.axes[0]], [ax1]: pair[this.props.axes[1]] }));
     }
 
-    @computed get defaultGraphTitle(){
+    @computed get defaultGraphTitle() {
         var ax0 = this.props.axes[0];
-        var ax1 = (this.props.axes.length>1)? this.props.axes[1] : undefined;
-        if (this.props.axes.length<2 || !ax1 || !/\d/.test(this.props.pairs[0][ax1]) || !this.numericalYData){
-            return ax0 + " Histogram";
-        }
-        else return ax0 + " by " + ax1 + " Histogram";
-    }    
+        var ax1 = this.props.axes.length > 1 ? this.props.axes[1] : undefined;
+        if (this.props.axes.length < 2 || !ax1 || !/\d/.test(this.props.pairs[0][ax1]) || !this.numericalYData) {
+            return ax0 + ' Histogram';
+        } else return ax0 + ' by ' + ax1 + ' Histogram';
+    }
 
     @computed get incomingLinks() {
         return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links
@@ -83,11 +87,11 @@ export class Histogram extends React.Component<HistogramProps> {
     }
 
     @computed get rangeVals(): { xMin?: number; xMax?: number; yMin?: number; yMax?: number } {
-        if (this.numericalXData){
+        if (this.numericalXData) {
             const data = this.data(this._histogramData);
-            return {xMin: Math.min.apply(null, data), xMax: Math.max.apply(null, data), yMin:0, yMax:0}
+            return { xMin: Math.min.apply(null, data), xMax: Math.max.apply(null, data), yMin: 0, yMax: 0 };
         }
-        return {xMin:0, xMax:0, yMin:0, yMax:0}
+        return { xMin: 0, xMax: 0, yMin: 0, yMax: 0 };
     }
 
     componentWillUnmount() {
@@ -97,7 +101,7 @@ export class Histogram extends React.Component<HistogramProps> {
         this._disposers.chartData = reaction(
             () => ({ dataSet: this._histogramData, w: this.width, h: this.height }),
             ({ dataSet, w, h }) => {
-                if (dataSet!.length>0) {
+                if (dataSet!.length > 0) {
                     this.drawChart(dataSet, w, h);
                 }
             },
@@ -113,7 +117,7 @@ export class Histogram extends React.Component<HistogramProps> {
             //
             title: 'histogram doc selection' + this._currSelected,
         });
-        PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.dataDoc);
+        PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.rootDoc);
         return anchor;
     };
 
@@ -127,20 +131,22 @@ export class Histogram extends React.Component<HistogramProps> {
 
     // cleans data by converting numerical data to numbers and taking out empty cells
     data = (dataSet: any) => {
-        var validData = dataSet.filter((d: { [x: string]: unknown; }) => {
+        var validData = dataSet.filter((d: { [x: string]: unknown }) => {
             var valid = true;
             Object.keys(dataSet[0]).map(key => {
                 if (!d[key] || Number.isNaN(d[key])) valid = false;
-            })
+            });
             return valid;
-        })
-        var field = dataSet[0]? Object.keys(dataSet[0])[0] : undefined;
-        const data = validData.map((d: { [x: string]: any; }) => {
-            if (this.numericalXData) { return +d[field!].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '') }
-            return d[field!]
-        })
+        });
+        var field = dataSet[0] ? Object.keys(dataSet[0])[0] : undefined;
+        const data = validData.map((d: { [x: string]: any }) => {
+            if (this.numericalXData) {
+                return +d[field!].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '');
+            }
+            return d[field!];
+        });
         return data;
-    }
+    };
 
     // outlines the bar selected / hovered over
     highlightSelectedBar = (changeSelectedVariables: boolean, svg: any, eachRectWidth: any, pointerX: any, xAxisTitle: any, yAxisTitle: any, histDataSet: any) => {
@@ -148,35 +154,31 @@ export class Histogram extends React.Component<HistogramProps> {
         var barCounter = -1;
         const selected = svg.selectAll('.histogram-bar').filter((d: any) => {
             barCounter++; // uses the order of bars and width of each bar to find which one the pointer is over
-            if ((barCounter*eachRectWidth ) <= pointerX && pointerX <= ((barCounter+1)*eachRectWidth)){
-                var showSelected = this.numericalYData? this._histogramData.filter((data: { [x: string]: any; }) => StrCast(data[xAxisTitle]).replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '')==d[0])[0] 
-                : histDataSet.filter((data: { [x: string]: any; }) => data[xAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '')==d[0])[0];
-                if (this.numericalXData){
+            if (barCounter * eachRectWidth <= pointerX && pointerX <= (barCounter + 1) * eachRectWidth) {
+                var showSelected = this.numericalYData
+                    ? this._histogramData.filter((data: { [x: string]: any }) => StrCast(data[xAxisTitle]).replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '') == d[0])[0]
+                    : histDataSet.filter((data: { [x: string]: any }) => data[xAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '') == d[0])[0];
+                if (this.numericalXData) {
                     // calculating frequency
-                    if (d[0] && d[1] && d[0]!=d[1]){
-                        showSelected = {[xAxisTitle]: (d3.min(d) + " to " + d3.max(d)), frequency: d.length}
-                    }
-                    else if (!this.numericalYData) showSelected = {[xAxisTitle]: showSelected[xAxisTitle], frequency: d.length}
-                } 
-                if (changeSelectedVariables){
-                    // for when a bar is selected - not just hovered over
-                    sameAsCurrent = this._currSelected? 
-                        (showSelected[xAxisTitle]==this._currSelected![xAxisTitle] 
-                    && showSelected[yAxisTitle]==this._currSelected![yAxisTitle])
-                        : false;
-                    this._currSelected = sameAsCurrent? undefined: showSelected;
-                    this.selectedData = sameAsCurrent? undefined: d;
+                    if (d[0] && d[1] && d[0] != d[1]) {
+                        showSelected = { [xAxisTitle]: d3.min(d) + ' to ' + d3.max(d), frequency: d.length };
+                    } else if (!this.numericalYData) showSelected = { [xAxisTitle]: showSelected[xAxisTitle], frequency: d.length };
                 }
-                else this.hoverOverData = d;
-                return true
+                if (changeSelectedVariables) {
+                    // for when a bar is selected - not just hovered over
+                    sameAsCurrent = this._currSelected ? showSelected[xAxisTitle] == this._currSelected![xAxisTitle] && showSelected[yAxisTitle] == this._currSelected![yAxisTitle] : false;
+                    this._currSelected = sameAsCurrent ? undefined : showSelected;
+                    this.selectedData = sameAsCurrent ? undefined : d;
+                } else this.hoverOverData = d;
+                return true;
             }
             return false;
         });
-        if (changeSelectedVariables){
+        if (changeSelectedVariables) {
             if (sameAsCurrent!) this.curBarSelected = undefined;
             else this.curBarSelected = selected;
         }
-    }
+    };
 
     // draws the histogram
     drawChart = (dataSet: any, width: number, height: number) => {
@@ -184,306 +186,338 @@ export class Histogram extends React.Component<HistogramProps> {
         d3.select(this._histogramRef.current).select('.tooltip').remove();
 
         var data = this.data(dataSet);
-        var xAxisTitle = Object.keys(dataSet[0])[0]
+        var xAxisTitle = Object.keys(dataSet[0])[0];
         var yAxisTitle = this.numericalYData ? Object.keys(dataSet[0])[1] : 'frequency';
-        let uniqueArr: unknown[] = [...new Set(data)]
-        var numBins = (this.numericalXData && Number.isInteger(data[0]))? (this.rangeVals.xMax! - this.rangeVals.xMin! ) : uniqueArr.length
-        var translateXAxis = !this.numericalXData || numBins<this.maxBins ?  width/(numBins+1)/2 : 0;
-        if (numBins>this.maxBins) numBins = this.maxBins;
-        var startingPoint = this.numericalXData? this.rangeVals.xMin! : 0;
-        var endingPoint = this.numericalXData? this.rangeVals.xMax! : numBins;
+        let uniqueArr: unknown[] = [...new Set(data)];
+        var numBins = this.numericalXData && Number.isInteger(data[0]) ? this.rangeVals.xMax! - this.rangeVals.xMin! : uniqueArr.length;
+        var translateXAxis = !this.numericalXData || numBins < this.maxBins ? width / (numBins + 1) / 2 : 0;
+        if (numBins > this.maxBins) numBins = this.maxBins;
+        var startingPoint = this.numericalXData ? this.rangeVals.xMin! : 0;
+        var endingPoint = this.numericalXData ? this.rangeVals.xMax! : numBins;
 
         // converts data into Objects
-        var histDataSet = dataSet.filter((d: { [x: string]: unknown; }) => {
+        var histDataSet = dataSet.filter((d: { [x: string]: unknown }) => {
             var valid = true;
             Object.keys(dataSet[0]).map(key => {
                 if (!d[key] || Number.isNaN(d[key])) valid = false;
-            })
+            });
             return valid;
         });
         if (!this.numericalXData) {
-            var histStringDataSet: { [x: string]: unknown; }[] = [];
-            if (this.numericalYData){
-                for (let i=0; i<dataSet.length; i++){
-                    histStringDataSet.push({[yAxisTitle]: dataSet[i][yAxisTitle], [xAxisTitle]: dataSet[i][xAxisTitle]})
+            var histStringDataSet: { [x: string]: unknown }[] = [];
+            if (this.numericalYData) {
+                for (let i = 0; i < dataSet.length; i++) {
+                    histStringDataSet.push({ [yAxisTitle]: dataSet[i][yAxisTitle], [xAxisTitle]: dataSet[i][xAxisTitle] });
                 }
-            }
-            else{
-                for (let i=0; i<uniqueArr.length; i++){
-                    histStringDataSet.push({[yAxisTitle]: 0, [xAxisTitle]: uniqueArr[i]})
+            } else {
+                for (let i = 0; i < uniqueArr.length; i++) {
+                    histStringDataSet.push({ [yAxisTitle]: 0, [xAxisTitle]: uniqueArr[i] });
                 }
-                for (let i=0; i<data.length; i++){
-                    let barData = histStringDataSet.filter(each => each[xAxisTitle]==data[i])
-                    histStringDataSet.filter(each => each[xAxisTitle]==data[i])[0][yAxisTitle] = Number(barData[0][yAxisTitle]) + 1;
+                for (let i = 0; i < data.length; i++) {
+                    let barData = histStringDataSet.filter(each => each[xAxisTitle] == data[i]);
+                    histStringDataSet.filter(each => each[xAxisTitle] == data[i])[0][yAxisTitle] = Number(barData[0][yAxisTitle]) + 1;
                 }
             }
-            histDataSet = histStringDataSet
+            histDataSet = histStringDataSet;
         }
 
         // initial graph and binning data for histogram
         var svg = (this._histogramSvg = d3
             .select(this._histogramRef.current)
-            .append("svg")
-                .attr("class", "graph")
-                .attr("width", width + this.props.margin.right + this.props.margin.left)
-                .attr("height", height + this.props.margin.top + this.props.margin.bottom)
-            .append("g")
-                .attr("transform",
-                      "translate(" + this.props.margin.left + "," + this.props.margin.top + ")"));
-        var x = d3.scaleLinear()
-                .domain(this.numericalXData? [startingPoint!, endingPoint!] : [0, numBins])
-                .range([0, width ]);
-        var histogram = d3.histogram()
-            .value(function(d) {return d})
+            .append('svg')
+            .attr('class', 'graph')
+            .attr('width', width + this.props.margin.right + this.props.margin.left)
+            .attr('height', height + this.props.margin.top + this.props.margin.bottom)
+            .append('g')
+            .attr('transform', 'translate(' + this.props.margin.left + ',' + this.props.margin.top + ')'));
+        var x = d3
+            .scaleLinear()
+            .domain(this.numericalXData ? [startingPoint!, endingPoint!] : [0, numBins])
+            .range([0, width]);
+        var histogram = d3
+            .histogram()
+            .value(function (d) {
+                return d;
+            })
             .domain([startingPoint!, endingPoint!])
-            .thresholds(x.ticks(numBins))
-        var bins = histogram(data)
-        var eachRectWidth = width/(bins.length)
-        var graphStartingPoint = (bins[0].x1 && bins[1])? bins[0].x1! - (bins[1].x1! - bins[1].x0!) : 0;
+            .thresholds(x.ticks(numBins));
+        var bins = histogram(data);
+        var eachRectWidth = width / bins.length;
+        var graphStartingPoint = bins[0].x1 && bins[1] ? bins[0].x1! - (bins[1].x1! - bins[1].x0!) : 0;
         bins[0].x0 = graphStartingPoint;
-        x = x.domain([graphStartingPoint, endingPoint])
-             .range([0, Number.isInteger(this.rangeVals.xMin!)? (width-eachRectWidth) : width ])
+        x = x.domain([graphStartingPoint, endingPoint]).range([0, Number.isInteger(this.rangeVals.xMin!) ? width - eachRectWidth : width]);
         var xAxis;
 
         // more calculations based on bins
         // x-axis
-        if (!this.numericalXData) { // reorganize to match data if the data is strings rather than numbers
+        if (!this.numericalXData) {
+            // reorganize to match data if the data is strings rather than numbers
             // uniqueArr.sort()
-            histDataSet.sort()
-            for (let i=0; i<data.length; i++){
-                var index = 0
-                for (let j=0; j<uniqueArr.length; j++){
-                    if (uniqueArr[j] == data[i]){
+            histDataSet.sort();
+            for (let i = 0; i < data.length; i++) {
+                var index = 0;
+                for (let j = 0; j < uniqueArr.length; j++) {
+                    if (uniqueArr[j] == data[i]) {
                         index = j;
                     }
                 }
-                if (bins[index]) bins[index].push(data[i])
+                if (bins[index]) bins[index].push(data[i]);
             }
             bins.pop();
-            eachRectWidth = width/(bins.length)
-            bins.forEach(d => d.x0 = d.x0!)
-            xAxis = d3.axisBottom(x)
-                .ticks(bins.length>1? bins.length-1: 1)
-                .tickFormat( i => uniqueArr[i.valueOf()] as string)
-                .tickPadding(10)
-            x.range([0, width-eachRectWidth])
-            x.domain([0, bins.length-1])
+            eachRectWidth = width / bins.length;
+            bins.forEach(d => (d.x0 = d.x0!));
+            xAxis = d3
+                .axisBottom(x)
+                .ticks(bins.length > 1 ? bins.length - 1 : 1)
+                .tickFormat(i => uniqueArr[i.valueOf()] as string)
+                .tickPadding(10);
+            x.range([0, width - eachRectWidth]);
+            x.domain([0, bins.length - 1]);
             translateXAxis = eachRectWidth / 2;
-        }
-        else {
+        } else {
             var allSame = true;
-            for (var i=0; i<bins.length; i++){
-                if (bins[i] && bins[i][0]){
+            for (var i = 0; i < bins.length; i++) {
+                if (bins[i] && bins[i][0]) {
                     var compare = bins[i][0];
-                    for (let j=1; j<bins[i].length; j++){
+                    for (let j = 1; j < bins[i].length; j++) {
                         if (bins[i][j] != compare) allSame = false;
                     }
                 }
             }
-            if (allSame){ 
+            if (allSame) {
                 translateXAxis = eachRectWidth / 2;
-                eachRectWidth = width/(bins.length)
-            }
-            else {
-                eachRectWidth = width/(bins.length+1)
-                var tickDiff = (bins.length>=2? (bins[bins.length-2].x1!-bins[bins.length-2].x0!): 0)
+                eachRectWidth = width / bins.length;
+            } else {
+                eachRectWidth = width / (bins.length + 1);
+                var tickDiff = bins.length >= 2 ? bins[bins.length - 2].x1! - bins[bins.length - 2].x0! : 0;
                 var curDomain = x.domain();
-                x.domain([curDomain[0], curDomain[0] + tickDiff*bins.length])
+                x.domain([curDomain[0], curDomain[0] + tickDiff * bins.length]);
             }
 
-            xAxis = d3.axisBottom(x)
-                .ticks(bins.length-1)
-            x.range([0, width-eachRectWidth])
+            xAxis = d3.axisBottom(x).ticks(bins.length - 1);
+            x.range([0, width - eachRectWidth]);
         }
         // y-axis
-        const maxFrequency = this.numericalYData? d3.max(histDataSet, function(d: any) { 
-            return d[yAxisTitle]? Number(d[yAxisTitle]!.replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '')) : 0})
-                    : d3.max(bins, function(d) { return d.length; })
-        var y = d3.scaleLinear()
-            .range([height, 0]);
-            y.domain([0, +maxFrequency!]);
-        var yAxis = d3.axisLeft(y)
-            .ticks(maxFrequency!)
-        if (this.numericalYData){
+        const maxFrequency = this.numericalYData
+            ? d3.max(histDataSet, function (d: any) {
+                  return d[yAxisTitle] ? Number(d[yAxisTitle]!.replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '')) : 0;
+              })
+            : d3.max(bins, function (d) {
+                  return d.length;
+              });
+        var y = d3.scaleLinear().range([height, 0]);
+        y.domain([0, +maxFrequency!]);
+        var yAxis = d3.axisLeft(y).ticks(maxFrequency!);
+        if (this.numericalYData) {
             const yScale = scaleCreatorNumerical(0, Number(maxFrequency), height, 0);
             yAxisCreator(svg.append('g'), width, yScale);
+        } else {
+            svg.append('g').call(yAxis);
         }
-        else{
-            svg.append("g")
-            .call(yAxis);
-        }
-        svg.append("g")
-            .attr("transform", "translate(" + translateXAxis + ", " + height + ")")
-            .call(xAxis)
+        svg.append('g')
+            .attr('transform', 'translate(' + translateXAxis + ', ' + height + ')')
+            .call(xAxis);
 
         // click/hover
         const onPointClick = action((e: any) => this.highlightSelectedBar(true, svg, eachRectWidth, d3.pointer(e)[0], xAxisTitle, yAxisTitle, histDataSet));
         const onHover = action((e: any) => {
-            const selected = this.highlightSelectedBar(false, svg, eachRectWidth, d3.pointer(e)[0], xAxisTitle, yAxisTitle, histDataSet)
+            const selected = this.highlightSelectedBar(false, svg, eachRectWidth, d3.pointer(e)[0], xAxisTitle, yAxisTitle, histDataSet);
             updateHighlights();
-        })
+        });
         const mouseOut = action((e: any) => {
             this.hoverOverData = undefined;
             updateHighlights();
-        })
+        });
         const updateHighlights = () => {
             const hoverOverBar = this.hoverOverData;
             const selectedData = this.selectedData;
-            svg.selectAll('rect').attr("class", function(d: any) { return ((hoverOverBar && hoverOverBar[0]==d[0]) || selectedData && selectedData[0]==d[0])? 'histogram-bar hover' : 'histogram-bar'; })
-        }
-        svg.on('click', onPointClick)
-            .on('mouseover', onHover)
-            .on('mouseout', mouseOut)
+            svg.selectAll('rect').attr('class', function (d: any) {
+                return (hoverOverBar && hoverOverBar[0] == d[0]) || (selectedData && selectedData[0] == d[0]) ? 'histogram-bar hover' : 'histogram-bar';
+            });
+        };
+        svg.on('click', onPointClick).on('mouseover', onHover).on('mouseout', mouseOut);
 
         // axis titles
-        svg.append("text")
-            .attr("transform", "translate(" + (width/2) + " ," + (height+40) + ")")
-            .style("text-anchor", "middle")
+        svg.append('text')
+            .attr('transform', 'translate(' + width / 2 + ' ,' + (height + 40) + ')')
+            .style('text-anchor', 'middle')
             .text(xAxisTitle);
-        svg.append("text")
-            .attr("transform", "rotate(-90)" + " " + "translate( 0, " + -10 + ")")
-            .attr("x", -(height/2))
-            .attr("y", -20)
-            .style("text-anchor", "middle")
+        svg.append('text')
+            .attr('transform', 'rotate(-90)' + ' ' + 'translate( 0, ' + -10 + ')')
+            .attr('x', -(height / 2))
+            .attr('y', -20)
+            .style('text-anchor', 'middle')
             .text(yAxisTitle);
-        d3.format('.0f')
+        d3.format('.0f');
 
         // draw bars
         var selected = this.selectedData;
-        svg.selectAll("rect")
+        svg.selectAll('rect')
             .data(bins)
             .enter()
-            .append("rect")
-              .attr("transform", this.numericalYData?
-                    function (d) {
-                        var eachData = histDataSet.filter((data: { [x: string]: number; }) => {return data[xAxisTitle]==d[0]}) 
-                        var length = eachData.length? eachData[0][yAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '') : 0;
-                        return "translate(" + x(d.x0!) + "," + y(length) + ")";
-                    }
-                    : function(d) { return "translate(" + x(d.x0!) + "," + y(d.length) + ")"; })
-              .attr("height", this.numericalYData? 
-                    function(d) {
-                        var eachData = histDataSet.filter((data: { [x: string]: number; }) => {return data[xAxisTitle]==d[0]}) 
-                        var length = eachData.length? eachData[0][yAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '') : 0;
-                        return height-y(length)}
-                    : function(d) { 
-                        return height - y(d.length)})
-              .attr("width", eachRectWidth)
-              .attr("class", selected? 
-                    function(d) {
-                        return (selected && selected[0]==d[0])? 'histogram-bar hover' : 'histogram-bar';
-                    }: function(d) {return 'histogram-bar'})
-              .attr("fill", (d)=>{ 
+            .append('rect')
+            .attr(
+                'transform',
+                this.numericalYData
+                    ? function (d) {
+                          var eachData = histDataSet.filter((data: { [x: string]: number }) => {
+                              return data[xAxisTitle] == d[0];
+                          });
+                          var length = eachData.length ? eachData[0][yAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '') : 0;
+                          return 'translate(' + x(d.x0!) + ',' + y(length) + ')';
+                      }
+                    : function (d) {
+                          return 'translate(' + x(d.x0!) + ',' + y(d.length) + ')';
+                      }
+            )
+            .attr(
+                'height',
+                this.numericalYData
+                    ? function (d) {
+                          var eachData = histDataSet.filter((data: { [x: string]: number }) => {
+                              return data[xAxisTitle] == d[0];
+                          });
+                          var length = eachData.length ? eachData[0][yAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '') : 0;
+                          return height - y(length);
+                      }
+                    : function (d) {
+                          return height - y(d.length);
+                      }
+            )
+            .attr('width', eachRectWidth)
+            .attr(
+                'class',
+                selected
+                    ? function (d) {
+                          return selected && selected[0] == d[0] ? 'histogram-bar hover' : 'histogram-bar';
+                      }
+                    : function (d) {
+                          return 'histogram-bar';
+                      }
+            )
+            .attr('fill', d => {
                 var barColor;
                 var barColors = StrListCast(this.props.layoutDoc.histogramBarColors).map(each => each.split('::'));
-                barColors.map(each => { 
-                    if (d[0] && d[0].toString() && each[0]==d[0].toString()) barColor = each[1]; 
+                barColors.map(each => {
+                    if (d[0] && d[0].toString() && each[0] == d[0].toString()) barColor = each[1];
                     else {
-                        var range = StrCast(each[0]).split(" to ");
-                        if (Number(range[0])<=d[0] && d[0]<=Number(range[1])) barColor = each[1];
+                        var range = StrCast(each[0]).split(' to ');
+                        if (Number(range[0]) <= d[0] && d[0] <= Number(range[1])) barColor = each[1];
                     }
                 });
-                return barColor? StrCast(barColor) : StrCast(this.props.layoutDoc.defaultHistogramColor)})
+                return barColor ? StrCast(barColor) : StrCast(this.props.layoutDoc.defaultHistogramColor);
+            });
     };
 
     @action changeSelectedColor = (color: string) => {
-        this.curBarSelected.attr("fill", color);
-        var barName = StrCast(this._currSelected[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''))
+        this.curBarSelected.attr('fill', color);
+        var barName = StrCast(this._currSelected[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''));
 
-        const barColors = Cast(this.props.layoutDoc.histogramBarColors, listSpec("string"), null);
-        barColors.map(each => { if (each.split('::')[0] == barName) barColors.splice(barColors.indexOf(each), 1) });
+        const barColors = Cast(this.props.layoutDoc.histogramBarColors, listSpec('string'), null);
+        barColors.map(each => {
+            if (each.split('::')[0] == barName) barColors.splice(barColors.indexOf(each), 1);
+        });
         barColors.push(StrCast(barName + '::' + color));
     };
-    
-    @action eraseSelectedColor= () => {
-        this.curBarSelected.attr("fill", this.props.layoutDoc.defaultHistogramColor);
-        var barName = StrCast(this._currSelected[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''))
 
-        const barColors = Cast(this.props.layoutDoc.histogramBarColors, listSpec("string"), null);
-        barColors.map(each => { if (each.split('::')[0] == barName) barColors.splice(barColors.indexOf(each), 1) });
+    @action eraseSelectedColor = () => {
+        this.curBarSelected.attr('fill', this.props.layoutDoc.defaultHistogramColor);
+        var barName = StrCast(this._currSelected[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''));
+
+        const barColors = Cast(this.props.layoutDoc.histogramBarColors, listSpec('string'), null);
+        barColors.map(each => {
+            if (each.split('::')[0] == barName) barColors.splice(barColors.indexOf(each), 1);
+        });
     };
 
     render() {
-        this._histogramData
-        var curSelectedBarName;
-        var titleAccessor: any='';
-        if (this.props.axes.length==2) titleAccessor = 'histogram-title-'+this.props.axes[0]+'-'+this.props.axes[1];
-        else if (this.props.axes.length>0) titleAccessor = 'histogram-title-'+this.props.axes[0];
+        this._histogramData;
+        var curSelectedBarName = '';
+        var titleAccessor: any = '';
+        if (this.props.axes.length == 2) titleAccessor = 'dataViz_title_histogram_' + this.props.axes[0] + '-' + this.props.axes[1];
+        else if (this.props.axes.length > 0) titleAccessor = 'dataViz_title_histogram_' + this.props.axes[0];
         if (!this.props.layoutDoc[titleAccessor]) this.props.layoutDoc[titleAccessor] = this.defaultGraphTitle;
         if (!this.props.layoutDoc.defaultHistogramColor) this.props.layoutDoc.defaultHistogramColor = '#69b3a2';
         if (!this.props.layoutDoc.histogramBarColors) this.props.layoutDoc.histogramBarColors = new List<string>();
         var selected: string;
-        if (this._currSelected){
-            curSelectedBarName = StrCast(this._currSelected![this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''))
+        if (this._currSelected) {
+            curSelectedBarName = StrCast(this._currSelected![this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''));
             selected = '{ ';
             Object.keys(this._currSelected).map(key => {
-                key!=''? selected += key + ': ' + this._currSelected[key] + ', ': '';
-            })
-            selected = selected.substring(0, selected.length-2);
+                key != '' ? (selected += key + ': ' + this._currSelected[key] + ', ') : '';
+            });
+            selected = selected.substring(0, selected.length - 2);
             selected += ' }';
-        }
-        else selected = 'none';
+        } else selected = 'none';
         var selectedBarColor;
         var barColors = StrListCast(this.props.layoutDoc.histogramBarColors).map(each => each.split('::'));
-        barColors.map(each => {if (each[0]==curSelectedBarName!) selectedBarColor = each[1]});
+        barColors.map(each => {
+            if (each[0] == curSelectedBarName!) selectedBarColor = each[1];
+        });
 
         this.componentDidMount();
 
-        if (this._histogramData.length>0){
-            return (
-                this.props.axes.length >= 1 ? (
-                <div className="chart-container" >
-                    <div className="graph-title"> 
+        if (this._histogramData.length > 0) {
+            return this.props.axes.length >= 1 ? (
+                <div className="chart-container">
+                    <div className="graph-title">
                         <EditableText
                             val={StrCast(this.props.layoutDoc[titleAccessor])}
-                            setVal={undoable (action(val => this.props.layoutDoc[titleAccessor] = val as string), "Change Graph Title")}
-                            color={"black"}
+                            setVal={undoable(
+                                action(val => (this.props.layoutDoc[titleAccessor] = val as string)),
+                                'Change Graph Title'
+                            )}
+                            color={'black'}
                             size={Size.LARGE}
                             fillWidth
                         />
-                        &nbsp; &nbsp; 
+                        &nbsp; &nbsp;
                         <ColorPicker
-                            tooltip={'Change Default Bar Color'} 
-                            type={Type.SEC} 
-                            icon={<FaFillDrip/>}
+                            tooltip={'Change Default Bar Color'}
+                            type={Type.SEC}
+                            icon={<FaFillDrip />}
                             selectedColor={StrCast(this.props.layoutDoc.defaultHistogramColor)}
-                            setSelectedColor={undoable (color => this.props.layoutDoc.defaultHistogramColor = color, "Change Default Bar Color")}
+                            setFinalColor={undoable(color => (this.props.layoutDoc.defaultHistogramColor = color), 'Change Default Bar Color')}
+                            setSelectedColor={undoable(color => (this.props.layoutDoc.defaultHistogramColor = color), 'Change Default Bar Color')}
                             size={Size.XSMALL}
                         />
                     </div>
                     <div ref={this._histogramRef} />
-                    {selected != 'none' ? 
+                    {selected != 'none' ? (
                         <div className={'selected-data'}>
                             Selected: {selected}
                             &nbsp; &nbsp;
                             <ColorPicker
-                                tooltip={'Change Bar Color'} 
-                                type={Type.SEC} 
-                                icon={<FaFillDrip/>}
-                                selectedColor={selectedBarColor? selectedBarColor : this.curBarSelected.attr("fill")}
-                                setSelectedColor={undoable (color => this.changeSelectedColor(color), "Change Selected Bar Color")}
+                                tooltip={'Change Bar Color'}
+                                type={Type.SEC}
+                                icon={<FaFillDrip />}
+                                selectedColor={selectedBarColor ? selectedBarColor : this.curBarSelected.attr('fill')}
+                                setFinalColor={undoable(color => this.changeSelectedColor(color), 'Change Selected Bar Color')}
+                                setSelectedColor={undoable(color => this.changeSelectedColor(color), 'Change Selected Bar Color')}
                                 size={Size.XSMALL}
                             />
                             &nbsp;
                             <IconButton
-                                icon={<FontAwesomeIcon icon={'eraser'} />} 
-                                size={Size.XSMALL} 
-                                color={'black'} 
+                                icon={<FontAwesomeIcon icon={'eraser'} />}
+                                size={Size.XSMALL}
+                                color={'black'}
                                 type={Type.SEC}
                                 tooltip={'Revert to the default bar color'}
-                                onClick={undoable (action(() => this.eraseSelectedColor()), "Change Selected Bar Color")}
+                                onClick={undoable(
+                                    action(() => this.eraseSelectedColor()),
+                                    'Change Selected Bar Color'
+                                )}
                             />
                         </div>
-                    : null}
+                    ) : null}
                 </div>
-                ) : <span className="chart-container"> {'first use table view to select a column to graph'}</span>
+            ) : (
+                <span className="chart-container"> {'first use table view to select a column to graph'}</span>
+            );
+        } else
+            return (
+                // when it is a brushed table and the incoming table doesn't have any rows selected
+                <div className="chart-container">Selected rows of data from the incoming DataVizBox to display.</div>
             );
-        }
-        else return (
-            // when it is a brushed table and the incoming table doesn't have any rows selected
-            <div className='chart-container'>
-                Selected rows of data from the incoming DataVizBox to display.
-            </div>
-        )
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
index 8bace941f..8e549adbe 100644
--- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx
+++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
@@ -50,20 +50,21 @@ export class LineChart extends React.Component<LineChartProps> {
     // TODO: nda - some sort of mapping that keeps track of the annotated points so we can easily remove when annotations list updates
 
     @computed get _lineChartData() {
-        var guids = StrListCast(this.props.layoutDoc.rowGuids);
+        var guids = StrListCast(this.props.layoutDoc.dataViz_rowGuids);
         if (this.props.axes.length <= 1) return [];
         return this.props.pairs
-            ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.selected && StrListCast(this.incomingLinks[0].selected).includes(guids[this.props.pairs.indexOf(pair)])))
+            ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.dataViz_selectedRows && StrListCast(this.incomingLinks[0].dataViz_selectedRows).includes(guids[this.props.pairs.indexOf(pair)])))
             .map(pair => ({ x: Number(pair[this.props.axes[0]]), y: Number(pair[this.props.axes[1]]) }))
             .sort((a, b) => (a.x < b.x ? -1 : 1));
     }
-    @computed get graphTitle(){
-        return this.props.axes[1] + " vs. " + this.props.axes[0] + " Line Chart";
-    }  
+    @computed get graphTitle() {
+        return this.props.axes[1] + ' vs. ' + this.props.axes[0] + ' Line Chart';
+    }
     @computed get incomingLinks() {
         return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links
             .filter(link => {
-                return link.link_anchor_1 == this.props.rootDoc.draggedFrom}) // get links where this chart doc is the target of the link
+                return link.link_anchor_1 == this.props.rootDoc.draggedFrom;
+            }) // get links where this chart doc is the target of the link
             .map(link => DocCast(link.link_anchor_1)); // then return the source of the link
     }
     @computed get incomingSelected() {
@@ -158,7 +159,7 @@ export class LineChart extends React.Component<LineChartProps> {
 
     @action
     restoreView = (data: Doc) => {
-        const coords = Cast(data.presDataVizSelection, listSpec('number'), null);
+        const coords = Cast(data.config_dataVizSelection, listSpec('number'), null);
         if (coords?.length > 1 && (this._currSelected?.x !== coords[0] || this._currSelected?.y !== coords[1])) {
             this.setCurrSelected(coords[0], coords[1]);
             return true;
@@ -176,8 +177,8 @@ export class LineChart extends React.Component<LineChartProps> {
             //
             title: 'line doc selection' + this._currSelected?.x,
         });
-        PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.dataDoc);
-        anchor.presDataVizSelection = this._currSelected ? new List<number>([this._currSelected.x, this._currSelected.y]) : undefined;
+        PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.rootDoc);
+        anchor.config_dataVizSelection = this._currSelected ? new List<number>([this._currSelected.x, this._currSelected.y]) : undefined;
         return anchor;
     };
 
@@ -189,14 +190,13 @@ export class LineChart extends React.Component<LineChartProps> {
         return this.props.width - this.props.margin.left - this.props.margin.right;
     }
 
-    @computed get defaultGraphTitle(){
+    @computed get defaultGraphTitle() {
         var ax0 = this.props.axes[0];
-        var ax1 = (this.props.axes.length>1)? this.props.axes[1] : undefined;
-        if (this.props.axes.length<2 || !/\d/.test(this.props.pairs[0][ax0]) || !ax1){
-            return ax0 + " Line Chart";
-        }
-        else return ax1 + " by " + ax0 + " Line Chart";
-    } 
+        var ax1 = this.props.axes.length > 1 ? this.props.axes[1] : undefined;
+        if (this.props.axes.length < 2 || !/\d/.test(this.props.pairs[0][ax0]) || !ax1) {
+            return ax0 + ' Line Chart';
+        } else return ax1 + ' by ' + ax0 + ' Line Chart';
+    }
 
     setupTooltip() {
         return d3
@@ -215,7 +215,7 @@ export class LineChart extends React.Component<LineChartProps> {
     @action
     setCurrSelected(x?: number, y?: number) {
         // TODO: nda - get rid of svg element in the list?
-        if (this._currSelected && this._currSelected.x==x && this._currSelected.y==y) this._currSelected = undefined;
+        if (this._currSelected && this._currSelected.x == x && this._currSelected.y == y) this._currSelected = undefined;
         else this._currSelected = x !== undefined && y !== undefined ? { x, y } : undefined;
         this.props.pairs.forEach(pair => pair[this.props.axes[0]] === x && pair[this.props.axes[1]] === y && (pair.selected = true));
     }
@@ -256,7 +256,7 @@ export class LineChart extends React.Component<LineChartProps> {
         const svg = (this._lineChartSvg = d3
             .select(this._lineChartRef.current)
             .append('svg')
-            .attr("class", "graph")
+            .attr('class', 'graph')
             .attr('width', `${width + margin.left + margin.right}`)
             .attr('height', `${height + margin.top + margin.bottom}`)
             .append('g')
@@ -271,13 +271,13 @@ export class LineChart extends React.Component<LineChartProps> {
         // get valid data points
         const data = dataSet[0];
         const lineGen = createLineGenerator(xScale, yScale);
-        var validData = data.filter((d => {
+        var validData = data.filter(d => {
             var valid = true;
             Object.keys(data[0]).map(key => {
                 if (!d[key] || Number.isNaN(d[key])) valid = false;
-            })
+            });
             return valid;
-        }))
+        });
         // draw the plot line
         drawLine(svg.append('path'), validData, lineGen);
         // draw the datapoint circle
@@ -321,17 +321,17 @@ export class LineChart extends React.Component<LineChartProps> {
             .on('click', onPointClick);
 
         // axis titles
-        svg.append("text")
-            .attr("transform", "translate(" + (width/2) + " ," + (height+40) + ")")
-            .style("text-anchor", "middle")
+        svg.append('text')
+            .attr('transform', 'translate(' + width / 2 + ' ,' + (height + 40) + ')')
+            .style('text-anchor', 'middle')
             .text(this.props.axes[0]);
-        svg.append("text")
-            .attr("transform", "rotate(-90)" + " " + "translate( 0, " + -10 + ")")
-            .attr("x", -(height/2))
-            .attr("y", -20)
-            .attr("height", 20)
-            .attr("width", 20)
-            .style("text-anchor", "middle")
+        svg.append('text')
+            .attr('transform', 'rotate(-90)' + ' ' + 'translate( 0, ' + -10 + ')')
+            .attr('x', -(height / 2))
+            .attr('y', -20)
+            .attr('height', 20)
+            .attr('width', 20)
+            .style('text-anchor', 'middle')
             .text(this.props.axes[1]);
     };
 
@@ -348,42 +348,41 @@ export class LineChart extends React.Component<LineChartProps> {
         tooltip
             .html(() => `<b>(${d0.x},${d0.y})</b>`) // text content for tooltip
             .style('pointer-events', 'none')
-            .style('transform', `translate(${xScale(d0.x)-this.width}px,${yScale(d0.y)}px)`);
+            .style('transform', `translate(${xScale(d0.x) - this.width}px,${yScale(d0.y)}px)`);
     }
 
     render() {
         this.componentDidMount();
-        var titleAccessor:any = '';
-        if (this.props.axes.length==2) titleAccessor = 'lineChart-title-'+this.props.axes[0]+'-'+this.props.axes[1];
-        else if (this.props.axes.length>0) titleAccessor = 'lineChart-title-'+this.props.axes[0];
+        var titleAccessor: any = '';
+        if (this.props.axes.length == 2) titleAccessor = 'dataViz_title_lineChart_' + this.props.axes[0] + '-' + this.props.axes[1];
+        else if (this.props.axes.length > 0) titleAccessor = 'dataViz_title_lineChart_' + this.props.axes[0];
         if (!this.props.layoutDoc[titleAccessor]) this.props.layoutDoc[titleAccessor] = this.defaultGraphTitle;
         const selectedPt = this._currSelected ? `{ ${this.props.axes[0]}: ${this._currSelected.x} ${this.props.axes[1]}: ${this._currSelected.y} }` : 'none';
-        if (this._lineChartData.length>0){
-            return (
-                this.props.axes.length>=2 && /\d/.test(this.props.pairs[0][this.props.axes[0]]) && /\d/.test(this.props.pairs[0][this.props.axes[1]]) ? (
-                <div className="chart-container" >
-                    <div className="graph-title"> 
+        if (this._lineChartData.length > 0) {
+            return this.props.axes.length >= 2 && /\d/.test(this.props.pairs[0][this.props.axes[0]]) && /\d/.test(this.props.pairs[0][this.props.axes[1]]) ? (
+                <div className="chart-container">
+                    <div className="graph-title">
                         <EditableText
                             val={StrCast(this.props.layoutDoc[titleAccessor])}
-                            setVal={undoable (action(val => this.props.layoutDoc[titleAccessor] = val as string), "Change Graph Title")}
-                            color={"black"}
+                            setVal={undoable(
+                                action(val => (this.props.layoutDoc[titleAccessor] = val as string)),
+                                'Change Graph Title'
+                            )}
+                            color={'black'}
                             size={Size.LARGE}
                             fillWidth
                         />
                     </div>
                     <div ref={this._lineChartRef} />
-                    {selectedPt!='none'? 
-                        <div className={'selected-data'}> {`Selected: ${selectedPt}`}</div>
-                    : null}
+                    {selectedPt != 'none' ? <div className={'selected-data'}> {`Selected: ${selectedPt}`}</div> : null}
                 </div>
-                ) : <span className="chart-container"> {'first use table view to select two numerical axes to plot'}</span>
+            ) : (
+                <span className="chart-container"> {'first use table view to select two numerical axes to plot'}</span>
+            );
+        } else
+            return (
+                // when it is a brushed table and the incoming table doesn't have any rows selected
+                <div className="chart-container">Selected rows of data from the incoming DataVizBox to display.</div>
             );
-        }
-        else return (
-            // when it is a brushed table and the incoming table doesn't have any rows selected
-            <div className='chart-container'>
-                Selected rows of data from the incoming DataVizBox to display.
-            </div>
-        )
     }
 }
diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx
index 0c54d0a4e..fe77293f0 100644
--- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx
+++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx
@@ -1,18 +1,18 @@
-import { observer } from "mobx-react";
-import { Doc, StrListCast } from "../../../../../fields/Doc";
+import { observer } from 'mobx-react';
+import { Doc, StrListCast } from '../../../../../fields/Doc';
 import * as React from 'react';
 import * as d3 from 'd3';
-import { IReactionDisposer, action, computed, observable, reaction } from "mobx";
-import { LinkManager } from "../../../../util/LinkManager";
-import { Cast, DocCast, StrCast} from "../../../../../fields/Types";
-import { PinProps, PresBox } from "../../trails";
-import { Docs } from "../../../../documents/Documents";
-import { List } from "../../../../../fields/List";
+import { IReactionDisposer, action, computed, observable, reaction } from 'mobx';
+import { LinkManager } from '../../../../util/LinkManager';
+import { Cast, DocCast, StrCast } from '../../../../../fields/Types';
+import { PinProps, PresBox } from '../../trails';
+import { Docs } from '../../../../documents/Documents';
+import { List } from '../../../../../fields/List';
 import './Chart.scss';
-import { ColorPicker, EditableText, Size, Type } from "browndash-components";
-import { FaFillDrip } from "react-icons/fa";
-import { listSpec } from "../../../../../fields/Schema";
-import { undoable } from "../../../../util/UndoManager";
+import { ColorPicker, EditableText, Size, Type } from 'browndash-components';
+import { FaFillDrip } from 'react-icons/fa';
+import { listSpec } from '../../../../../fields/Schema';
+import { undoable } from '../../../../util/UndoManager';
 
 export interface PieChartProps {
     rootDoc: Doc;
@@ -33,7 +33,6 @@ export interface PieChartProps {
 
 @observer
 export class PieChart extends React.Component<PieChartProps> {
-
     private _disposers: { [key: string]: IReactionDisposer } = {};
     private _piechartRef: React.RefObject<HTMLDivElement> = React.createRef();
     private _piechartSvg: d3.Selection<SVGGElement, unknown, null, undefined> | undefined;
@@ -45,31 +44,34 @@ export class PieChart extends React.Component<PieChartProps> {
 
     // filters all data to just display selected data if brushed (created from an incoming link)
     @computed get _piechartData() {
-        var guids = StrListCast(this.props.layoutDoc.rowGuids);
+        var guids = StrListCast(this.props.layoutDoc.dataViz_rowGuids);
         if (this.props.axes.length < 1) return [];
         if (this.props.axes.length < 2) {
             var ax0 = this.props.axes[0];
-            if (/\d/.test(this.props.pairs[0][ax0])){ this.byCategory = false }
+            if (/\d/.test(this.props.pairs[0][ax0])) {
+                this.byCategory = false;
+            }
             return this.props.pairs
-                ?.filter(pair => (!this.incomingLinks.length ? true :  this.incomingLinks[0]!.selected && StrListCast(this.incomingLinks[0].selected).includes(guids[this.props.pairs.indexOf(pair)])))
-                .map(pair => ({ [ax0]: (pair[this.props.axes[0]])}))
-        };
+                ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.dataViz_selectedRows && StrListCast(this.incomingLinks[0].dataViz_selectedRows).includes(guids[this.props.pairs.indexOf(pair)])))
+                .map(pair => ({ [ax0]: pair[this.props.axes[0]] }));
+        }
         var ax0 = this.props.axes[0];
         var ax1 = this.props.axes[1];
-        if (/\d/.test(this.props.pairs[0][ax0])) { this.byCategory = false; }
+        if (/\d/.test(this.props.pairs[0][ax0])) {
+            this.byCategory = false;
+        }
         return this.props.pairs
-            ?.filter(pair => (!this.incomingLinks.length ? true :  this.incomingLinks[0]!.selected && StrListCast(this.incomingLinks[0].selected).includes(guids[this.props.pairs.indexOf(pair)])))
-            .map(pair => ({ [ax0]: (pair[this.props.axes[0]]), [ax1]: (pair[this.props.axes[1]]) }))
+            ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.dataViz_selectedRows && StrListCast(this.incomingLinks[0].dataViz_selectedRows).includes(guids[this.props.pairs.indexOf(pair)])))
+            .map(pair => ({ [ax0]: pair[this.props.axes[0]], [ax1]: pair[this.props.axes[1]] }));
     }
 
-    @computed get defaultGraphTitle(){
+    @computed get defaultGraphTitle() {
         var ax0 = this.props.axes[0];
-        var ax1 = (this.props.axes.length>1)? this.props.axes[1] : undefined;
-        if (this.props.axes.length<2 || !/\d/.test(this.props.pairs[0][ax0]) || !ax1){
-            return ax0 + " Pie Chart";
-        }
-        else return ax1 + " by " + ax0 + " Pie Chart";
-    }    
+        var ax1 = this.props.axes.length > 1 ? this.props.axes[1] : undefined;
+        if (this.props.axes.length < 2 || !/\d/.test(this.props.pairs[0][ax0]) || !ax1) {
+            return ax0 + ' Pie Chart';
+        } else return ax1 + ' by ' + ax0 + ' Pie Chart';
+    }
 
     @computed get incomingLinks() {
         return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links
@@ -84,7 +86,7 @@ export class PieChart extends React.Component<PieChartProps> {
         this._disposers.chartData = reaction(
             () => ({ dataSet: this._piechartData, w: this.width, h: this.height }),
             ({ dataSet, w, h }) => {
-                if (dataSet!.length>0) {
+                if (dataSet!.length > 0) {
                     this.drawChart(dataSet, w, h);
                 }
             },
@@ -100,7 +102,7 @@ export class PieChart extends React.Component<PieChartProps> {
             //
             title: 'piechart doc selection' + this._currSelected,
         });
-        PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.dataDoc);
+        PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.rootDoc);
         return anchor;
     };
 
@@ -114,167 +116,177 @@ export class PieChart extends React.Component<PieChartProps> {
 
     // cleans data by converting numerical data to numbers and taking out empty cells
     data = (dataSet: any) => {
-        var validData = dataSet.filter((d: { [x: string]: unknown; }) => {
+        var validData = dataSet.filter((d: { [x: string]: unknown }) => {
             var valid = true;
             Object.keys(dataSet[0]).map(key => {
                 if (!d[key] || Number.isNaN(d[key])) valid = false;
-            })
+            });
             return valid;
-        })
-        var field = dataSet[0]? Object.keys(dataSet[0])[0] : undefined;
-        const data = validData.map((d: { [x: string]: any; }) => {
-            if (!this.byCategory) { return +d[field!].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '') }
-            return d[field!]
-        })
+        });
+        var field = dataSet[0] ? Object.keys(dataSet[0])[0] : undefined;
+        const data = validData.map((d: { [x: string]: any }) => {
+            if (!this.byCategory) {
+                return +d[field!].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '');
+            }
+            return d[field!];
+        });
         return data;
-    }
+    };
 
     // outlines the slice selected / hovered over
     highlightSelectedSlice = (changeSelectedVariables: boolean, svg: any, arc: any, radius: any, pointer: any, pieDataSet: any) => {
-            var index = -1;
-            var sameAsCurrent: boolean;
-            const selected = svg.selectAll('.slice').filter((d: any) => {
-                index++;
-                var p1 = [0,0]; // center of pie
-                var p3 = [arc.centroid(d)[0]*2, arc.centroid(d)[1]*2]; // outward peak of arc
-                var p2 = [radius*Math.sin(d.startAngle), -radius*Math.cos(d.startAngle)]; // start of arc
-                var p4 = [radius*Math.sin(d.endAngle), -radius*Math.cos(d.endAngle)]; // end of arc
+        var index = -1;
+        var sameAsCurrent: boolean;
+        const selected = svg.selectAll('.slice').filter((d: any) => {
+            index++;
+            var p1 = [0, 0]; // center of pie
+            var p3 = [arc.centroid(d)[0] * 2, arc.centroid(d)[1] * 2]; // outward peak of arc
+            var p2 = [radius * Math.sin(d.startAngle), -radius * Math.cos(d.startAngle)]; // start of arc
+            var p4 = [radius * Math.sin(d.endAngle), -radius * Math.cos(d.endAngle)]; // end of arc
 
-                // draw an imaginary horizontal line from the pointer to see how many times it crosses a slice edge
-                var lineCrossCount = 0;
-                // if for all 4 lines
-                if (Math.min(p1[1], p2[1])<=pointer[1] && pointer[1]<=Math.max(p1[1], p2[1])){ // within y bounds
-                    if (pointer[0] <= (pointer[1]-p1[1])*(p2[0]-p1[0])/(p2[1]-p1[1])+p1[0]) lineCrossCount++; } // intercepts x
-                if (Math.min(p2[1], p3[1])<=pointer[1] && pointer[1]<=Math.max(p2[1], p3[1])){
-                    if (pointer[0] <= (pointer[1]-p2[1])*(p3[0]-p2[0])/(p3[1]-p2[1])+p2[0]) lineCrossCount++; }
-                if (Math.min(p3[1], p4[1])<=pointer[1] && pointer[1]<=Math.max(p3[1], p4[1])){
-                    if (pointer[0] <= (pointer[1]-p3[1])*(p4[0]-p3[0])/(p4[1]-p3[1])+p3[0]) lineCrossCount++; }
-                if (Math.min(p4[1], p1[1])<=pointer[1] && pointer[1]<=Math.max(p4[1], p1[1])){
-                    if (pointer[0] <= (pointer[1]-p4[1])*(p1[0]-p4[0])/(p1[1]-p4[1])+p4[0]) lineCrossCount++; }
-                if (lineCrossCount % 2 != 0) { // inside the slice of it crosses an odd number of edges
-                    var showSelected = this.byCategory? pieDataSet[index] : this._piechartData[index];
-                    if (changeSelectedVariables){
-                        // for when a bar is selected - not just hovered over
-                        sameAsCurrent = (this._currSelected)? 
-                            (showSelected[Object.keys(showSelected)[0]]==this._currSelected![Object.keys(showSelected)[0]] 
-                        && showSelected[Object.keys(showSelected)[1]]==this._currSelected![Object.keys(showSelected)[1]])
-                            : 
-                            this._currSelected===showSelected;
-                        this._currSelected = sameAsCurrent? undefined: showSelected;
-                        this.selectedData = sameAsCurrent? undefined: d;
-                    }
-                    else this.hoverOverData = d;
-                    return true;
-                }
-                return false;
-            });
-            if (changeSelectedVariables){
-                if (sameAsCurrent!) this.curSliceSelected = undefined;
-                else  this.curSliceSelected = selected;
+            // draw an imaginary horizontal line from the pointer to see how many times it crosses a slice edge
+            var lineCrossCount = 0;
+            // if for all 4 lines
+            if (Math.min(p1[1], p2[1]) <= pointer[1] && pointer[1] <= Math.max(p1[1], p2[1])) {
+                // within y bounds
+                if (pointer[0] <= ((pointer[1] - p1[1]) * (p2[0] - p1[0])) / (p2[1] - p1[1]) + p1[0]) lineCrossCount++;
+            } // intercepts x
+            if (Math.min(p2[1], p3[1]) <= pointer[1] && pointer[1] <= Math.max(p2[1], p3[1])) {
+                if (pointer[0] <= ((pointer[1] - p2[1]) * (p3[0] - p2[0])) / (p3[1] - p2[1]) + p2[0]) lineCrossCount++;
             }
-    }
+            if (Math.min(p3[1], p4[1]) <= pointer[1] && pointer[1] <= Math.max(p3[1], p4[1])) {
+                if (pointer[0] <= ((pointer[1] - p3[1]) * (p4[0] - p3[0])) / (p4[1] - p3[1]) + p3[0]) lineCrossCount++;
+            }
+            if (Math.min(p4[1], p1[1]) <= pointer[1] && pointer[1] <= Math.max(p4[1], p1[1])) {
+                if (pointer[0] <= ((pointer[1] - p4[1]) * (p1[0] - p4[0])) / (p1[1] - p4[1]) + p4[0]) lineCrossCount++;
+            }
+            if (lineCrossCount % 2 != 0) {
+                // inside the slice of it crosses an odd number of edges
+                var showSelected = this.byCategory ? pieDataSet[index] : this._piechartData[index];
+                if (changeSelectedVariables) {
+                    // for when a bar is selected - not just hovered over
+                    sameAsCurrent = this._currSelected
+                        ? showSelected[Object.keys(showSelected)[0]] == this._currSelected![Object.keys(showSelected)[0]] && showSelected[Object.keys(showSelected)[1]] == this._currSelected![Object.keys(showSelected)[1]]
+                        : this._currSelected === showSelected;
+                    this._currSelected = sameAsCurrent ? undefined : showSelected;
+                    this.selectedData = sameAsCurrent ? undefined : d;
+                } else this.hoverOverData = d;
+                return true;
+            }
+            return false;
+        });
+        if (changeSelectedVariables) {
+            if (sameAsCurrent!) this.curSliceSelected = undefined;
+            else this.curSliceSelected = selected;
+        }
+    };
 
     // draws the pie chart
     drawChart = (dataSet: any, width: number, height: number) => {
         d3.select(this._piechartRef.current).select('svg').remove();
         d3.select(this._piechartRef.current).select('.tooltip').remove();
-        
-        var percentField = Object.keys(dataSet[0])[0]
-        var descriptionField = Object.keys(dataSet[0])[1]!
-        var radius = Math.min(width, height-this.props.margin.top-this.props.margin.bottom) /2
+
+        var percentField = Object.keys(dataSet[0])[0];
+        var descriptionField = Object.keys(dataSet[0])[1]!;
+        var radius = Math.min(width, height - this.props.margin.top - this.props.margin.bottom) / 2;
 
         // converts data into Objects
         var data = this.data(dataSet);
-        var pieDataSet = dataSet.filter((d: { [x: string]: unknown; }) => {
+        var pieDataSet = dataSet.filter((d: { [x: string]: unknown }) => {
             var valid = true;
             Object.keys(dataSet[0]).map(key => {
                 if (!d[key] || Number.isNaN(d[key])) valid = false;
-            })
+            });
             return valid;
         });
-        if (this.byCategory){
-            let uniqueCategories = [...new Set(data)]
+        if (this.byCategory) {
+            let uniqueCategories = [...new Set(data)];
             var pieStringDataSet: { frequency: number }[] = [];
-            for (let i=0; i<uniqueCategories.length; i++){
-                pieStringDataSet.push({frequency: 0, [percentField]: uniqueCategories[i]})
+            for (let i = 0; i < uniqueCategories.length; i++) {
+                pieStringDataSet.push({ frequency: 0, [percentField]: uniqueCategories[i] });
             }
-            for (let i=0; i<data.length; i++){
-                let sliceData = pieStringDataSet.filter((each: any) => each[percentField]==data[i])
+            for (let i = 0; i < data.length; i++) {
+                let sliceData = pieStringDataSet.filter((each: any) => each[percentField] == data[i]);
                 sliceData[0].frequency = sliceData[0].frequency + 1;
             }
-            pieDataSet = pieStringDataSet
-            percentField = Object.keys(pieDataSet[0])[0]
-            descriptionField = Object.keys(pieDataSet[0])[1]!
-            data = this.data(pieStringDataSet)
+            pieDataSet = pieStringDataSet;
+            percentField = Object.keys(pieDataSet[0])[0];
+            descriptionField = Object.keys(pieDataSet[0])[1]!;
+            data = this.data(pieStringDataSet);
         }
-        var trackDuplicates : {[key: string]: any} = {};
-        data.forEach((eachData: any) => !trackDuplicates[eachData]? trackDuplicates[eachData] = 0: null)
+        var trackDuplicates: { [key: string]: any } = {};
+        data.forEach((eachData: any) => (!trackDuplicates[eachData] ? (trackDuplicates[eachData] = 0) : null));
 
         // initial chart
         var svg = (this._piechartSvg = d3
             .select(this._piechartRef.current)
-            .append("svg")
-                .attr("class", "graph")
-                .attr("width", width + this.props.margin.right + this.props.margin.left)
-                .attr("height", height + this.props.margin.top + this.props.margin.bottom)
-            .append("g"));
-        let g = svg.append("g")
-            .attr("transform",
-                    "translate(" + (width/2 + this.props.margin.left) + "," + height/2 + ")");
+            .append('svg')
+            .attr('class', 'graph')
+            .attr('width', width + this.props.margin.right + this.props.margin.left)
+            .attr('height', height + this.props.margin.top + this.props.margin.bottom)
+            .append('g'));
+        let g = svg.append('g').attr('transform', 'translate(' + (width / 2 + this.props.margin.left) + ',' + height / 2 + ')');
         var pie = d3.pie();
-        var arc = d3.arc()
-            .innerRadius(0)
-            .outerRadius(radius);
+        var arc = d3.arc().innerRadius(0).outerRadius(radius);
 
         // click/hover
         const onPointClick = action((e: any) => this.highlightSelectedSlice(true, svg, arc, radius, d3.pointer(e), pieDataSet));
         const onHover = action((e: any) => {
-            const selected = this.highlightSelectedSlice(false, svg, arc, radius, d3.pointer(e), pieDataSet)
+            const selected = this.highlightSelectedSlice(false, svg, arc, radius, d3.pointer(e), pieDataSet);
             updateHighlights();
-        })
+        });
         const mouseOut = action((e: any) => {
             this.hoverOverData = undefined;
             updateHighlights();
-        })
+        });
         const updateHighlights = () => {
             const hoverOverSlice = this.hoverOverData;
             const selectedData = this.selectedData;
-            svg.selectAll('path').attr("class", function(d: any) { 
-                return ((selectedData && d.startAngle==selectedData.startAngle && d.endAngle==selectedData.endAngle)
-                    || ((hoverOverSlice && d.startAngle==hoverOverSlice.startAngle && d.endAngle==hoverOverSlice.endAngle)))? 'slice hover' : 'slice'; })
-        }
+            svg.selectAll('path').attr('class', function (d: any) {
+                return (selectedData && d.startAngle == selectedData.startAngle && d.endAngle == selectedData.endAngle) || (hoverOverSlice && d.startAngle == hoverOverSlice.startAngle && d.endAngle == hoverOverSlice.endAngle)
+                    ? 'slice hover'
+                    : 'slice';
+            });
+        };
 
         // drawing the slices
         var selected = this.selectedData;
-        var arcs = g.selectAll("arc")
-            .data(pie(data))
-            .enter()
-            .append("g")
-        arcs.append("path")
-            .attr("fill", (d, i)=>{ 
-                var possibleDataPoints = pieDataSet.filter((each: { [x: string]: any | { valueOf(): number; }; }) => {
+        var arcs = g.selectAll('arc').data(pie(data)).enter().append('g');
+        arcs.append('path')
+            .attr('fill', (d, i) => {
+                var possibleDataPoints = pieDataSet.filter((each: { [x: string]: any | { valueOf(): number } }) => {
                     try {
-                        return Number(each[percentField].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''))==Number(d.data)
+                        return Number(each[percentField].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '')) == Number(d.data);
                     } catch (error) {
-                        return each[percentField]==d.data
-                    }})
+                        return each[percentField] == d.data;
+                    }
+                });
                 var dataPoint;
-                if (possibleDataPoints.length==1) dataPoint = possibleDataPoints[0];
-                else{
-                    dataPoint = possibleDataPoints[trackDuplicates[d.data.toString()]]
+                if (possibleDataPoints.length == 1) dataPoint = possibleDataPoints[0];
+                else {
+                    dataPoint = possibleDataPoints[trackDuplicates[d.data.toString()]];
                     trackDuplicates[d.data.toString()] = trackDuplicates[d.data.toString()] + 1;
                 }
                 var sliceColor;
-                if (dataPoint){
+                if (dataPoint) {
                     var accessByName = dataPoint[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '');
                     var sliceColors = StrListCast(this.props.layoutDoc.pieSliceColors).map(each => each.split('::'));
-                    sliceColors.map(each => {if (each[0]==StrCast(accessByName)) sliceColor = each[1]});
+                    sliceColors.map(each => {
+                        if (each[0] == StrCast(accessByName)) sliceColor = each[1];
+                    });
                 }
-                return sliceColor? StrCast(sliceColor) : d3.schemeSet3[i]? d3.schemeSet3[i]: d3.schemeSet3[i%(d3.schemeSet3.length)] })
-                .attr("class", selected? 
-                function(d) {
-                    return (selected && d.startAngle==selected.startAngle && d.endAngle==selected.endAngle)? 'slice hover' : 'slice';
-                }: function(d) {return 'slice'})
+                return sliceColor ? StrCast(sliceColor) : d3.schemeSet3[i] ? d3.schemeSet3[i] : d3.schemeSet3[i % d3.schemeSet3.length];
+            })
+            .attr(
+                'class',
+                selected
+                    ? function (d) {
+                          return selected && d.startAngle == selected.startAngle && d.endAngle == selected.endAngle ? 'slice hover' : 'slice';
+                      }
+                    : function (d) {
+                          return 'slice';
+                      }
+            )
             .attr('d', arc)
             .on('click', onPointClick)
             .on('mouseover', onHover)
@@ -282,99 +294,106 @@ export class PieChart extends React.Component<PieChartProps> {
 
         // adding labels
         trackDuplicates = {};
-        data.forEach((eachData: any) => !trackDuplicates[eachData]? trackDuplicates[eachData] = 0: null)
-        arcs.append("text")
-            .attr("transform",function(d){ 
-                var centroid = arc.centroid(d as unknown as d3.DefaultArcObject)
-                var heightOffset = (centroid[1]/radius) * Math.abs(centroid[1])
-                return "translate("+ (centroid[0]+centroid[0]/(radius*.02)) + "," + (centroid[1]+heightOffset)  + ")"; })
-            .attr("text-anchor", "middle")
-            .text(function(d){ 
-                var possibleDataPoints = pieDataSet.filter((each: { [x: string]: any | { valueOf(): number; }; }) => {
+        data.forEach((eachData: any) => (!trackDuplicates[eachData] ? (trackDuplicates[eachData] = 0) : null));
+        arcs.append('text')
+            .attr('transform', function (d) {
+                var centroid = arc.centroid(d as unknown as d3.DefaultArcObject);
+                var heightOffset = (centroid[1] / radius) * Math.abs(centroid[1]);
+                return 'translate(' + (centroid[0] + centroid[0] / (radius * 0.02)) + ',' + (centroid[1] + heightOffset) + ')';
+            })
+            .attr('text-anchor', 'middle')
+            .text(function (d) {
+                var possibleDataPoints = pieDataSet.filter((each: { [x: string]: any | { valueOf(): number } }) => {
                     try {
-                        return Number(each[percentField].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''))==Number(d.data)
+                        return Number(each[percentField].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '')) == Number(d.data);
                     } catch (error) {
-                        return each[percentField]==d.data
-                    }})
+                        return each[percentField] == d.data;
+                    }
+                });
                 var dataPoint;
-                if (possibleDataPoints.length==1) dataPoint = possibleDataPoints[0];
-                else{
-                    dataPoint = possibleDataPoints[trackDuplicates[d.data.toString()]]
+                if (possibleDataPoints.length == 1) dataPoint = possibleDataPoints[0];
+                else {
+                    dataPoint = possibleDataPoints[trackDuplicates[d.data.toString()]];
                     trackDuplicates[d.data.toString()] = trackDuplicates[d.data.toString()] + 1;
                 }
-                return dataPoint? dataPoint[percentField]! + (!descriptionField? '' : (' - ' + dataPoint[descriptionField]))! : ''})
+                return dataPoint ? dataPoint[percentField]! + (!descriptionField ? '' : ' - ' + dataPoint[descriptionField])! : '';
+            });
     };
 
     @action changeSelectedColor = (color: string) => {
-        this.curSliceSelected.attr("fill", color);
-        var sliceName = this._currSelected[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '')
-        
-        const sliceColors = Cast(this.props.layoutDoc.pieSliceColors, listSpec("string"), null);
-        sliceColors.map(each => { if (each.split('::')[0] == sliceName) sliceColors.splice(sliceColors.indexOf(each), 1) });
+        this.curSliceSelected.attr('fill', color);
+        var sliceName = this._currSelected[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '');
+
+        const sliceColors = Cast(this.props.layoutDoc.pieSliceColors, listSpec('string'), null);
+        sliceColors.map(each => {
+            if (each.split('::')[0] == sliceName) sliceColors.splice(sliceColors.indexOf(each), 1);
+        });
         sliceColors.push(StrCast(sliceName + '::' + color));
     };
 
     render() {
         this.componentDidMount();
-        var titleAccessor: any='';
-        if (this.props.axes.length==2) titleAccessor = 'pieChart-title-'+this.props.axes[0]+'-'+this.props.axes[1];
-        else if (this.props.axes.length>0) titleAccessor = 'pieChart-title-'+this.props.axes[0];
+        var titleAccessor: any = '';
+        if (this.props.axes.length == 2) titleAccessor = 'dataViz_title_pieChart_' + this.props.axes[0] + '-' + this.props.axes[1];
+        else if (this.props.axes.length > 0) titleAccessor = 'dataViz_title_pieChart_' + this.props.axes[0];
         if (!this.props.layoutDoc[titleAccessor]) this.props.layoutDoc[titleAccessor] = this.defaultGraphTitle;
         if (!this.props.layoutDoc.pieSliceColors) this.props.layoutDoc.pieSliceColors = new List<string>();
         var selected: string;
-        var curSelectedSliceName;
-        if (this._currSelected){
-            curSelectedSliceName = StrCast(this._currSelected![this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''))
+        var curSelectedSliceName = '';
+        if (this._currSelected) {
+            curSelectedSliceName = StrCast(this._currSelected![this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''));
             selected = '{ ';
             Object.keys(this._currSelected).map(key => {
-                key!=''? selected += key + ': ' + this._currSelected[key] + ', ': '';
-            })
-            selected = selected.substring(0, selected.length-2);
+                key != '' ? (selected += key + ': ' + this._currSelected[key] + ', ') : '';
+            });
+            selected = selected.substring(0, selected.length - 2);
             selected += ' }';
-        }
-        else selected = 'none';
+        } else selected = 'none';
         var selectedSliceColor;
         var sliceColors = StrListCast(this.props.layoutDoc.pieSliceColors).map(each => each.split('::'));
-        sliceColors.map(each => {if (each[0]==curSelectedSliceName!) selectedSliceColor = each[1]});
+        sliceColors.map(each => {
+            if (each[0] == curSelectedSliceName!) selectedSliceColor = each[1];
+        });
 
-        if (this._piechartData.length>0){
-            return (
-                this.props.axes.length >= 1 ? (
+        if (this._piechartData.length > 0) {
+            return this.props.axes.length >= 1 ? (
                 <div className="chart-container">
-                    <div className="graph-title"> 
+                    <div className="graph-title">
                         <EditableText
                             val={StrCast(this.props.layoutDoc[titleAccessor])}
-                            setVal={undoable (action(val => this.props.layoutDoc[titleAccessor] = val as string), "Change Graph Title")}
-                            color={"black"}
+                            setVal={undoable(
+                                action(val => (this.props.layoutDoc[titleAccessor] = val as string)),
+                                'Change Graph Title'
+                            )}
+                            color={'black'}
                             size={Size.LARGE}
                             fillWidth
                         />
                     </div>
                     <div ref={this._piechartRef} />
-                    {selected != 'none' ? 
+                    {selected != 'none' ? (
                         <div className={'selected-data'}>
                             Selected: {selected}
                             &nbsp; &nbsp;
                             <ColorPicker
-                                tooltip={'Change Slice Color'} 
-                                type={Type.SEC} 
-                                icon={<FaFillDrip/>}
-                                selectedColor={selectedSliceColor? selectedSliceColor : this.curSliceSelected.attr("fill")}
-                                setSelectedColor={undoable (color => this.changeSelectedColor(color), "Change Selected Slice Color")}
+                                tooltip={'Change Slice Color'}
+                                type={Type.SEC}
+                                icon={<FaFillDrip />}
+                                selectedColor={selectedSliceColor ? selectedSliceColor : this.curSliceSelected.attr('fill')}
+                                setFinalColor={undoable(color => this.changeSelectedColor(color), 'Change Selected Slice Color')}
+                                setSelectedColor={undoable(color => this.changeSelectedColor(color), 'Change Selected Slice Color')}
                                 size={Size.XSMALL}
                             />
                         </div>
-                    : null}
+                    ) : null}
                 </div>
-                ) : <span className="chart-container"> {'first use table view to select a column to graph'}</span>
+            ) : (
+                <span className="chart-container"> {'first use table view to select a column to graph'}</span>
+            );
+        } else
+            return (
+                // when it is a brushed table and the incoming table doesn't have any rows selected
+                <div className="chart-container">Selected rows of data from the incoming DataVizBox to display.</div>
             );
-        }
-        else return (
-            // when it is a brushed table and the incoming table doesn't have any rows selected
-            <div className='chart-container'>
-                Selected rows of data from the incoming DataVizBox to display.
-            </div>
-        )
     }
-
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
index f56d34fa6..5579d6d80 100644
--- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx
+++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
@@ -1,4 +1,4 @@
-import { action, computed, } from 'mobx';
+import { action, computed } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, Field, StrListCast } from '../../../../../fields/Doc';
@@ -31,44 +31,45 @@ interface TableBoxProps {
 
 @observer
 export class TableBox extends React.Component<TableBoxProps> {
-
     // filters all data to just display selected data if brushed (created from an incoming link)
     @computed get _tableData() {
         if (this.incomingLinks.length! <= 0) return this.props.pairs;
-        var guids = StrListCast(this.props.layoutDoc.rowGuids);
-        return this.props.pairs?.filter(pair => this.incomingLinks[0]!.selected && StrListCast(this.incomingLinks[0].selected).includes(guids[this.props.pairs.indexOf(pair)]))
+        var guids = StrListCast(this.props.layoutDoc.dataViz_rowGuids);
+        return this.props.pairs?.filter(pair => this.incomingLinks[0]!.dataViz_selectedRows && StrListCast(this.incomingLinks[0].dataViz_selectedRows).includes(guids[this.props.pairs.indexOf(pair)]));
     }
 
     @computed get incomingLinks() {
         return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links
             .filter(link => {
-                return link.link_anchor_1 == this.props.rootDoc.draggedFrom}) // get links where this chart doc is the target of the link
+                return link.link_anchor_1 == this.props.rootDoc.draggedFrom;
+            }) // get links where this chart doc is the target of the link
             .map(link => DocCast(link.link_anchor_1)); // then return the source of the link
     }
 
     @computed get columns() {
-        if (!this.props.layoutDoc.rowGuids) this.props.layoutDoc.rowGuids = new List<string>();
-        const guids = Cast(this.props.layoutDoc.rowGuids, listSpec("string"), null);
-        if (guids.length==0) this.props.pairs.map(row => guids.push(Utils.GenerateGuid()));
-        return this._tableData.length ? Array.from(Object.keys(this._tableData[0])).filter(header => header!='' && header!=undefined) : [];
+        if (!this.props.layoutDoc.dataViz_rowGuids) this.props.layoutDoc.dataViz_rowGuids = new List<string>();
+        const guids = Cast(this.props.layoutDoc.dataViz_rowGuids, listSpec('string'), null);
+        if (guids.length == 0) this.props.pairs.map(row => guids.push(Utils.GenerateGuid()));
+        return this._tableData.length ? Array.from(Object.keys(this._tableData[0])).filter(header => header != '' && header != undefined) : [];
     }
 
     // updates the 'selected' field to no longer include rows that aren't in the table
     filterSelectedRowsDown() {
-        if (!this.props.layoutDoc.selected) this.props.layoutDoc.selected = new List<string>();
-        const selected = Cast(this.props.layoutDoc.selected, listSpec("string"), null);
-        const incomingSelected = this.incomingLinks.length? StrListCast(this.incomingLinks[0].selected) : undefined;
-        if (incomingSelected){
+        if (!this.props.layoutDoc.dataViz_selectedRows) this.props.layoutDoc.dataViz_selectedRows = new List<string>();
+        const selected = Cast(this.props.layoutDoc.dataViz_selectedRows, listSpec('string'), null);
+        const incomingSelected = this.incomingLinks.length ? StrListCast(this.incomingLinks[0].dataViz_selectedRows) : undefined;
+        if (incomingSelected) {
             selected.map(guid => {
-                if (!incomingSelected.includes(guid)) selected.splice(selected.indexOf(guid), 1)}); // filters through selected to remove guids that were removed in the incoming data
+                if (!incomingSelected.includes(guid)) selected.splice(selected.indexOf(guid), 1);
+            }); // filters through selected to remove guids that were removed in the incoming data
         }
     }
 
     render() {
         this.filterSelectedRowsDown();
-        if (this._tableData.length>0){
+        if (this._tableData.length > 0) {
             return (
-                <div className="table-container" style={{height: this.props.height}}>
+                <div className="table-container" style={{ height: this.props.height }}>
                     <table className="table">
                         <thead>
                             <tr className="table-row">
@@ -83,7 +84,8 @@ export class TableBox extends React.Component<TableBoxProps> {
                                                 style={{
                                                     color: this.props.axes.slice().reverse().lastElement() === col ? 'darkgreen' : this.props.axes.lastElement() === col ? 'darkred' : undefined,
                                                     background: this.props.axes.slice().reverse().lastElement() === col ? '#E3fbdb' : this.props.axes.lastElement() === col ? '#Fbdbdb' : undefined,
-                                                    fontWeight: 'bolder', border: '3px solid black'
+                                                    fontWeight: 'bolder',
+                                                    border: '3px solid black',
                                                 }}
                                                 onPointerDown={e => {
                                                     const downX = e.clientX;
@@ -91,12 +93,13 @@ export class TableBox extends React.Component<TableBoxProps> {
                                                     setupMoveUpEvents(
                                                         {},
                                                         e,
-                                                        e => { // dragging off a column to create a brushed DataVizBox
+                                                        e => {
+                                                            // dragging off a column to create a brushed DataVizBox
                                                             const sourceAnchorCreator = () => this.props.docView?.()!.rootDoc!;
                                                             const targetCreator = (annotationOn: Doc | undefined) => {
                                                                 const embedding = Doc.MakeEmbedding(this.props.docView?.()!.rootDoc!);
-                                                                embedding._dataVizView = DataVizView.TABLE;
-                                                                embedding._data_vizAxes = new List<string>([col, col]);
+                                                                embedding._dataViz = DataVizView.TABLE;
+                                                                embedding._dataViz_axes = new List<string>([col, col]);
                                                                 embedding._draggedFrom = this.props.docView?.()!.rootDoc!;
                                                                 embedding.annotationOn = annotationOn; //this.props.docView?.()!.rootDoc!;
                                                                 embedding.histogramBarColors = Field.Copy(this.props.layoutDoc.histogramBarColors);
@@ -142,25 +145,33 @@ export class TableBox extends React.Component<TableBoxProps> {
                         <tbody>
                             {this._tableData?.map((p, i) => {
                                 var containsData = false;
-                                var guid = StrListCast(this.props.layoutDoc.rowGuids)![this.props.pairs.indexOf(p)]
-                                this.columns.map(col => {if (p[col]!='' && p[col]!=null && p[col]!=undefined) containsData = true})
-                                if (containsData){
+                                var guid = StrListCast(this.props.layoutDoc.dataViz_rowGuids)![this.props.pairs.indexOf(p)];
+                                this.columns.map(col => {
+                                    if (p[col] != '' && p[col] != null && p[col] != undefined) containsData = true;
+                                });
+                                if (containsData) {
                                     return (
-                                        <tr key={i} className="table-row" onClick={action(e => {
+                                        <tr
+                                            key={i}
+                                            className="table-row"
+                                            onClick={action(e => {
                                                 // selecting a row
-                                                const selected = Cast(this.props.layoutDoc.selected, listSpec("string"), null);
+                                                const selected = Cast(this.props.layoutDoc.dataViz_selectedRows, listSpec('string'), null);
                                                 if (selected.includes(guid)) selected.splice(selected.indexOf(guid), 1);
                                                 else {
-                                                    selected.push(guid)};
-                                            })} style={{ background: StrListCast(this.props.layoutDoc.selected).includes(guid) ? 'lightgrey' : '', width: '110%' }}>
+                                                    selected.push(guid);
+                                                }
+                                            })}
+                                            style={{ background: StrListCast(this.props.layoutDoc.dataViz_selectedRows).includes(guid) ? 'lightgrey' : '', width: '110%' }}>
                                             {this.columns.map(col => {
-                                                    // each cell
-                                                    var colSelected = this.props.axes[0]==col || this.props.axes[1]==col;
-                                                    return (
-                                                    <td key={this.columns.indexOf(col)} style={{border: colSelected? '3px solid black' : '1px solid black', fontWeight: colSelected? 'bolder' : 'normal'}}>
+                                                // each cell
+                                                var colSelected = this.props.axes[0] == col || this.props.axes[1] == col;
+                                                return (
+                                                    <td key={this.columns.indexOf(col)} style={{ border: colSelected ? '3px solid black' : '1px solid black', fontWeight: colSelected ? 'bolder' : 'normal' }}>
                                                         {p[col]}
                                                     </td>
-                                             )})}
+                                                );
+                                            })}
                                         </tr>
                                     );
                                 }
@@ -169,12 +180,10 @@ export class TableBox extends React.Component<TableBoxProps> {
                     </table>
                 </div>
             );
-        }
-        else return (
-            // when it is a brushed table and the incoming table doesn't have any rows selected
-            <div className='chart-container'>
-                Selected rows of data from the incoming DataVizBox to display.
-            </div>
-        )
+        } else
+            return (
+                // when it is a brushed table and the incoming table doesn't have any rows selected
+                <div className="chart-container">Selected rows of data from the incoming DataVizBox to display.</div>
+            );
     }
 }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 90fb55290..c5dead708 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -113,7 +113,7 @@ export interface DocComponentView {
     getAnchor?: (addAsAnnotation: boolean, pinData?: PinProps) => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box)
     restoreView?: (viewSpec: Doc) => boolean;
     scrollPreview?: (docView: DocumentView, doc: Doc, focusSpeed: number, options: DocFocusOptions) => Opt<number>; // returns the duration of the focus
-    brushView?: (view: { width: number; height: number; panX: number; panY: number }) => void;
+    brushView?: (view: { width: number; height: number; panX: number; panY: number }, transTime: number) => void;
     getView?: (doc: Doc) => Promise<Opt<DocumentView>>; // returns a nested DocumentView for the specified doc or undefined
     addDocTab?: (doc: Doc, where: OpenWhere) => boolean; // determines how to add a document - used in following links to open the target ina local lightbox
     addDocument?: (doc: Doc | Doc[], annotationKey?: string) => boolean; // add a document (used only by collections)
@@ -1095,6 +1095,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     pointerEvents: this.rootDoc.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined,
                     minWidth: 50 * ffscale(),
                     maxHeight: `max(100%, ${20 * ffscale()}px)`,
+                    background: StrCast(this.layoutDoc._backgroundColor, 'rgba(0,0,0,0.2)'),
+                    color: lightOrDark(StrCast(this.layoutDoc._backgroundColor, 'black')),
                 }}>
                 <FormattedTextBox
                     {...this.props}
@@ -1112,10 +1114,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             </div>
         );
         const targetDoc = showTitle?.startsWith('_') ? this.layoutDoc : this.rootDoc;
-        const background = StrCast(
-            SharingManager.Instance.users.find(u => u.user.email === this.dataDoc.author)?.sharingDoc.headingColor,
-            Doc.UserDoc().layout_showTitle && [DocumentType.RTF, DocumentType.COL].includes(this.rootDoc.type as any) ? StrCast(Doc.SharingDoc().headingColor) : SettingsManager.Instance.userVariantColor
-        );
+        const background = StrCast(SharingManager.Instance.users.find(u => u.user.email === this.dataDoc.author)?.sharingDoc.headingColor, StrCast(Doc.SharingDoc().headingColor, SettingsManager.Instance.userVariantColor));
         const sidebarWidthPercent = +StrCast(this.layoutDoc.layout_sidebarWidthPercent).replace('%', '');
         const titleView = !showTitle ? null : (
             <div
@@ -1204,7 +1203,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
      * @returns a function that will wrap a JSX animation element wrapping any JSX element
      */
     public static AnimationEffect(renderDoc: JSX.Element, presEffectDoc: Opt<Doc>, root: Doc) {
-        const dir = presEffectDoc?.presEffectDirection ?? presEffectDoc?.followLinkAnimDirection;
+        const dir = presEffectDoc?.presentation_effectDirection ?? presEffectDoc?.followLinkAnimDirection;
         const effectProps = {
             left: dir === PresEffectDirection.Left,
             right: dir === PresEffectDirection.Right,
@@ -1212,10 +1211,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             bottom: dir === PresEffectDirection.Bottom,
             opposite: true,
             delay: 0,
-            duration: Cast(presEffectDoc?.presTransition, 'number', Cast(presEffectDoc?.followLinkTransitionTime, 'number', null)),
+            duration: Cast(presEffectDoc?.presentation_transition, 'number', Cast(presEffectDoc?.followLinkTransitionTime, 'number', null)),
         };
         //prettier-ignore
-        switch (StrCast(presEffectDoc?.presEffect, StrCast(presEffectDoc?.followLinkAnimEffect))) {
+        switch (StrCast(presEffectDoc?.presentation_effect, StrCast(presEffectDoc?.followLinkAnimEffect))) {
             default: 
             case PresEffect.None:       return renderDoc;
             case PresEffect.Zoom:       return <Zoom {...effectProps}>{renderDoc}</Zoom>;
@@ -1543,7 +1542,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
                             <div className="webBox-textHighlight">
                                 <ObserverJsxParser autoCloseVoidElements={true} key={42} onError={(e: any) => console.log('PARSE error', e)} renderInWrapper={false} jsx={StrCast(this.textHtmlOverlay)} />
                             </div>,
-                            { presEffect: this.htmlOverlayEffect ?? 'Zoom' } as any as Doc,
+                            { presentation_effect: this.htmlOverlayEffect ?? 'Zoom' } as any as Doc,
                             this.rootDoc
                         )}{' '}
                     </Fade>
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index 1b2209224..bf3c79cf9 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -267,7 +267,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         return ScriptCast(this.rootDoc.script);
     }
 
-    colorBatch:UndoManager.Batch|undefined;
+    colorBatch: UndoManager.Batch | undefined;
     /**
      * Color button
      */
@@ -285,7 +285,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 setFinalColor={value => {
                     this.colorScript?.script.run({ this: this.layoutDoc, self: this.rootDoc, value: value, _readOnly_: false });
                     this.colorBatch?.end();
-                    this.colorBatch= undefined;
+                    this.colorBatch = undefined;
                 }}
                 selectedColor={curColor}
                 type={Type.PRIM}
diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx
index 61711417f..40f48dafe 100644
--- a/src/client/views/nodes/FunctionPlotBox.tsx
+++ b/src/client/views/nodes/FunctionPlotBox.tsx
@@ -48,8 +48,8 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps>
             annotationOn: this.rootDoc,
         });
         PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), datarange: true } }, this.rootDoc);
-        anchor.presXRange = new List<number>(Array.from(this._plot.options.xAxis.domain));
-        anchor.presYRange = new List<number>(Array.from(this._plot.options.yAxis.domain));
+        anchor.config_xRange = new List<number>(Array.from(this._plot.options.xAxis.domain));
+        anchor.config_yRange = new List<number>(Array.from(this._plot.options.yAxis.domain));
         if (addAsAnnotation) this.addDocument(anchor);
         return anchor;
     };
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index d763753a5..c7ccd1ea0 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -22,7 +22,6 @@ import { DocumentType } from '../../documents/DocumentTypes';
 import { Networking } from '../../Network';
 import { DocumentManager } from '../../util/DocumentManager';
 import { DragManager } from '../../util/DragManager';
-import { SnappingManager } from '../../util/SnappingManager';
 import { undoBatch } from '../../util/UndoManager';
 import { ContextMenu } from '../../views/ContextMenu';
 import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
@@ -80,10 +79,10 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             this._getAnchor?.(this._savedAnnotations, false) ?? // use marquee anchor, otherwise, save zoom/pan as anchor
             Docs.Create.ConfigDocument({
                 title: 'ImgAnchor:' + this.rootDoc.title,
-                presPanX: NumCast(this.layoutDoc._freeform_panX),
-                presPanY: NumCast(this.layoutDoc._freeform_panY),
-                presViewScale: Cast(this.layoutDoc._freeform_scale, 'number', null),
-                presTransition: 1000,
+                config_panX: NumCast(this.layoutDoc._freeform_panX),
+                config_panY: NumCast(this.layoutDoc._freeform_panY),
+                config_viewScale: Cast(this.layoutDoc._freeform_scale, 'number', null),
+                presentation_transition: 1000,
                 annotationOn: this.rootDoc,
             });
         if (anchor) {
@@ -295,7 +294,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     choosePath(url: URL) {
         const lower = url.href.toLowerCase();
         if (url.protocol === 'data') return url.href;
-        if (url.href.indexOf(window.location.origin) === -1 && url.href.indexOf("dashblobstore") === -1) return Utils.CorsProxy(url.href);
+        if (url.href.indexOf(window.location.origin) === -1 && url.href.indexOf('dashblobstore') === -1) return Utils.CorsProxy(url.href);
         if (!/\.(png|jpg|jpeg|gif|webp)$/.test(lower)) return `/assets/unknown-file-icon-hi.png`;
 
         const ext = extname(url.href);
@@ -414,7 +413,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     @computed get content() {
         TraceMobx();
 
-        const backAlpha = DashColor(this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor)).alpha();
+        const backColor = DashColor(this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor));
+        const backAlpha = backColor.red() === 0 && backColor.green() === 0 && backColor.blue() === 0 ? backColor.alpha() : 1;
         const srcpath = this.layoutDoc.hideImage ? '' : this.paths[0];
         const fadepath = this.layoutDoc.hideImage ? '' : this.paths.lastElement();
         const { nativeWidth, nativeHeight, nativeOrientation } = this.nativeSize;
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index a6712a3db..673f711be 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -4,7 +4,7 @@ import { Doc, Field, FieldResult } from '../../../fields/Doc';
 import { List } from '../../../fields/List';
 import { RichTextField } from '../../../fields/RichTextField';
 import { ComputedField, ScriptField } from '../../../fields/ScriptField';
-import { DocCast, NumCast } from '../../../fields/Types';
+import { DocCast } from '../../../fields/Types';
 import { ImageField } from '../../../fields/URLField';
 import { returnAll, returnAlways, returnTrue } from '../../../Utils';
 import { Docs } from '../../documents/Documents';
@@ -13,6 +13,7 @@ import { CompiledScript, CompileScript, ScriptOptions } from '../../util/Scripti
 import { undoBatch } from '../../util/UndoManager';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
+import { DocumentIconContainer } from './DocumentIcon';
 import { OpenWhere } from './DocumentView';
 import { FieldView, FieldViewProps } from './FieldView';
 import { FormattedTextBox } from './formattedText/FormattedTextBox';
@@ -20,10 +21,6 @@ import { ImageBox } from './ImageBox';
 import './KeyValueBox.scss';
 import { KeyValuePair } from './KeyValuePair';
 import React = require('react');
-import { DocumentManager } from '../../util/DocumentManager';
-import { ScriptingGlobals } from '../../util/ScriptingGlobals';
-import { ScriptingRepl } from '../ScriptingRepl';
-import { DocumentIconContainer } from './DocumentIcon';
 
 export type KVPScript = {
     script: CompiledScript;
@@ -147,7 +144,15 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
         const self = this;
         const keys = Object.keys(ids).slice();
         //for (const key of [...keys.filter(id => id !== 'layout' && !id.includes('_')).sort(), ...keys.filter(id => id === 'layout' || id.includes('_')).sort()]) {
-        for (const key of keys.sort()) {
+        for (const key of keys.sort((a: string, b: string) => {
+            const a_ = a.split('_')[0];
+            const b_ = b.split('_')[0];
+            if (a_ < b_) return -1;
+            if (a_ > b_) return 1;
+            if (a === a_) return -1;
+            if (b === b_) return 1;
+            return a === b ? 0 : a < b ? -1 : 1;
+        })) {
             rows.push(
                 <KeyValuePair
                     doc={realDoc}
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index 710d41471..efb949a47 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -18,7 +18,7 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
         this.props.setContentView?.(this);
     }
     render() {
-        if (this.dataDoc.treeViewOpen === undefined) setTimeout(() => (this.dataDoc.treeViewOpen = true));
+        if (this.dataDoc.treeView_Open === undefined) setTimeout(() => (this.dataDoc.treeView_Open = true));
         return (
             <div className={`linkBox-container${this.props.isContentActive() ? '-interactive' : ''}`} style={{ background: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor) }}>
                 <ComparisonBox
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 4919ee94c..cf017d746 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -102,7 +102,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     }
     private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void);
     @computed get inlineTextAnnotations() {
-        return this.allMapMarkers.filter(a => a.textInlineAnnotations);
+        return this.allMapMarkers.filter(a => a.text_inlineAnnotations);
     }
 
     @observable private _map: google.maps.Map = null as unknown as google.maps.Map;
diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx
index a9154c5bb..b3ae8242d 100644
--- a/src/client/views/nodes/MapBox/MapBox2.tsx
+++ b/src/client/views/nodes/MapBox/MapBox2.tsx
@@ -100,7 +100,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     }
     private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void);
     @computed get inlineTextAnnotations() {
-        return this.allMapMarkers.filter(a => a.textInlineAnnotations);
+        return this.allMapMarkers.filter(a => a.text_inlineAnnotations);
     }
 
     @observable private _map: google.maps.Map = null as unknown as google.maps.Map;
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index fd4c6366b..758b49655 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -213,7 +213,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         );
     }
 
-    brushView = (view: { width: number; height: number; panX: number; panY: number }) => this._pdfViewer?.brushView(view);
+    brushView = (view: { width: number; height: number; panX: number; panY: number }, transTime: number) => this._pdfViewer?.brushView(view, transTime);
 
     sidebarAddDocTab = (doc: Doc, where: OpenWhere) => {
         if (DocListCast(this.props.Document[this.props.fieldKey + '_sidebar']).includes(doc) && !this.SidebarShown) {
@@ -224,7 +224,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
     focus = (anchor: Doc, options: DocFocusOptions) => {
         this._initialScrollTarget = anchor;
-        return this._pdfViewer?.scrollFocus(anchor, NumCast(anchor.y, NumCast(anchor.presViewScroll)), options);
+        return this._pdfViewer?.scrollFocus(anchor, NumCast(anchor.y, NumCast(anchor.config_scrollTop)), options);
     };
 
     getView = async (doc: Doc) => {
@@ -247,7 +247,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const anchor = annoAnchor ?? docAnchor();
         PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: true, pannable: true } }, this.rootDoc);
         anchor.text = ele?.textContent ?? '';
-        anchor.textHtml = ele?.innerHTML;
+        anchor.text_html = ele?.innerHTML;
         if (addAsAnnotation || annoAnchor) {
             this.addDocument(anchor);
         }
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index f5df42161..febf8341e 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -51,7 +51,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     public static sidebarResizerWidth = 5;
     static webStyleSheet = addStyleSheet();
     private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void);
-    private _setBrushViewer: undefined | ((view: { width: number; height: number; panX: number; panY: number }) => void);
+    private _setBrushViewer: undefined | ((view: { width: number; height: number; panX: number; panY: number }, transTime: number) => void);
     private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
     private _outerRef: React.RefObject<HTMLDivElement> = React.createRef();
     private _disposers: { [name: string]: IReactionDisposer } = {};
@@ -91,7 +91,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return DocListCast(this.dataDoc[this.annotationKey]);
     }
     @computed get inlineTextAnnotations() {
-        return this.allAnnotations.filter(a => a.textInlineAnnotations);
+        return this.allAnnotations.filter(a => a.text_inlineAnnotations);
     }
     @computed get webField() {
         return Cast(this.rootDoc[this.props.fieldKey], WebField)?.url;
@@ -133,7 +133,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 (this._iframe?.contentWindow as any)?.find(searchString, false, bwd, true);
             }
         } catch (e) {
-            console.log("WebBox search error", e)
+            console.log('WebBox search error', e);
         }
         return true;
     };
@@ -281,8 +281,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return this._savedAnnotations;
     };
 
-    setBrushViewer = (func?: (view: { width: number; height: number; panX: number; panY: number }) => void) => (this._setBrushViewer = func);
-    brushView = (view: { width: number; height: number; panX: number; panY: number }) => this._setBrushViewer?.(view);
+    setBrushViewer = (func?: (view: { width: number; height: number; panX: number; panY: number }, transTime: number) => void) => (this._setBrushViewer = func);
+    brushView = (view: { width: number; height: number; panX: number; panY: number }, transTime: number) => this._setBrushViewer?.(view, transTime);
     focus = (anchor: Doc, options: DocFocusOptions) => {
         if (anchor !== this.rootDoc && this._outerRef.current) {
             const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
@@ -303,7 +303,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     getView = (doc: Doc) => {
         if (Doc.AreProtosEqual(doc, this.rootDoc)) return new Promise<Opt<DocumentView>>(res => res(this.props.DocumentView?.()));
         if (this.rootDoc.layout_fieldKey === 'layout_icon') this.props.DocumentView?.().iconify();
-        const webUrl = WebCast(doc.presData)?.url;
+        const webUrl = WebCast(doc.config_data)?.url;
         if (this._url && webUrl && webUrl.href !== this._url) this.setData(webUrl.href);
         if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) this.toggleSidebar(false);
         return new Promise<Opt<DocumentView>>(res => DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv)));
@@ -334,7 +334,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             });
         PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: pinProps?.pinData ? true : false, pannable: true } }, this.rootDoc);
         anchor.text = ele?.textContent ?? '';
-        anchor.textHtml = ele?.innerHTML;
+        anchor.text_html = ele?.innerHTML;
         //addAsAnnotation &&
         this.addDocumentWrapper(anchor);
         return anchor;
@@ -761,7 +761,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     }
 
     addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => {
-        (doc instanceof Doc ? [doc] : doc).forEach(doc => (doc.presData = new WebField(this._url)));
+        (doc instanceof Doc ? [doc] : doc).forEach(doc => (doc.config_data = new WebField(this._url)));
         return this.addDocument(doc, annotationKey);
     };
 
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 1dcc445e8..eea4f513e 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -417,7 +417,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             DocListCast(Doc.MyPublishedDocs.data).forEach(term => (tr = this.hyperlinkTerm(tr, term, newAutoLinks)));
             tr = tr.setSelection(isNodeSel && false ? new NodeSelection(tr.doc.resolve(f)) : new TextSelection(tr.doc.resolve(f), tr.doc.resolve(t)));
             this._editorView?.dispatch(tr);
-            this.prepareForTyping();
+            // this.prepareForTyping();
         }
         oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.link_anchor_2 !== this.rootDoc).forEach(LinkManager.Instance.deleteLink);
     };
@@ -730,7 +730,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
 
     @undoBatch
     showTargetTrail = (anchor: Doc) => {
-        const trail = DocCast(anchor.presTrail);
+        const trail = DocCast(anchor.presentationTrail);
         if (trail) {
             Doc.ActivePresentation = trail;
             this.props.addDocTab(trail, OpenWhere.replaceRight);
@@ -1237,7 +1237,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         this._disposers.selected = reaction(
             () => this.props.isSelected(),
             action(selected => {
-                selected && this.prepareForTyping();
+                //selected && setTimeout(() => this.prepareForTyping());
                 if (FormattedTextBox._globalHighlights.has('Bold Text')) {
                     this.layoutDoc[DocCss] = this.layoutDoc[DocCss] + 1; // css change happens outside of mobx/react, so this will notify anyone interested in the layout that it has changed
                 }
@@ -1550,18 +1550,17 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
 
     // add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet.
     prepareForTyping = () => {
-        this._editorView?.dispatch(
-            this._editorView?.state.tr.setStoredMarks([
-                ...(this._editorView.state.storedMarks?.filter(mark => ![schema.marks.em, schema.marks.underline, schema.marks.pFontFamily, schema.marks.pFontSize, schema.marks.strong, schema.marks.pFontColor].includes(mark.type)) ?? []),
-                ...[schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })],
-                ...(Doc.UserDoc().fontColor !== 'transparent' && Doc.UserDoc().fontColor ? [schema.mark(schema.marks.pFontColor, { color: StrCast(Doc.UserDoc().fontColor) })] : []),
-                ...(Doc.UserDoc().fontStyle === 'italics' ? [schema.mark(schema.marks.em)] : []),
-                ...(Doc.UserDoc().textDecoration === 'underline' ? [schema.mark(schema.marks.underline)] : []),
-                ...(Doc.UserDoc().fontFamily ? [schema.mark(schema.marks.pFontFamily, { family: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FontFamily) })] : []),
-                ...(Doc.UserDoc().fontSize ? [schema.mark(schema.marks.pFontSize, { fontSize: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FontSize) })] : []),
-                ...(Doc.UserDoc().fontWeight === 'bold' ? [schema.mark(schema.marks.strong)] : []),
-            ])
-        );
+        if (!this._editorView) return;
+        const docDefaultMarks = [
+            ...(Doc.UserDoc().fontColor !== 'transparent' && Doc.UserDoc().fontColor ? [schema.mark(schema.marks.pFontColor, { color: StrCast(Doc.UserDoc().fontColor) })] : []),
+            ...(Doc.UserDoc().fontStyle === 'italics' ? [schema.mark(schema.marks.em)] : []),
+            ...(Doc.UserDoc().textDecoration === 'underline' ? [schema.mark(schema.marks.underline)] : []),
+            ...(Doc.UserDoc().fontFamily ? [schema.mark(schema.marks.pFontFamily, { family: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FontFamily) })] : []),
+            ...(Doc.UserDoc().fontSize ? [schema.mark(schema.marks.pFontSize, { fontSize: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FontSize) })] : []),
+            ...(Doc.UserDoc().fontWeight === 'bold' ? [schema.mark(schema.marks.strong)] : []),
+            ...[schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })],
+        ];
+        this._editorView?.dispatch(this._editorView?.state.tr.setStoredMarks(docDefaultMarks));
     };
 
     @action
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index 112a0d87e..ec11079b4 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -334,7 +334,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
 
     //Command to create a blank space
     bind('Space', (state: EditorState, dispatch: (tx: Transaction) => void) => {
-        if (GetEffectiveAcl(props.DataDoc) != AclEdit && GetEffectiveAcl(props.DataDoc) != AclAugment && GetEffectiveAcl(props.DataDoc) != AclAdmin) return true;
+        if (props.DataDoc && GetEffectiveAcl(props.DataDoc) != AclEdit && GetEffectiveAcl(props.DataDoc) != AclAugment && GetEffectiveAcl(props.DataDoc) != AclAdmin) return true;
         const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
         dispatch(splitMetadata(marks, state.tr));
         return false;
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 7c3e4baad..9c46459b0 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -104,7 +104,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
     _disposer: IReactionDisposer | undefined;
     componentDidMount() {
         this._disposer = reaction(
-            () => SelectionManager.Views(),
+            () => SelectionManager.Views().slice(),
             views => this.updateMenu(undefined, undefined, undefined)
         );
     }
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index aaedffc77..05f59d8fe 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -74,7 +74,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     constructor(props: any) {
         super(props);
         if (!PresBox.navigateToDocScript) {
-            PresBox.navigateToDocScript = ScriptField.MakeFunction('navigateToDoc(self.presentationTargetDoc, self)')!;
+            PresBox.navigateToDocScript = ScriptField.MakeFunction('navigateToDoc(self.presentation_targetDoc, self)')!;
         }
     }
 
@@ -116,7 +116,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return DocListCast(this.rootDoc[this.presFieldKey]);
     }
     @computed get tagDocs() {
-        return this.childDocs.map(doc => Cast(doc.presentationTargetDoc, Doc, null));
+        return this.childDocs.map(doc => Cast(doc.presentation_targetDoc, Doc, null));
     }
     @computed get itemIndex() {
         return NumCast(this.rootDoc._itemIndex);
@@ -125,10 +125,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return DocCast(this.childDocs[NumCast(this.rootDoc._itemIndex)]);
     }
     @computed get targetDoc() {
-        return Cast(this.activeItem?.presentationTargetDoc, Doc, null);
+        return Cast(this.activeItem?.presentation_targetDoc, Doc, null);
     }
     public static targetRenderedDoc = (doc: Doc) => {
-        const targetDoc = Cast(doc?.presentationTargetDoc, Doc, null);
+        const targetDoc = Cast(doc?.presentation_targetDoc, Doc, null);
         return targetDoc?.layout_unrendered ? DocCast(targetDoc.annotationOn) : targetDoc;
     };
     @computed get scrollable() {
@@ -149,7 +149,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @computed get selectedDoc() {
         return this.selectedDocumentView?.rootDoc;
     }
-    isActiveItemTarget = (layoutDoc: Doc) => this.activeItem?.presentationTargetDoc === layoutDoc;
+    isActiveItemTarget = (layoutDoc: Doc) => this.activeItem?.presentation_targetDoc === layoutDoc;
     clearSelectedArray = () => this.selectedArray.clear();
     addToSelectedArray = action((doc: Doc) => this.selectedArray.add(doc));
     removeFromSelectedArray = action((doc: Doc) => this.selectedArray.delete(doc));
@@ -196,18 +196,18 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
         this.turnOffEdit(true);
         this._disposers.selection = reaction(
-            () => SelectionManager.Views(),
+            () => SelectionManager.Views().slice(),
             views => (!PresBox.Instance || views.some(view => view.props.Document === this.rootDoc)) && this.updateCurrentPresentation(),
             { fireImmediately: true }
         );
         this._disposers.editing = reaction(
-            () => this.layoutDoc.presStatus === PresStatus.Edit,
+            () => this.layoutDoc.presentation_status === PresStatus.Edit,
             editing => {
                 if (editing) {
                     this.childDocs.forEach(doc => {
-                        if (doc.presIndexed !== undefined) {
+                        if (doc.presentation_indexed !== undefined) {
                             this.progressivizedItems(doc)?.forEach(indexedDoc => (indexedDoc.opacity = undefined));
-                            doc.presIndexed = Math.min(this.progressivizedItems(doc)?.length ?? 0, 1);
+                            doc.presentation_indexed = Math.min(this.progressivizedItems(doc)?.length ?? 0, 1);
                         }
                     });
                 }
@@ -224,10 +224,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     _mediaTimer!: [NodeJS.Timeout, Doc];
     // 'Play on next' for audio or video therefore first navigate to the audio/video before it should be played
     startTempMedia = (targetDoc: Doc, activeItem: Doc) => {
-        const duration: number = NumCast(activeItem.presEndTime) - NumCast(activeItem.presStartTime);
+        const duration: number = NumCast(activeItem.config_clipEnd) - NumCast(activeItem.config_clipStart);
         if ([DocumentType.VID, DocumentType.AUDIO].includes(targetDoc.type as any)) {
             const targMedia = DocumentManager.Instance.getDocumentView(targetDoc);
-            targMedia?.ComponentView?.playFrom?.(NumCast(activeItem.presStartTime), NumCast(activeItem.presStartTime) + duration);
+            targMedia?.ComponentView?.playFrom?.(NumCast(activeItem.config_clipStart), NumCast(activeItem.config_clipStart) + duration);
         }
     };
 
@@ -251,12 +251,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             (nextSelected: number, force = false) =>
             () => {
                 if (nextSelected < this.childDocs.length) {
-                    if (force || this.childDocs[nextSelected].groupWithUp) {
+                    if (force || this.childDocs[nextSelected].presentation_groupWithUp) {
                         this.addToSelectedArray(this.childDocs[nextSelected]);
-                        const serial = nextSelected + 1 < this.childDocs.length && NumCast(this.childDocs[nextSelected + 1].groupWithUp) > 1;
+                        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.presDuration) - NumCast(this.activeItem.presTransition);
+                                const waitTime = NumCast(this.activeItem.presentation_duration) - NumCast(this.activeItem.presentation_transition);
                                 await new Promise<void>(res => setTimeout(() => res(), Math.max(0, waitTime)));
                                 doGroupWithUp(nextSelected + 1)();
                             });
@@ -274,7 +274,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     // docs within a slide target that will be progressively revealed
     progressivizedItems = (doc: Doc) => {
         const targetList = PresBox.targetRenderedDoc(doc);
-        if (doc.presIndexed !== undefined && targetList) {
+        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']);
             return listItems.filter(doc => !doc.layout_unrendered);
         }
@@ -283,7 +283,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     next = () => {
         const progressiveReveal = (first: boolean) => {
-            const presIndexed = Cast(this.activeItem?.presIndexed, 'number', null);
+            const presIndexed = Cast(this.activeItem?.presentation_indexed, 'number', null);
             if (presIndexed !== undefined) {
                 const targetRenderedDoc = PresBox.targetRenderedDoc(this.activeItem);
                 targetRenderedDoc._dataTransition = 'all 1s';
@@ -296,8 +296,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         const targetView = listItems && DocumentManager.Instance.getFirstDocumentView(listItemDoc);
                         Doc.linkFollowUnhighlight();
                         Doc.HighlightDoc(listItemDoc);
-                        listItemDoc.presEffect = this.activeItem.presBulletEffect;
-                        listItemDoc.presTransition = 500;
+                        listItemDoc.presentation_effect = this.activeItem.presBulletEffect;
+                        listItemDoc.presentation_transition = 500;
                         targetView?.setAnimEffect(listItemDoc, 500);
                         if (targetView?.docView && this.activeItem.presBulletExpand) {
                             targetView.docView._animateScalingTo = 1.2;
@@ -308,7 +308,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             });
                         }
                         listItemDoc.opacity = undefined;
-                        this.activeItem.presIndexed = presIndexed + 1;
+                        this.activeItem.presentation_indexed = presIndexed + 1;
                     }
                     return true;
                 }
@@ -322,7 +322,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this.nextSlide(curLast + 1 === this.childDocs.length ? (this.layoutDoc.presLoop ? 0 : curLast) : curLast + 1);
             progressiveReveal(true); // shows first progressive document, but without a transition effect
         } else {
-            if (this.childDocs[this.itemIndex + 1] === undefined && (this.layoutDoc.presLoop || this.layoutDoc.presStatus === PresStatus.Edit)) {
+            if (this.childDocs[this.itemIndex + 1] === undefined && (this.layoutDoc.presLoop || this.layoutDoc.presentation_status === PresStatus.Edit)) {
                 // Case 2: Last slide and presLoop is toggled ON or it is in Edit mode
                 this.nextSlide(0);
                 progressiveReveal(true); // shows first progressive document, but without a transition effect
@@ -338,9 +338,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const activeItem: Doc = this.activeItem;
         let prevSelected = this.itemIndex;
         // Functionality for group with up
-        let didZoom = activeItem.presMovement;
-        for (; prevSelected > 0 && this.childDocs[Math.max(0, prevSelected - 1)].groupWithUp; prevSelected--) {
-            didZoom = didZoom === 'none' ? this.childDocs[prevSelected].presMovement : didZoom;
+        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;
         }
         if (activeItem && this.childDocs[this.itemIndex - 1] !== undefined) {
             // Case 2: There are no other frames so it should go to the previous slide
@@ -361,20 +361,20 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         Doc.UnBrushAllDocs();
         if (index >= 0 && index < this.childDocs.length) {
             this.rootDoc._itemIndex = index;
-            if (from?.mediaStopTriggerList && this.layoutDoc.presStatus !== PresStatus.Edit) {
+            if (from?.mediaStopTriggerList && this.layoutDoc.presentation_status !== PresStatus.Edit) {
                 DocListCast(from.mediaStopTriggerList).forEach(this.stopTempMedia);
             }
-            if (from?.mediaStop === 'auto' && this.layoutDoc.presStatus !== PresStatus.Edit) {
-                this.stopTempMedia(from.presentationTargetDoc);
+            if (from?.mediaStop === 'auto' && this.layoutDoc.presentation_status !== PresStatus.Edit) {
+                this.stopTempMedia(from.presentation_targetDoc);
             }
             // If next slide is audio / video 'Play automatically' then the next slide should be played
-            if (this.layoutDoc.presStatus !== PresStatus.Edit && (this.targetDoc.type === DocumentType.AUDIO || this.targetDoc.type === DocumentType.VID) && this.activeItem.mediaStart === 'auto') {
+            if (this.layoutDoc.presentation_status !== PresStatus.Edit && (this.targetDoc.type === DocumentType.AUDIO || this.targetDoc.type === DocumentType.VID) && this.activeItem.mediaStart === 'auto') {
                 this.startTempMedia(this.targetDoc, this.activeItem);
             }
             if (!group) this.clearSelectedArray();
             this.childDocs[index] && this.addToSelectedArray(this.childDocs[index]); //Update selected array
             this.turnOffEdit();
-            this.navigateToActiveItem(finished); //Handles movement to element only when presTrail is list
+            this.navigateToActiveItem(finished); //Handles movement to element only when presentationTrail is list
             this.doHideBeforeAfter(); //Handles hide after/before
         }
     });
@@ -398,33 +398,33 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     playAnnotation = (anno: AudioField) => {};
     @action
-    static restoreTargetDocView(bestTargetView: Opt<DocumentView>, activeItem: Doc, transTime: number, pinDocLayout: boolean = BoolCast(activeItem.presPinLayout), pinDataTypes?: pinDataTypes, targetDoc?: Doc) {
+    static restoreTargetDocView(bestTargetView: Opt<DocumentView>, activeItem: Doc, transTime: number, pinDocLayout: boolean = BoolCast(activeItem.config_pinLayout), pinDataTypes?: pinDataTypes, targetDoc?: Doc) {
         const bestTarget = bestTargetView?.rootDoc ?? (targetDoc?.layout_unrendered ? DocCast(targetDoc?.annotationOn) : targetDoc);
         if (!bestTarget) return;
         let changed = false;
         if (pinDocLayout) {
             if (
-                bestTarget.x !== NumCast(activeItem.presX, NumCast(bestTarget.x)) ||
-                bestTarget.y !== NumCast(activeItem.presY, NumCast(bestTarget.y)) ||
-                bestTarget.rotation !== NumCast(activeItem.presRotation, NumCast(bestTarget.rotation)) ||
-                bestTarget.width !== NumCast(activeItem.presWidth, NumCast(bestTarget.width)) ||
-                bestTarget.height !== NumCast(activeItem.presHeight, NumCast(bestTarget.height))
+                bestTarget.x !== NumCast(activeItem.config_x, NumCast(bestTarget.x)) ||
+                bestTarget.y !== NumCast(activeItem.config_y, NumCast(bestTarget.y)) ||
+                bestTarget.rotation !== NumCast(activeItem.config_rotation, NumCast(bestTarget.rotation)) ||
+                bestTarget.width !== NumCast(activeItem.config_width, NumCast(bestTarget.width)) ||
+                bestTarget.height !== NumCast(activeItem.config_height, NumCast(bestTarget.height))
             ) {
                 bestTarget._dataTransition = `all ${transTime}ms`;
-                bestTarget.x = NumCast(activeItem.presX, NumCast(bestTarget.x));
-                bestTarget.y = NumCast(activeItem.presY, NumCast(bestTarget.y));
-                bestTarget.rotation = NumCast(activeItem.presRotation, NumCast(bestTarget.rotation));
-                bestTarget.width = NumCast(activeItem.presWidth, NumCast(bestTarget.width));
-                bestTarget.height = NumCast(activeItem.presHeight, NumCast(bestTarget.height));
+                bestTarget.x = NumCast(activeItem.config_x, NumCast(bestTarget.x));
+                bestTarget.y = NumCast(activeItem.config_y, NumCast(bestTarget.y));
+                bestTarget.rotation = NumCast(activeItem.config_rotation, NumCast(bestTarget.rotation));
+                bestTarget.width = NumCast(activeItem.config_width, NumCast(bestTarget.width));
+                bestTarget.height = NumCast(activeItem.config_height, NumCast(bestTarget.height));
                 setTimeout(() => (bestTarget._dataTransition = undefined), transTime + 10);
                 changed = true;
             }
         }
 
-        const activeFrame = activeItem.presActiveFrame ?? activeItem.presCurrentFrame;
+        const activeFrame = activeItem.config_activeFrame ?? activeItem.config_currentFrame;
         if (activeFrame !== undefined) {
-            const transTime = NumCast(activeItem.presTransition, 500);
-            const acontext = activeItem.presActiveFrame !== undefined ? DocCast(DocCast(activeItem.presentationTargetDoc).embedContainer) : DocCast(activeItem.presentationTargetDoc);
+            const transTime = 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;
             if (context) {
                 const ffview = DocumentManager.Instance.getFirstDocumentView(context)?.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
@@ -434,73 +434,73 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 }
             }
         }
-        if ((pinDataTypes?.dataview && activeItem.presData !== undefined) || (!pinDataTypes && activeItem.presData !== undefined)) {
+        if ((pinDataTypes?.dataview && activeItem.config_data !== undefined) || (!pinDataTypes && activeItem.config_data !== undefined)) {
             bestTarget._dataTransition = `all ${transTime}ms`;
             const fkey = Doc.LayoutFieldKey(bestTarget);
             const setData = bestTargetView?.ComponentView?.setData;
-            if (setData) setData(activeItem.presData);
-            else Doc.GetProto(bestTarget)[fkey] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData;
-            bestTarget[fkey + '_usePath'] = activeItem.presUsePath;
+            if (setData) setData(activeItem.config_data);
+            else Doc.GetProto(bestTarget)[fkey] = activeItem.config_data instanceof ObjectField ? activeItem.config_data[Copy]() : activeItem.config_data;
+            bestTarget[fkey + '_usePath'] = activeItem.config_usePath;
             setTimeout(() => (bestTarget._dataTransition = undefined), transTime + 10);
         }
-        if (pinDataTypes?.datarange || (!pinDataTypes && activeItem.presXRange !== undefined)) {
-            if (bestTarget.xRange !== activeItem.presXRange) {
-                bestTarget.xRange = (activeItem.presXRange as ObjectField)?.[Copy]();
+        if (pinDataTypes?.datarange || (!pinDataTypes && activeItem.config_xRange !== undefined)) {
+            if (bestTarget.xRange !== activeItem.config_xRange) {
+                bestTarget.xRange = (activeItem.config_xRange as ObjectField)?.[Copy]();
                 changed = true;
             }
-            if (bestTarget.yRange !== activeItem.presYRange) {
-                bestTarget.yRange = (activeItem.presYRange as ObjectField)?.[Copy]();
+            if (bestTarget.yRange !== activeItem.config_yRange) {
+                bestTarget.yRange = (activeItem.config_yRange as ObjectField)?.[Copy]();
                 changed = true;
             }
         }
-        if (pinDataTypes?.clippable || (!pinDataTypes && activeItem.presClipWidth !== undefined)) {
+        if (pinDataTypes?.clippable || (!pinDataTypes && activeItem.config_clipWidth !== undefined)) {
             const fkey = '_' + Doc.LayoutFieldKey(bestTarget);
-            if (bestTarget[fkey + '_clipWidth'] !== activeItem.presClipWidth) {
-                bestTarget[fkey + '_clipWidth'] = activeItem.presClipWidth;
+            if (bestTarget[fkey + '_clipWidth'] !== activeItem.config_clipWidth) {
+                bestTarget[fkey + '_clipWidth'] = activeItem.config_clipWidth;
                 changed = true;
             }
         }
-        if (pinDataTypes?.temporal || (!pinDataTypes && activeItem.presStartTime !== undefined)) {
-            if (bestTarget._layout_currentTimecode !== activeItem.presStartTime) {
-                bestTarget._layout_currentTimecode = activeItem.presStartTime;
+        if (pinDataTypes?.temporal || (!pinDataTypes && activeItem.config_clipStart !== undefined)) {
+            if (bestTarget._layout_currentTimecode !== activeItem.config_clipStart) {
+                bestTarget._layout_currentTimecode = activeItem.config_clipStart;
                 changed = true;
             }
         }
-        if (pinDataTypes?.inkable || (!pinDataTypes && (activeItem.presFillColor !== undefined || activeItem.color !== undefined))) {
-            if (bestTarget.fillColor !== activeItem.presFillColor) {
-                Doc.GetProto(bestTarget).fillColor = activeItem.presFillColor;
+        if (pinDataTypes?.inkable || (!pinDataTypes && (activeItem.config_fillColor !== undefined || activeItem.color !== undefined))) {
+            if (bestTarget.fillColor !== activeItem.config_fillColor) {
+                Doc.GetProto(bestTarget).fillColor = activeItem.config_fillColor;
                 changed = true;
             }
-            if (bestTarget.color !== activeItem.presColor) {
-                Doc.GetProto(bestTarget).color = activeItem.presColor;
+            if (bestTarget.color !== activeItem.config_color) {
+                Doc.GetProto(bestTarget).color = activeItem.config_color;
                 changed = true;
             }
             if (bestTarget.width !== activeItem.width) {
-                bestTarget._width = NumCast(activeItem.presWidth, NumCast(bestTarget.width));
+                bestTarget._width = NumCast(activeItem.config_width, NumCast(bestTarget.width));
                 changed = true;
             }
             if (bestTarget.height !== activeItem.height) {
-                bestTarget._height = NumCast(activeItem.presHeight, NumCast(bestTarget.height));
+                bestTarget._height = NumCast(activeItem.config_height, NumCast(bestTarget.height));
                 changed = true;
             }
         }
-        if ((pinDataTypes?.type_collection && activeItem.presViewType !== undefined) || (!pinDataTypes && activeItem.presViewType !== undefined)) {
-            if (bestTarget._type_collection !== activeItem.presViewType) {
-                bestTarget._type_collection = activeItem.presViewType;
+        if ((pinDataTypes?.type_collection && activeItem.config_viewType !== undefined) || (!pinDataTypes && activeItem.config_viewType !== undefined)) {
+            if (bestTarget._type_collection !== activeItem.config_viewType) {
+                bestTarget._type_collection = activeItem.config_viewType;
                 changed = true;
             }
         }
 
-        if ((pinDataTypes?.filters && activeItem.presDocFilters !== undefined) || (!pinDataTypes && activeItem.presDocFilters !== undefined)) {
-            if (bestTarget.childFilters !== activeItem.presDocFilters) {
-                bestTarget.childFilters = ObjectField.MakeCopy(activeItem.presDocFilters as ObjectField) || new List<string>([]);
+        if ((pinDataTypes?.filters && activeItem.config_docFilters !== undefined) || (!pinDataTypes && activeItem.config_docFilters !== undefined)) {
+            if (bestTarget.childFilters !== activeItem.config_docFilters) {
+                bestTarget.childFilters = ObjectField.MakeCopy(activeItem.config_docFilters as ObjectField) || new List<string>([]);
                 changed = true;
             }
         }
 
-        if ((pinDataTypes?.pivot && activeItem.presPivotField !== undefined) || (!pinDataTypes && activeItem.presPivotField !== undefined)) {
-            if (bestTarget.pivotField !== activeItem.presPivotField) {
-                bestTarget.pivotField = activeItem.presPivotField;
+        if ((pinDataTypes?.pivot && activeItem.config_pivotField !== undefined) || (!pinDataTypes && activeItem.config_pivotField !== undefined)) {
+            if (bestTarget.pivotField !== activeItem.config_pivotField) {
+                bestTarget.pivotField = activeItem.config_pivotField;
                 bestTarget._prevFilterIndex = 1; // need to revisit this...see CollectionTimeView
                 changed = true;
             }
@@ -509,21 +509,21 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             changed = true;
         }
 
-        if (pinDataTypes?.scrollable || (!pinDataTypes && activeItem.presViewScroll !== undefined)) {
-            if (bestTarget._layout_scrollTop !== activeItem.presViewScroll) {
-                bestTarget._layout_scrollTop = activeItem.presViewScroll;
+        if (pinDataTypes?.scrollable || (!pinDataTypes && activeItem.config_scrollTop !== undefined)) {
+            if (bestTarget._layout_scrollTop !== activeItem.config_scrollTop) {
+                bestTarget._layout_scrollTop = activeItem.config_scrollTop;
                 changed = true;
-                const contentBounds = Cast(activeItem.presPinViewBounds, listSpec('number'));
+                const contentBounds = Cast(activeItem.config_viewBounds, listSpec('number'));
                 if (contentBounds) {
                     const dv = DocumentManager.Instance.getDocumentView(bestTarget)?.ComponentView;
-                    dv?.brushView?.({ panX: (contentBounds[0] + contentBounds[2]) / 2, panY: (contentBounds[1] + contentBounds[3]) / 2, width: contentBounds[2] - contentBounds[0], height: contentBounds[3] - contentBounds[1] });
+                    dv?.brushView?.({ panX: (contentBounds[0] + contentBounds[2]) / 2, panY: (contentBounds[1] + contentBounds[3]) / 2, width: contentBounds[2] - contentBounds[0], height: contentBounds[3] - contentBounds[1] }, transTime);
                 }
             }
         }
-        if (pinDataTypes?.dataannos || (!pinDataTypes && activeItem.presAnnotations !== undefined)) {
+        if (pinDataTypes?.dataannos || (!pinDataTypes && activeItem.config_annotations !== undefined)) {
             const fkey = Doc.LayoutFieldKey(bestTarget);
             const oldItems = DocListCast(bestTarget[fkey + '_annotations']).filter(doc => doc.layout_unrendered);
-            const newItems = DocListCast(activeItem.presAnnotations).map(doc => {
+            const newItems = DocListCast(activeItem.config_annotations).map(doc => {
                 doc.hidden = false;
                 return doc;
             });
@@ -536,11 +536,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const newList = new List<Doc>([...oldItems, ...hiddenItems, ...newItems]);
             Doc.GetProto(bestTarget)[fkey + '_annotations'] = newList;
         }
-        if (pinDataTypes?.poslayoutview || (!pinDataTypes && activeItem.presPinLayoutData !== undefined)) {
+        if (pinDataTypes?.poslayoutview || (!pinDataTypes && activeItem.config_pinLayoutData !== undefined)) {
             changed = true;
             const layoutField = Doc.LayoutFieldKey(bestTarget);
             const transitioned = new Set<Doc>();
-            StrListCast(activeItem.presPinLayoutData)
+            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 })
                 .forEach(async data => {
                     const doc = DocCast(DocServer.GetCachedRefField(data.id));
@@ -562,8 +562,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 });
             setTimeout(() => Array.from(transitioned).forEach(action(doc => (doc._dataTransition = undefined))), transTime + 10);
         }
-        if ((pinDataTypes?.pannable || (!pinDataTypes && (activeItem.presPinViewBounds !== undefined || activeItem.presPanX !== undefined || activeItem.presViewScale !== undefined))) && !bestTarget._isGroup) {
-            const contentBounds = Cast(activeItem.presPinViewBounds, listSpec('number'));
+        if ((pinDataTypes?.pannable || (!pinDataTypes && (activeItem.config_viewBounds !== undefined || activeItem.config_panX !== undefined || activeItem.config_viewScale !== undefined))) && !bestTarget._isGroup) {
+            const contentBounds = Cast(activeItem.config_viewBounds, listSpec('number'));
             if (contentBounds) {
                 const viewport = { panX: (contentBounds[0] + contentBounds[2]) / 2, panY: (contentBounds[1] + contentBounds[3]) / 2, width: contentBounds[2] - contentBounds[0], height: contentBounds[3] - contentBounds[1] };
                 bestTarget._freeform_panX = viewport.panX;
@@ -572,14 +572,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 if (dv) {
                     changed = true;
                     const computedScale = NumCast(activeItem.presZoom, 1) * Math.min(dv.props.PanelWidth() / viewport.width, dv.props.PanelHeight() / viewport.height);
-                    activeItem.presMovement === PresMovement.Zoom && (bestTarget._freeform_scale = computedScale);
-                    dv.ComponentView?.brushView?.(viewport);
+                    activeItem.presentation_movement === PresMovement.Zoom && (bestTarget._freeform_scale = computedScale);
+                    dv.ComponentView?.brushView?.(viewport, transTime);
                 }
             } else {
-                if (bestTarget._freeform_panX !== activeItem.presPanX || bestTarget._freeform_panY !== activeItem.presPanY || bestTarget._freeform_scale !== activeItem.presViewScale) {
-                    bestTarget._freeform_panX = activeItem.presPanX ?? bestTarget._freeform_panX;
-                    bestTarget._freeform_panY = activeItem.presPanY ?? bestTarget._freeform_panY;
-                    bestTarget._freeform_scale = activeItem.presViewScale ?? bestTarget._freeform_scale;
+                if (bestTarget._freeform_panX !== activeItem.config_panX || bestTarget._freeform_panY !== activeItem.config_panY || bestTarget._freeform_scale !== activeItem.config_viewScale) {
+                    bestTarget._freeform_panX = activeItem.config_panX ?? bestTarget._freeform_panX;
+                    bestTarget._freeform_panY = activeItem.config_panY ?? bestTarget._freeform_panY;
+                    bestTarget._freeform_scale = activeItem.config_viewScale ?? bestTarget._freeform_scale;
                     changed = true;
                 }
             }
@@ -594,17 +594,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     /// target doc when navigating to it.
     @action
     static pinDocView(pinDoc: Doc, pinProps: PinProps, targetDoc: Doc) {
+        pinDoc.presentation = true;
+        pinDoc.config = '';
         if (pinProps.pinDocLayout) {
-            pinDoc.presPinLayout = true;
-            pinDoc.presX = NumCast(targetDoc.x);
-            pinDoc.presY = NumCast(targetDoc.y);
-            pinDoc.presRotation = NumCast(targetDoc.rotation);
-            pinDoc.presWidth = NumCast(targetDoc.width);
-            pinDoc.presHeight = NumCast(targetDoc.height);
+            pinDoc.config_pinLayout = true;
+            pinDoc.config_x = NumCast(targetDoc.x);
+            pinDoc.config_y = NumCast(targetDoc.y);
+            pinDoc.config_rotation = NumCast(targetDoc.rotation);
+            pinDoc.config_width = NumCast(targetDoc.width);
+            pinDoc.config_height = NumCast(targetDoc.height);
         }
         if (pinProps.pinAudioPlay) pinDoc.presPlayAudio = true;
         if (pinProps.pinData) {
-            pinDoc.presPinData =
+            pinDoc.config_pinData =
                 pinProps.pinData.scrollable ||
                 pinProps.pinData.temporal ||
                 pinProps.pinData.pannable ||
@@ -616,30 +618,30 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 pinProps?.activeFrame !== undefined;
             const fkey = Doc.LayoutFieldKey(targetDoc);
             if (pinProps.pinData.dataview) {
-                pinDoc.presUsePath = targetDoc[fkey + '_usePath'];
-                pinDoc.presData = targetDoc[fkey] instanceof ObjectField ? (targetDoc[fkey] as ObjectField)[Copy]() : targetDoc.data;
+                pinDoc.config_usePath = targetDoc[fkey + '_usePath'];
+                pinDoc.config_data = targetDoc[fkey] instanceof ObjectField ? (targetDoc[fkey] as ObjectField)[Copy]() : targetDoc.data;
             }
             if (pinProps.pinData.dataannos) {
                 const fkey = Doc.LayoutFieldKey(targetDoc);
-                pinDoc.presAnnotations = new List<Doc>(DocListCast(Doc.GetProto(targetDoc)[fkey + '_annotations']).filter(doc => !doc.layout_unrendered));
+                pinDoc.config_annotations = new List<Doc>(DocListCast(Doc.GetProto(targetDoc)[fkey + '_annotations']).filter(doc => !doc.layout_unrendered));
             }
             if (pinProps.pinData.inkable) {
-                pinDoc.presFillColor = targetDoc.fillColor;
-                pinDoc.presColor = targetDoc.color;
-                pinDoc.presWidth = targetDoc._width;
-                pinDoc.presHeight = targetDoc._height;
+                pinDoc.config_fillColor = targetDoc.fillColor;
+                pinDoc.config_color = targetDoc.color;
+                pinDoc.config_width = targetDoc._width;
+                pinDoc.config_height = targetDoc._height;
             }
-            if (pinProps.pinData.scrollable) pinDoc.presViewScroll = targetDoc._layout_scrollTop;
+            if (pinProps.pinData.scrollable) pinDoc.config_scrollTop = targetDoc._layout_scrollTop;
             if (pinProps.pinData.clippable) {
                 const fkey = Doc.LayoutFieldKey(targetDoc);
-                pinDoc.presClipWidth = targetDoc[fkey + '_clipWidth'];
+                pinDoc.config_clipWidth = targetDoc[fkey + '_clipWidth'];
             }
             if (pinProps.pinData.datarange) {
-                pinDoc.presXRange = undefined; //targetDoc?.xrange;
-                pinDoc.presYRange = undefined; //targetDoc?.yrange;
+                pinDoc.config_xRange = undefined; //targetDoc?.xrange;
+                pinDoc.config_yRange = undefined; //targetDoc?.yrange;
             }
             if (pinProps.pinData.poslayoutview)
-                pinDoc.presPinLayoutData = new List<string>(
+                pinDoc.config_pinLayoutData = new List<string>(
                     DocListCast(targetDoc[fkey] as ObjectField).map(d =>
                         JSON.stringify({
                             id: d[Id],
@@ -654,28 +656,28 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         })
                     )
                 );
-            if (pinProps.pinData.type_collection) pinDoc.presViewType = targetDoc._type_collection;
-            if (pinProps.pinData.filters) pinDoc.presDocFilters = ObjectField.MakeCopy(targetDoc.childFilters as ObjectField);
-            if (pinProps.pinData.pivot) pinDoc.presPivotField = targetDoc._pivotField;
+            if (pinProps.pinData.type_collection) pinDoc.config_viewType = targetDoc._type_collection;
+            if (pinProps.pinData.filters) pinDoc.config_docFilters = ObjectField.MakeCopy(targetDoc.childFilters as ObjectField);
+            if (pinProps.pinData.pivot) pinDoc.config_pivotField = targetDoc._pivotField;
             if (pinProps.pinData.pannable) {
-                pinDoc.presPanX = NumCast(targetDoc._freeform_panX);
-                pinDoc.presPanY = NumCast(targetDoc._freeform_panY);
-                pinDoc.presViewScale = NumCast(targetDoc._freeform_scale, 1);
+                pinDoc.config_panX = NumCast(targetDoc._freeform_panX);
+                pinDoc.config_panY = NumCast(targetDoc._freeform_panY);
+                pinDoc.config_viewScale = NumCast(targetDoc._freeform_scale, 1);
             }
             if (pinProps.pinData.temporal) {
-                pinDoc.presStartTime = targetDoc._layout_currentTimecode;
-                const duration = NumCast(pinDoc[`${Doc.LayoutFieldKey(pinDoc)}_duration`], NumCast(targetDoc.presStartTime) + 0.1);
-                pinDoc.presEndTime = NumCast(targetDoc.clipEnd, duration);
+                pinDoc.config_clipStart = targetDoc._layout_currentTimecode;
+                const duration = NumCast(pinDoc[`${Doc.LayoutFieldKey(pinDoc)}_duration`], NumCast(targetDoc.config_clipStart) + 0.1);
+                pinDoc.config_clipEnd = NumCast(targetDoc.clipEnd, duration);
             }
         }
         if (pinProps?.pinViewport) {
             // If pinWithView option set then update scale and x / y props of slide
             const bounds = pinProps.pinViewport;
-            pinDoc.presPinView = true;
-            pinDoc.presViewScale = NumCast(targetDoc._freeform_scale, 1);
-            pinDoc.presPanX = bounds.left + bounds.width / 2;
-            pinDoc.presPanY = bounds.top + bounds.height / 2;
-            pinDoc.presPinViewBounds = new List<number>([bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + bounds.height]);
+            pinDoc.config_pinView = true;
+            pinDoc.config_viewScale = NumCast(targetDoc._freeform_scale, 1);
+            pinDoc.config_panX = bounds.left + bounds.width / 2;
+            pinDoc.config_panY = bounds.top + bounds.height / 2;
+            pinDoc.config_viewBounds = new List<number>([bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + bounds.height]);
         }
     }
     /**
@@ -712,23 +714,23 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     };
 
     static NavigateToTarget(targetDoc: Doc, activeItem: Doc, finished?: () => void) {
-        if (activeItem.presMovement === PresMovement.None && targetDoc.type === DocumentType.SCRIPTING) {
+        if (activeItem.presentation_movement === PresMovement.None && targetDoc.type === DocumentType.SCRIPTING) {
             (DocumentManager.Instance.getFirstDocumentView(targetDoc)?.ComponentView as ScriptingBox)?.onRun?.();
             return;
         }
-        const effect = activeItem.presEffect && activeItem.presEffect !== PresEffect.None ? activeItem.presEffect : undefined;
-        const presTime = NumCast(activeItem.presTransition, effect ? 750 : 500);
+        const effect = activeItem.presentation_effect && activeItem.presentation_effect !== PresEffect.None ? activeItem.presentation_effect : undefined;
+        const presTime = NumCast(activeItem.presentation_transition, effect ? 750 : 500);
         const options: DocFocusOptions = {
-            willPan: activeItem.presMovement !== PresMovement.None,
-            willZoomCentered: activeItem.presMovement === PresMovement.Zoom || activeItem.presMovement === PresMovement.Jump || activeItem.presMovement === PresMovement.Center,
-            zoomScale: activeItem.presMovement === PresMovement.Center ? 0 : NumCast(activeItem.presZoom, 1),
-            zoomTime: activeItem.presMovement === PresMovement.Jump ? 0 : Math.min(Math.max(effect ? 750 : 500, (effect ? 0.2 : 1) * presTime), presTime),
+            willPan: activeItem.presentation_movement !== PresMovement.None,
+            willZoomCentered: activeItem.presentation_movement === PresMovement.Zoom || activeItem.presentation_movement === PresMovement.Jump || activeItem.presentation_movement === PresMovement.Center,
+            zoomScale: activeItem.presentation_movement === PresMovement.Center ? 0 : NumCast(activeItem.presZoom, 1),
+            zoomTime: activeItem.presentation_movement === PresMovement.Jump ? 0 : Math.min(Math.max(effect ? 750 : 500, (effect ? 0.2 : 1) * presTime), presTime),
             effect: activeItem,
             noSelect: true,
             openLocation: OpenWhere.addLeft,
             anchorDoc: activeItem,
             easeFunc: StrCast(activeItem.presEaseFunc, 'ease') as any,
-            zoomTextSelections: BoolCast(activeItem.presZoomText),
+            zoomTextSelections: BoolCast(activeItem.presentation_zoomText),
             playAudio: BoolCast(activeItem.presPlayAudio),
         };
         if (activeItem.presOpenInLightbox) {
@@ -738,7 +740,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             }
         }
         if (targetDoc) {
-            if (activeItem.presentationTargetDoc instanceof Doc) activeItem.presentationTargetDoc[Animation] = undefined;
+            if (activeItem.presentation_targetDoc instanceof Doc) activeItem.presentation_targetDoc[Animation] = undefined;
 
             DocumentManager.Instance.AddViewRenderedCb(LightboxView.LightboxDoc, dv => {
                 // if target or the doc it annotates is not in the lightbox, then close the lightbox
@@ -761,16 +763,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const tagDoc = PresBox.targetRenderedDoc(curDoc);
             const itemIndexes: number[] = this.getAllIndexes(this.tagDocs, tagDoc);
             let opacity: Opt<number> = index === this.itemIndex ? 1 : undefined;
-            if (curDoc.presHide) {
+            if (curDoc.presentation_hide) {
                 if (index !== this.itemIndex) {
                     opacity = 1;
                 }
             }
             const hidingIndBef = itemIndexes.find(item => item >= this.itemIndex) ?? itemIndexes.slice().reverse().lastElement();
-            if (curDoc.presHideBefore && index === hidingIndBef) {
+            if (curDoc.presentation_hideBefore && index === hidingIndBef) {
                 if (index > this.itemIndex) {
                     opacity = 0;
-                } else if (index === this.itemIndex || !curDoc.presHideAfter) {
+                } else if (index === this.itemIndex || !curDoc.presentation_hideAfter) {
                     opacity = 1;
                     setTimeout(() => (tagDoc._dataTransition = undefined), 1000);
                 }
@@ -780,15 +782,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     .slice()
                     .reverse()
                     .find(item => item <= this.itemIndex) ?? itemIndexes.lastElement();
-            if (curDoc.presHideAfter && index === hidingIndAft) {
+            if (curDoc.presentation_hideAfter && index === hidingIndAft) {
                 if (index < this.itemIndex) {
                     opacity = 0;
-                } else if (index === this.itemIndex || !curDoc.presHideBefore) {
+                } else if (index === this.itemIndex || !curDoc.presentation_hideBefore) {
                     opacity = 1;
                 }
             }
             const hidingInd = itemIndexes.find(item => item === this.itemIndex);
-            if (curDoc.presHide && index === hidingInd) {
+            if (curDoc.presentation_hide && index === hidingInd) {
                 if (index === this.itemIndex) {
                     opacity = 0;
                 }
@@ -846,9 +848,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     // The function pauses the auto presentation
     @action
     pauseAutoPres = () => {
-        if (this.layoutDoc.presStatus === PresStatus.Autoplay) {
+        if (this.layoutDoc.presentation_status === PresStatus.Autoplay) {
             if (this._presTimer) clearTimeout(this._presTimer);
-            this.layoutDoc.presStatus = PresStatus.Manual;
+            this.layoutDoc.presentation_status = PresStatus.Manual;
             this.childDocs.forEach(this.stopTempMedia);
         }
     };
@@ -879,23 +881,23 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         runInAction(() => (this._expandBoolean = !this._expandBoolean));
         this.rootDoc.expandBoolean = this._expandBoolean;
         this.childDocs.forEach(doc => {
-            doc.presExpandInlineButton = this._expandBoolean;
+            doc.presentation_expandInlineButton = this._expandBoolean;
         });
     };
 
     initializePresState = (startIndex: number) => {
         this.childDocs.forEach((doc, index) => {
             const tagDoc = PresBox.targetRenderedDoc(doc);
-            if (doc.presHideBefore && index > startIndex) tagDoc.opacity = 0;
-            if (doc.presHideAfter && index < startIndex) tagDoc.opacity = 0;
-            if (doc.presIndexed !== undefined && index >= startIndex) {
-                const startInd = NumCast(doc.presIndexedStart);
+            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) {
+                const startInd = NumCast(doc.presentation_indexedStart);
                 this.progressivizedItems(doc)
                     ?.slice(startInd)
                     .forEach(indexedDoc => (indexedDoc.opacity = 0));
-                doc.presIndexed = Math.min(this.progressivizedItems(doc)?.length ?? 0, startInd);
+                doc.presentation_indexed = Math.min(this.progressivizedItems(doc)?.length ?? 0, startInd);
             }
-            // if (doc.presHide && this.childDocs.indexOf(doc) === startIndex)  tagDoc.opacity = 0;
+            // if (doc.presentation_hide && this.childDocs.indexOf(doc) === startIndex)  tagDoc.opacity = 0;
         });
     };
 
@@ -909,13 +911,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         PresBox.Instance = this;
         clearTimeout(this._presTimer);
         if (this.childDocs.length) {
-            this.layoutDoc.presStatus = PresStatus.Autoplay;
+            this.layoutDoc.presentation_status = PresStatus.Autoplay;
             this.initializePresState(startIndex);
             const func = () => {
-                const delay = NumCast(this.activeItem.presDuration, this.activeItem.type === DocumentType.SCRIPTING ? 0 : 2500) + NumCast(this.activeItem.presTransition);
+                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()) this.layoutDoc.presStatus = this._exitTrail?.() ?? PresStatus.Manual;
-                    this.layoutDoc.presStatus === PresStatus.Autoplay && func();
+                    if (!this.next()) this.layoutDoc.presentation_status = this._exitTrail?.() ?? PresStatus.Manual;
+                    this.layoutDoc.presentation_status === PresStatus.Autoplay && func();
                 }, delay);
             };
             this.gotoDocument(startIndex, this.activeItem, undefined, func);
@@ -949,7 +951,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         doc._width = PresBox.minimizedWidth;
         Doc.AddToMyOverlay(doc);
         PresBox.Instance?.initializePresState(PresBox.Instance.itemIndex);
-        return (doc.presStatus = PresStatus.Manual);
+        return (doc.presentation_status = PresStatus.Manual);
     }
 
     /**
@@ -993,34 +995,34 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     });
 
     movementName = action((activeItem: Doc) => {
-        if (![PresMovement.Zoom, PresMovement.Pan, PresMovement.Center, PresMovement.Jump, PresMovement.None].includes(StrCast(activeItem.presMovement) as any)) {
+        if (![PresMovement.Zoom, PresMovement.Pan, PresMovement.Center, PresMovement.Jump, PresMovement.None].includes(StrCast(activeItem.presentation_movement) as any)) {
             return PresMovement.Zoom;
         }
-        return StrCast(activeItem.presMovement);
+        return StrCast(activeItem.presentation_movement);
     });
 
     whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged((this._isChildActive = isActive)));
     // For dragging documents into the presentation trail
     addDocumentFilter = (docs: Doc[]) => {
         docs.forEach((doc, i) => {
-            if (doc.presentationTargetDoc) return true;
+            if (doc.presentation_targetDoc) return true;
             if (doc.type === DocumentType.LABEL) {
                 const audio = Cast(doc.annotationOn, Doc, null);
                 if (audio) {
                     audio.mediaStart = 'manual';
                     audio.mediaStop = 'manual';
-                    audio.presStartTime = NumCast(doc._timecodeToShow /* audioStart */, NumCast(doc._timecodeToShow /* videoStart */));
-                    audio.presEndTime = NumCast(doc._timecodeToHide /* audioEnd */, NumCast(doc._timecodeToHide /* videoEnd */));
-                    audio.presDuration = audio.presStartTime - audio.presEndTime;
+                    audio.config_clipStart = NumCast(doc._timecodeToShow /* audioStart */, NumCast(doc._timecodeToShow /* videoStart */));
+                    audio.config_clipEnd = NumCast(doc._timecodeToHide /* audioEnd */, NumCast(doc._timecodeToHide /* videoEnd */));
+                    audio.presentation_duration = audio.config_clipStart - audio.config_clipEnd;
                     TabDocView.PinDoc(audio, { audioRange: true });
                     setTimeout(() => this.removeDocument(doc), 0);
                     return false;
                 }
             } else {
-                if (!doc.presentationTargetDoc) doc.title = doc.title + ' - Slide';
-                doc.presentationTargetDoc = doc.createdFrom; // dropped document will be a new embedding of an embedded document somewhere else.
-                doc.presMovement = PresMovement.Zoom;
-                if (this._expandBoolean) doc.presExpandInlineButton = true;
+                if (!doc.presentation_targetDoc) doc.title = doc.title + ' - Slide';
+                doc.presentation_targetDoc = doc.createdFrom; // dropped document will be a new embedding of an embedded document somewhere else.
+                doc.presentation_movement = PresMovement.Zoom;
+                if (this._expandBoolean) doc.presentation_expandInlineButton = true;
             }
         });
         return true;
@@ -1040,7 +1042,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @computed get listOfSelected() {
         return Array.from(this.selectedArray).map((doc: Doc, index: any) => {
             const curDoc = Cast(doc, Doc, null);
-            const tagDoc = Cast(curDoc.presentationTargetDoc, Doc, null);
+            const tagDoc = Cast(curDoc.presentation_targetDoc, Doc, null);
             if (curDoc && curDoc === this.activeItem)
                 return (
                     <div key={index} className="selectedList-items">
@@ -1146,7 +1148,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (anchorNode && anchorNode.className?.includes('lm_title')) return;
         switch (e.key) {
             case 'Backspace':
-                if (this.layoutDoc.presStatus === 'edit') {
+                if (this.layoutDoc.presentation_status === 'edit') {
                     undoBatch(
                         action(() => {
                             for (const doc of this.selectedArray) {
@@ -1163,11 +1165,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             case 'Escape':
                 if (Doc.IsInMyOverlay(this.layoutDoc)) {
                     this.exitClicked();
-                } else if (this.layoutDoc.presStatus === PresStatus.Edit) {
+                } else if (this.layoutDoc.presentation_status === PresStatus.Edit) {
                     this.clearSelectedArray();
                     this._eleArray.length = this._dragArray.length = 0;
                 } else {
-                    this.layoutDoc.presStatus = PresStatus.Edit;
+                    this.layoutDoc.presentation_status = PresStatus.Edit;
                 }
                 if (this._presTimer) clearTimeout(this._presTimer);
                 handled = true;
@@ -1184,7 +1186,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     this.next();
                     if (this._presTimer) {
                         clearTimeout(this._presTimer);
-                        this.layoutDoc.presStatus = PresStatus.Manual;
+                        this.layoutDoc.presentation_status = PresStatus.Manual;
                     }
                 }
                 handled = true;
@@ -1201,19 +1203,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     this.back();
                     if (this._presTimer) {
                         clearTimeout(this._presTimer);
-                        this.layoutDoc.presStatus = PresStatus.Manual;
+                        this.layoutDoc.presentation_status = PresStatus.Manual;
                     }
                 }
                 handled = true;
                 break;
             case 'Spacebar':
             case ' ':
-                if (this.layoutDoc.presStatus === PresStatus.Manual) this.startOrPause(true);
-                else if (this.layoutDoc.presStatus === PresStatus.Autoplay) if (this._presTimer) clearTimeout(this._presTimer);
+                if (this.layoutDoc.presentation_status === PresStatus.Manual) this.startOrPause(true);
+                else if (this.layoutDoc.presentation_status === PresStatus.Autoplay) if (this._presTimer) clearTimeout(this._presTimer);
                 handled = true;
                 break;
             case 'a':
-                if ((e.metaKey || e.altKey) && this.layoutDoc.presStatus === 'edit') {
+                if ((e.metaKey || e.altKey) && this.layoutDoc.presentation_status === 'edit') {
                     this.clearSelectedArray();
                     this.childDocs.forEach(doc => this.addToSelectedArray(doc));
                     handled = true;
@@ -1236,9 +1238,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const presCollection = DocumentManager.GetContextPath(this.activeItem).reverse().lastElement();
         const dv = DocumentManager.Instance.getDocumentView(presCollection);
         this.childDocs
-            .filter(doc => Cast(doc.presentationTargetDoc, Doc, null))
+            .filter(doc => Cast(doc.presentation_targetDoc, Doc, null))
             .forEach((doc, index) => {
-                const tagDoc = Cast(doc.presentationTargetDoc, Doc, null);
+                const tagDoc = Cast(doc.presentation_targetDoc, Doc, null);
                 const srcContext = Cast(tagDoc.embedContainer, Doc, null);
                 const width = NumCast(tagDoc._width) / 10;
                 const height = Math.max(NumCast(tagDoc._height) / 10, 15);
@@ -1271,17 +1273,17 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             </div>
                         );
                     }
-                } else if (doc.presPinView && presCollection === tagDoc && dv) {
+                } else if (doc.config_pinView && presCollection === tagDoc && dv) {
                     // Case B: Document is presPinView and is presCollection
-                    const scale: number = 1 / NumCast(doc.presViewScale);
+                    const scale: number = 1 / NumCast(doc.config_viewScale);
                     const height: number = dv.props.PanelHeight() * scale;
                     const width: number = dv.props.PanelWidth() * scale;
                     const indWidth = width / 10;
                     const indHeight = Math.max(height / 10, 15);
                     const indEdge = Math.max(indWidth, indHeight);
                     const indFontSize = indEdge * 0.8;
-                    const xLoc: number = NumCast(doc.presPanX) - width / 2;
-                    const yLoc: number = NumCast(doc.presPanY) - height / 2;
+                    const xLoc: number = NumCast(doc.config_panX) - width / 2;
+                    const yLoc: number = NumCast(doc.config_panY) - height / 2;
                     docs.push(tagDoc);
                     order.push(
                         <>
@@ -1307,15 +1309,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @computed get paths() {
         let pathPoints = '';
         this.childDocs.forEach((doc, index) => {
-            const tagDoc = Cast(doc.presentationTargetDoc, Doc, null);
+            const tagDoc = Cast(doc.presentation_targetDoc, Doc, null);
             if (tagDoc) {
                 const n1x = NumCast(tagDoc.x) + NumCast(tagDoc._width) / 2;
                 const n1y = NumCast(tagDoc.y) + NumCast(tagDoc._height) / 2;
                 if ((index = 0)) pathPoints = n1x + ',' + n1y;
                 else pathPoints = pathPoints + ' ' + n1x + ',' + n1y;
-            } else if (doc.presPinView) {
-                const n1x = NumCast(doc.presPanX);
-                const n1y = NumCast(doc.presPanY);
+            } else if (doc.config_pinView) {
+                const n1x = NumCast(doc.config_panX);
+                const n1y = NumCast(doc.config_panY);
                 if ((index = 0)) pathPoints = n1x + ',' + n1y;
                 else pathPoints = pathPoints + ' ' + n1x + ',' + n1y;
             }
@@ -1339,7 +1341,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
     getPaths = (collection: Doc) => this.paths; // needs to be smarter and figure out the paths to draw for this specific collection.  or better yet, draw everything in an overlay layer instad of within a collection
 
-    // Converts seconds to ms and updates presTransition
+    // Converts seconds to ms and updates presentation_transition
     public static SetTransitionTime = (number: String, setter: (timeInMS: number) => void, change?: number) => {
         let timeInMS = Number(number) * 1000;
         if (change) timeInMS += change;
@@ -1350,10 +1352,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @undoBatch
     updateTransitionTime = (number: String, change?: number) => {
-        PresBox.SetTransitionTime(number, (timeInMS: number) => this.selectedArray.forEach(doc => (doc.presTransition = timeInMS)), change);
+        PresBox.SetTransitionTime(number, (timeInMS: number) => this.selectedArray.forEach(doc => (doc.presentation_transition = timeInMS)), change);
     };
 
-    // Converts seconds to ms and updates presTransition
+    // Converts seconds to ms and updates presentation_transition
     @undoBatch
     updateZoom = (number: String, change?: number) => {
         let scale = Number(number) / 100;
@@ -1364,7 +1366,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     };
 
     /*
-     * Converts seconds to ms and updates presDuration
+     * Converts seconds to ms and updates presentation_duration
      */
     @undoBatch
     updateDurationTime = (number: String, change?: number) => {
@@ -1372,31 +1374,31 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (change) timeInMS += change;
         if (timeInMS < 100) timeInMS = 100;
         if (timeInMS > 20000) timeInMS = 20000;
-        this.selectedArray.forEach(doc => (doc.presDuration = timeInMS));
+        this.selectedArray.forEach(doc => (doc.presentation_duration = timeInMS));
     };
 
     @undoBatch
-    updateMovement = action((movement: PresMovement, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (doc.presMovement = movement)));
+    updateMovement = action((movement: PresMovement, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (doc.presentation_movement = movement)));
 
     @undoBatch
     @action
     updateHideBefore = (activeItem: Doc) => {
-        activeItem.presHideBefore = !activeItem.presHideBefore;
-        this.selectedArray.forEach(doc => (doc.presHideBefore = activeItem.presHideBefore));
+        activeItem.presentation_hideBefore = !activeItem.presentation_hideBefore;
+        this.selectedArray.forEach(doc => (doc.presentation_hideBefore = activeItem.presentation_hideBefore));
     };
 
     @undoBatch
     @action
     updateHide = (activeItem: Doc) => {
-        activeItem.presHide = !activeItem.presHide;
-        this.selectedArray.forEach(doc => (doc.presHide = activeItem.presHide));
+        activeItem.presentation_hide = !activeItem.presentation_hide;
+        this.selectedArray.forEach(doc => (doc.presentation_hide = activeItem.presentation_hide));
     };
 
     @undoBatch
     @action
     updateHideAfter = (activeItem: Doc) => {
-        activeItem.presHideAfter = !activeItem.presHideAfter;
-        this.selectedArray.forEach(doc => (doc.presHideAfter = activeItem.presHideAfter));
+        activeItem.presentation_hideAfter = !activeItem.presentation_hideAfter;
+        this.selectedArray.forEach(doc => (doc.presentation_hideAfter = activeItem.presentation_hideAfter));
     };
 
     @undoBatch
@@ -1415,11 +1417,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @undoBatch
     @action
-    updateEffectDirection = (effect: PresEffectDirection, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (doc.presEffectDirection = effect));
+    updateEffectDirection = (effect: PresEffectDirection, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (doc.presentation_effectDirection = effect));
 
     @undoBatch
     @action
-    updateEffect = (effect: PresEffect, bullet: boolean, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (bullet ? (doc.presBulletEffect = effect) : (doc.presEffect = effect)));
+    updateEffect = (effect: PresEffect, bullet: boolean, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (bullet ? (doc.presBulletEffect = effect) : (doc.presentation_effect = effect)));
 
     static _sliderBatch: any;
     public static inputter = (min: string, step: string, max: string, value: number, active: boolean, change: (val: string) => void, hmargin?: number) => {
@@ -1449,16 +1451,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @undoBatch
     @action
     applyTo = (array: Doc[]) => {
-        this.updateMovement(this.activeItem.presMovement as PresMovement, true);
-        this.updateEffect(this.activeItem.presEffect as PresEffect, false, true);
+        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.presEffectDirection as PresEffectDirection, true);
-        const { presTransition, presDuration, presHideBefore, presHideAfter } = this.activeItem;
+        this.updateEffectDirection(this.activeItem.presentation_effectDirection as PresEffectDirection, true);
+        const { presentation_transition, presentation_duration, presentation_hideBefore, presentation_hideAfter } = this.activeItem;
         array.forEach(curDoc => {
-            curDoc.presTransition = presTransition;
-            curDoc.presDuration = presDuration;
-            curDoc.presHideBefore = presHideBefore;
-            curDoc.presHideAfter = presHideAfter;
+            curDoc.presentation_transition = presentation_transition;
+            curDoc.presentation_duration = presentation_duration;
+            curDoc.presentation_hideBefore = presentation_hideBefore;
+            curDoc.presentation_hideAfter = presentation_hideAfter;
         });
     };
 
@@ -1466,24 +1468,24 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const activeItem = this.activeItem;
         if (activeItem && this.targetDoc) {
             const targetType = this.targetDoc.type;
-            let duration = activeItem.presDuration ? NumCast(activeItem.presDuration) / 1000 : 0;
+            let duration = activeItem.presentation_duration ? NumCast(activeItem.presentation_duration) / 1000 : 0;
             if (activeItem.type === DocumentType.AUDIO) duration = NumCast(activeItem.duration);
             return (
                 <div className="presBox-ribbon">
                     <div className="ribbon-doubleButton">
                         <Tooltip title={<div className="dash-tooltip">{'Hide before presented'}</div>}>
-                            <div className={`ribbon-toggle ${activeItem.presHideBefore ? 'active' : ''}`} onClick={() => this.updateHideBefore(activeItem)}>
+                            <div className={`ribbon-toggle ${activeItem.presentation_hideBefore ? 'active' : ''}`} onClick={() => this.updateHideBefore(activeItem)}>
                                 Hide before
                             </div>
                         </Tooltip>
                         <Tooltip title={<div className="dash-tooltip">{'Hide while presented'}</div>}>
-                            <div className={`ribbon-toggle ${activeItem.presHide ? 'active' : ''}`} onClick={() => this.updateHide(activeItem)}>
+                            <div className={`ribbon-toggle ${activeItem.presentation_hide ? 'active' : ''}`} onClick={() => this.updateHide(activeItem)}>
                                 Hide
                             </div>
                         </Tooltip>
 
                         <Tooltip title={<div className="dash-tooltip">{'Hide after presented'}</div>}>
-                            <div className={`ribbon-toggle ${activeItem.presHideAfter ? 'active' : ''}`} onClick={() => this.updateHideAfter(activeItem)}>
+                            <div className={`ribbon-toggle ${activeItem.presentation_hideAfter ? 'active' : ''}`} onClick={() => this.updateHideAfter(activeItem)}>
                                 Hide after
                             </div>
                         </Tooltip>
@@ -1532,7 +1534,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (activeItem && this.targetDoc) {
             const effect = activeItem.presBulletEffect ? activeItem.presBulletEffect : PresMovement.None;
             const bulletEffect = (effect: PresEffect) => (
-                <div className={`presBox-dropdownOption ${activeItem.presEffect === effect || (effect === PresEffect.None && !activeItem.presEffect) ? 'active' : ''}`} onPointerDown={StopEvent} onClick={() => this.updateEffect(effect, true)}>
+                <div
+                    className={`presBox-dropdownOption ${activeItem.presentation_effect === effect || (effect === PresEffect.None && !activeItem.presentation_effect) ? 'active' : ''}`}
+                    onPointerDown={StopEvent}
+                    onClick={() => this.updateEffect(effect, true)}>
                     {effect}
                 </div>
             );
@@ -1545,25 +1550,31 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             style={{ margin: 10 }}
                             type="checkbox"
                             onChange={() => {
-                                activeItem.presIndexed = activeItem.presIndexed === undefined ? 0 : undefined;
-                                activeItem.presHideBefore = activeItem.presIndexed !== undefined;
+                                activeItem.presentation_indexed = activeItem.presentation_indexed === undefined ? 0 : undefined;
+                                activeItem.presentation_hideBefore = activeItem.presentation_indexed !== undefined;
                                 const tagDoc = PresBox.targetRenderedDoc(this.activeItem);
                                 const type = DocCast(tagDoc?.annotationOn)?.type ?? tagDoc.type;
-                                activeItem.presIndexedStart = type === DocumentType.COL ? 1 : 0;
+                                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);
                                 if (Cast(tagDoc[dataField], listSpec(Doc), null)?.filter(d => d instanceof Doc) === undefined) dataField = dataField + '_annotations';
 
-                                if (DocCast(activeItem.presentationTargetDoc).annotationOn) activeItem.data = ComputedField.MakeFunction(`self.presentationTargetDoc.annotationOn["${dataField}"]`);
-                                else activeItem.data = ComputedField.MakeFunction(`self.presentationTargetDoc["${dataField}"]`);
+                                if (DocCast(activeItem.presentation_targetDoc).annotationOn) activeItem.data = ComputedField.MakeFunction(`self.presentation_targetDoc.annotationOn["${dataField}"]`);
+                                else activeItem.data = ComputedField.MakeFunction(`self.presentation_targetDoc["${dataField}"]`);
                             }}
-                            checked={Cast(activeItem.presIndexed, 'number', null) !== undefined ? true : false}
+                            checked={Cast(activeItem.presentation_indexed, 'number', null) !== undefined ? true : false}
                         />
                     </div>
                     <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
                         <div className="presBox-subheading">Progressivize First Bullet</div>
-                        <input className="presBox-checkbox" style={{ margin: 10 }} type="checkbox" onChange={() => (activeItem.presIndexedStart = activeItem.presIndexedStart ? 0 : 1)} checked={!NumCast(activeItem.presIndexedStart)} />
+                        <input
+                            className="presBox-checkbox"
+                            style={{ margin: 10 }}
+                            type="checkbox"
+                            onChange={() => (activeItem.presentation_indexedStart = activeItem.presentation_indexedStart ? 0 : 1)}
+                            checked={!NumCast(activeItem.presentation_indexedStart)}
+                        />
                     </div>
                     <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
                         <div className="presBox-subheading">Expand Current Bullet</div>
@@ -1598,18 +1609,21 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
     @computed get transitionDropdown() {
         const activeItem = this.activeItem;
-        const presEffect = (effect: PresEffect) => (
-            <div className={`presBox-dropdownOption ${activeItem.presEffect === effect || (effect === PresEffect.None && !activeItem.presEffect) ? 'active' : ''}`} onPointerDown={StopEvent} onClick={() => this.updateEffect(effect, false)}>
+        const preseEffect = (effect: PresEffect) => (
+            <div
+                className={`presBox-dropdownOption ${activeItem.presentation_effect === effect || (effect === PresEffect.None && !activeItem.presentation_effect) ? 'active' : ''}`}
+                onPointerDown={StopEvent}
+                onClick={() => this.updateEffect(effect, false)}>
                 {effect}
             </div>
         );
         const presMovement = (movement: PresMovement) => (
-            <div className={`presBox-dropdownOption ${activeItem.presMovement === movement ? 'active' : ''}`} onPointerDown={StopEvent} onClick={() => this.updateMovement(movement)}>
+            <div className={`presBox-dropdownOption ${activeItem.presentation_movement === movement ? 'active' : ''}`} onPointerDown={StopEvent} onClick={() => this.updateMovement(movement)}>
                 {movement}
             </div>
         );
         const presDirection = (direction: PresEffectDirection, icon: string, gridColumn: number, gridRow: number, opts: object) => {
-            const color = activeItem.presEffectDirection === direction || (direction === PresEffectDirection.Center && !activeItem.presEffectDirection) ? Colors.LIGHT_BLUE : 'black';
+            const color = activeItem.presentation_effectDirection === direction || (direction === PresEffectDirection.Center && !activeItem.presentation_effectDirection) ? Colors.LIGHT_BLUE : 'black';
             return (
                 <Tooltip title={<div className="dash-tooltip">{direction}</div>}>
                     <div
@@ -1621,12 +1635,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             );
         };
         if (activeItem && this.targetDoc) {
-            const transitionSpeed = activeItem.presTransition ? NumCast(activeItem.presTransition) / 1000 : 0.5;
+            const transitionSpeed = activeItem.presentation_transition ? NumCast(activeItem.presentation_transition) / 1000 : 0.5;
             const zoom = NumCast(activeItem.presZoom, 1) * 100;
-            const effect = activeItem.presEffect ? activeItem.presEffect : PresMovement.None;
+            const effect = activeItem.presentation_effect ? activeItem.presentation_effect : PresMovement.None;
             return (
                 <div
-                    className={`presBox-ribbon ${this._transitionTools && this.layoutDoc.presStatus === PresStatus.Edit ? 'active' : ''}`}
+                    className={`presBox-ribbon ${this._transitionTools && this.layoutDoc.presentation_status === PresStatus.Edit ? 'active' : ''}`}
                     onPointerDown={StopEvent}
                     onPointerUp={StopEvent}
                     onClick={action(e => {
@@ -1654,7 +1668,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 {presMovement(PresMovement.Jump)}
                             </div>
                         </div>
-                        <div className="ribbon-doubleButton" style={{ display: activeItem.presMovement === PresMovement.Zoom ? 'inline-flex' : 'none' }}>
+                        <div className="ribbon-doubleButton" style={{ display: activeItem.presentation_movement === PresMovement.Zoom ? 'inline-flex' : 'none' }}>
                             <div className="presBox-subheading">Zoom (% screen filled)</div>
                             <div className="ribbon-property">
                                 <input className="presBox-input" type="number" readOnly={true} value={zoom} onChange={e => this.updateZoom(e.target.value)} />%
@@ -1668,7 +1682,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 </div>
                             </div>
                         </div>
-                        {PresBox.inputter('0', '1', '100', zoom, activeItem.presMovement === PresMovement.Zoom, this.updateZoom)}
+                        {PresBox.inputter('0', '1', '100', zoom, activeItem.presentation_movement === PresMovement.Zoom, this.updateZoom)}
                         <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
                             <div className="presBox-subheading">Transition Time</div>
                             <div className="ribbon-property">
@@ -1698,7 +1712,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         </div>
                         <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
                             <div className="presBox-subheading">Zoom Text Selections</div>
-                            <input className="presBox-checkbox" style={{ margin: 10 }} type="checkbox" onChange={() => (activeItem.presZoomText = !BoolCast(activeItem.presZoomText))} checked={BoolCast(activeItem.presZoomText)} />
+                            <input
+                                className="presBox-checkbox"
+                                style={{ margin: 10 }}
+                                type="checkbox"
+                                onChange={() => (activeItem.presentation_zoomText = !BoolCast(activeItem.presentation_zoomText))}
+                                checked={BoolCast(activeItem.presentation_zoomText)}
+                            />
                         </div>
                         <div
                             className="presBox-dropdown"
@@ -1710,17 +1730,17 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             {effect?.toString()}
                             <FontAwesomeIcon className="presBox-dropdownIcon" style={{ gridColumn: 2, color: this._openEffectDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={'angle-down'} />
                             <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} style={{ display: this._openEffectDropdown ? 'grid' : 'none' }} onPointerDown={e => e.stopPropagation()}>
-                                {presEffect(PresEffect.None)}
-                                {presEffect(PresEffect.Fade)}
-                                {presEffect(PresEffect.Flip)}
-                                {presEffect(PresEffect.Rotate)}
-                                {presEffect(PresEffect.Bounce)}
-                                {presEffect(PresEffect.Roll)}
+                                {preseEffect(PresEffect.None)}
+                                {preseEffect(PresEffect.Fade)}
+                                {preseEffect(PresEffect.Flip)}
+                                {preseEffect(PresEffect.Rotate)}
+                                {preseEffect(PresEffect.Bounce)}
+                                {preseEffect(PresEffect.Roll)}
                             </div>
                         </div>
                         <div className="ribbon-doubleButton" style={{ display: effect === PresEffectDirection.None ? 'none' : 'inline-flex' }}>
                             <div className="presBox-subheading">Effect direction</div>
-                            <div className="ribbon-property">{StrCast(this.activeItem.presEffectDirection)}</div>
+                            <div className="ribbon-property">{StrCast(this.activeItem.presentation_effectDirection)}</div>
                         </div>
                         <div className="effectDirection" style={{ display: effect === PresEffectDirection.None ? 'none' : 'grid', width: 40 }}>
                             {presDirection(PresEffectDirection.Left, 'angle-right', 1, 2, {})}
@@ -1760,10 +1780,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                             style={{ textAlign: 'center', width: '100%', height: 15, fontSize: 10 }}
                                             type="number"
                                             readOnly={true}
-                                            value={NumCast(activeItem.presStartTime).toFixed(2)}
+                                            value={NumCast(activeItem.config_clipStart).toFixed(2)}
                                             onKeyDown={e => e.stopPropagation()}
                                             onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
-                                                activeItem.presStartTime = Number(e.target.value);
+                                                activeItem.config_clipStart = Number(e.target.value);
                                             })}
                                         />
                                     </div>
@@ -1773,7 +1793,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                         Duration (s)
                                     </div>
                                     <div className="slider-number" style={{ backgroundColor: Colors.LIGHT_BLUE }}>
-                                        {Math.round((NumCast(activeItem.presEndTime) - NumCast(activeItem.presStartTime)) * 10) / 10}
+                                        {Math.round((NumCast(activeItem.config_clipEnd) - NumCast(activeItem.config_clipStart)) * 10) / 10}
                                     </div>
                                 </div>
                                 <div className="slider-block">
@@ -1787,9 +1807,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                             style={{ textAlign: 'center', width: '100%', height: 15, fontSize: 10 }}
                                             type="number"
                                             readOnly={true}
-                                            value={NumCast(activeItem.presEndTime).toFixed(2)}
+                                            value={NumCast(activeItem.config_clipEnd).toFixed(2)}
                                             onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
-                                                activeItem.presEndTime = Number(e.target.value);
+                                                activeItem.config_clipEnd = Number(e.target.value);
                                             })}
                                         />
                                     </div>
@@ -1801,12 +1821,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                     step="0.1"
                                     min={clipStart}
                                     max={clipEnd}
-                                    value={NumCast(activeItem.presEndTime)}
+                                    value={NumCast(activeItem.config_clipEnd)}
                                     style={{ gridColumn: 1, gridRow: 1 }}
                                     className={`toolbar-slider ${'end'}`}
                                     id="toolbar-slider"
                                     onPointerDown={e => {
-                                        this._batch = UndoManager.StartBatch('presEndTime');
+                                        this._batch = UndoManager.StartBatch('config_clipEnd');
                                         const endBlock = document.getElementById('endTime');
                                         if (endBlock) {
                                             endBlock.style.color = Colors.LIGHT_GRAY;
@@ -1824,7 +1844,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                     }}
                                     onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                                         e.stopPropagation();
-                                        activeItem.presEndTime = Number(e.target.value);
+                                        activeItem.config_clipEnd = Number(e.target.value);
                                     }}
                                 />
                                 <input
@@ -1832,12 +1852,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                     step="0.1"
                                     min={clipStart}
                                     max={clipEnd}
-                                    value={NumCast(activeItem.presStartTime)}
+                                    value={NumCast(activeItem.config_clipStart)}
                                     style={{ gridColumn: 1, gridRow: 1 }}
                                     className={`toolbar-slider ${'start'}`}
                                     id="toolbar-slider"
                                     onPointerDown={e => {
-                                        this._batch = UndoManager.StartBatch('presStartTime');
+                                        this._batch = UndoManager.StartBatch('config_clipStart');
                                         const startBlock = document.getElementById('startTime');
                                         if (startBlock) {
                                             startBlock.style.color = Colors.LIGHT_GRAY;
@@ -1855,7 +1875,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                     }}
                                     onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                                         e.stopPropagation();
-                                        activeItem.presStartTime = Number(e.target.value);
+                                        activeItem.config_clipStart = Number(e.target.value);
                                     }}
                                 />
                             </div>
@@ -1916,7 +1936,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return (
             <div
                 className="presBox-toolbar-dropdown"
-                style={{ display: this._newDocumentTools && this.layoutDoc.presStatus === 'edit' ? 'inline-flex' : 'none' }}
+                style={{ display: this._newDocumentTools && this.layoutDoc.presentation_status === 'edit' ? 'inline-flex' : 'none' }}
                 onClick={e => e.stopPropagation()}
                 onPointerUp={e => e.stopPropagation()}
                 onPointerDown={e => e.stopPropagation()}>
@@ -2053,10 +2073,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (doc) {
             const tabMap = CollectionDockingView.Instance?.tabMap;
             const tab = tabMap && Array.from(tabMap).find(tab => tab.DashDoc.type === DocumentType.COL)?.DashDoc;
-            const presCollection = DocumentManager.GetContextPath(this.activeItem).reverse().lastElement().presentationTargetDoc ?? tab;
+            const presCollection = DocumentManager.GetContextPath(this.activeItem).reverse().lastElement().presentation_targetDoc ?? tab;
             const data = Cast(presCollection?.data, listSpec(Doc));
-            const presData = Cast(this.rootDoc.data, listSpec(Doc));
-            if (data && presData) {
+            const config_data = Cast(this.rootDoc.data, listSpec(Doc));
+            if (data && config_data) {
                 data.push(doc);
                 TabDocView.PinDoc(doc, {});
                 this.gotoDocument(this.childDocs.length, this.activeItem);
@@ -2105,7 +2125,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     className="dropdown-play-button"
                     onClick={undoBatch(
                         action(() => {
-                            this.layoutDoc.presStatus = 'manual';
+                            this.layoutDoc.presentation_status = 'manual';
                             this.initializePresState(this.itemIndex);
                             this.turnOffEdit(true);
                             this.gotoDocument(this.itemIndex, this.activeItem);
@@ -2181,7 +2201,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 className={`presBox-buttons${Doc.IsInMyOverlay(this.rootDoc) ? ' inOverlay' : ''}`}
                 style={{ background: Doc.ActivePresentation === this.rootDoc ? Colors.LIGHT_BLUE : undefined, display: !this.rootDoc._chromeHidden ? 'none' : undefined }}>
                 {isMini ? null : (
-                    <select className="presBox-viewPicker" style={{ display: this.layoutDoc.presStatus === 'edit' ? 'block' : 'none' }} onPointerDown={e => e.stopPropagation()} onChange={this.viewChanged} value={mode}>
+                    <select className="presBox-viewPicker" style={{ display: this.layoutDoc.presentation_status === 'edit' ? 'block' : 'none' }} onPointerDown={e => e.stopPropagation()} onChange={this.viewChanged} value={mode}>
                         <option onPointerDown={StopEvent} value={CollectionViewType.Stacking}>
                             List
                         </option>
@@ -2196,12 +2216,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     </select>
                 )}
                 <div className="presBox-presentPanel" style={{ opacity: this.childDocs.length ? 1 : 0.3 }}>
-                    <span className={`presBox-button ${this.layoutDoc.presStatus === PresStatus.Edit ? 'present' : ''}`}>
+                    <span className={`presBox-button ${this.layoutDoc.presentation_status === PresStatus.Edit ? 'present' : ''}`}>
                         <div
                             className="presBox-button-left"
                             onClick={undoBatch(() => {
                                 if (this.childDocs.length) {
-                                    this.layoutDoc.presStatus = 'manual';
+                                    this.layoutDoc.presentation_status = 'manual';
                                     this.initializePresState(this.itemIndex);
                                     this.gotoDocument(this.itemIndex, this.activeItem);
                                 }
@@ -2227,12 +2247,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
 
     @computed get playButtons() {
-        const presEnd = !this.layoutDoc.presLoop && this.itemIndex === this.childDocs.length - 1 && (this.activeItem.presIndexed === undefined || NumCast(this.activeItem.presIndexed) === (this.progressivizedItems(this.activeItem)?.length ?? 0));
+        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));
         const presStart: boolean = !this.layoutDoc.presLoop && this.itemIndex === 0;
         const inOverlay = Doc.IsInMyOverlay(this.rootDoc);
         // Case 1: There are still other frames and should go through all frames before going to next slide
         return (
-            <div className="presPanelOverlay" style={{ display: this.layoutDoc.presStatus !== 'edit' ? 'inline-flex' : 'none' }}>
+            <div className="presPanelOverlay" style={{ display: this.layoutDoc.presentation_status !== 'edit' ? 'inline-flex' : 'none' }}>
                 <Tooltip title={<div className="dash-tooltip">{'Loop'}</div>}>
                     <div
                         className="presPanel-button"
@@ -2255,7 +2278,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 this.back();
                                 if (this._presTimer) {
                                     clearTimeout(this._presTimer);
-                                    this.layoutDoc.presStatus = PresStatus.Manual;
+                                    this.layoutDoc.presentation_status = PresStatus.Manual;
                                 }
                                 e.stopPropagation();
                             },
@@ -2265,9 +2288,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     }>
                     <FontAwesomeIcon icon={'arrow-left'} />
                 </div>
-                <Tooltip title={<div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? 'Pause' : 'Autoplay'}</div>}>
+                <Tooltip title={<div className="dash-tooltip">{this.layoutDoc.presentation_status === PresStatus.Autoplay ? 'Pause' : 'Autoplay'}</div>}>
                     <div className="presPanel-button" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this.startOrPause(true), false, false)}>
-                        <FontAwesomeIcon color={this.layoutDoc.presStatus === PresStatus.Autoplay ? 'red' : undefined} icon={this.layoutDoc.presStatus === PresStatus.Autoplay ? 'pause' : 'play'} />
+                        <FontAwesomeIcon color={this.layoutDoc.presentation_status === PresStatus.Autoplay ? 'red' : undefined} icon={this.layoutDoc.presentation_status === PresStatus.Autoplay ? 'pause' : 'play'} />
                     </div>
                 </Tooltip>
                 <div
@@ -2283,7 +2306,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 this.next();
                                 if (this._presTimer) {
                                     clearTimeout(this._presTimer);
-                                    this.layoutDoc.presStatus = PresStatus.Manual;
+                                    this.layoutDoc.presentation_status = PresStatus.Manual;
                                 }
                                 e.stopPropagation();
                             },
@@ -2316,7 +2339,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 </Tooltip>
                 <div className="presPanel-button-text" onClick={() => this.gotoDocument(0, this.activeItem)} style={{ display: inOverlay || this.props.PanelWidth() > 250 ? 'inline-flex' : 'none' }}>
                     {inOverlay ? '' : 'Slide'} {this.itemIndex + 1}
-                    {this.activeItem?.presIndexed !== undefined ? `(${this.activeItem.presIndexed}/${this.progressivizedItems(this.activeItem)?.length})` : ''} / {this.childDocs.length}
+                    {this.activeItem?.presentation_indexed !== undefined ? `(${this.activeItem.presentation_indexed}/${this.progressivizedItems(this.activeItem)?.length})` : ''} / {this.childDocs.length}
                 </div>
                 <div className="presPanel-divider"></div>
                 {this.props.PanelWidth() > 250 ? (
@@ -2324,7 +2347,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         className="presPanel-button-text"
                         onClick={undoBatch(
                             action(() => {
-                                this.layoutDoc.presStatus = PresStatus.Edit;
+                                this.layoutDoc.presentation_status = PresStatus.Edit;
                                 clearTimeout(this._presTimer);
                             })
                         )}>
@@ -2342,7 +2365,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     startOrPause = (makeActive = true) => {
         makeActive && this.updateCurrentPresentation();
-        if (!this.layoutDoc.presStatus || this.layoutDoc.presStatus === PresStatus.Manual || this.layoutDoc.presStatus === PresStatus.Edit) this.startPresentation(this.itemIndex);
+        if (!this.layoutDoc.presentation_status || this.layoutDoc.presentation_status === PresStatus.Manual || this.layoutDoc.presentation_status === PresStatus.Edit) this.startPresentation(this.itemIndex);
         else this.pauseAutoPres();
     };
 
@@ -2351,7 +2374,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         this.back();
         if (this._presTimer) {
             clearTimeout(this._presTimer);
-            this.layoutDoc.presStatus = PresStatus.Manual;
+            this.layoutDoc.presentation_status = PresStatus.Manual;
         }
     };
 
@@ -2360,19 +2383,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         this.next();
         if (this._presTimer) {
             clearTimeout(this._presTimer);
-            this.layoutDoc.presStatus = PresStatus.Manual;
+            this.layoutDoc.presentation_status = PresStatus.Manual;
         }
     };
 
     @undoBatch
     @action
     exitClicked = () => {
-        this.layoutDoc.presStatus = this._exitTrail?.() ?? this.exitMinimize();
+        this.layoutDoc.presentation_status = this._exitTrail?.() ?? this.exitMinimize();
         clearTimeout(this._presTimer);
     };
 
     AddToMap = (treeViewDoc: Doc, index: number[]) => {
-        if (!treeViewDoc.presentationTargetDoc) return this.childDocs; // if treeViewDoc is not a pres elements, then it's a sub-bullet of a progressivized slide which isn't added to the linearized list of pres elements since it's not really a pres element.
+        if (!treeViewDoc.presentation_targetDoc) return this.childDocs; // if treeViewDoc is not a pres elements, then it's a sub-bullet of a progressivized slide which isn't added to the linearized list of pres elements since it's not really a pres element.
         var indexNum = 0;
         for (let i = 0; i < index.length; i++) {
             indexNum += index[i] * 10 ** -i;
@@ -2388,20 +2411,23 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     };
 
     RemFromMap = (treeViewDoc: Doc, index: number[]) => {
-        if (!treeViewDoc.presentationTargetDoc) return this.childDocs; // if treeViewDoc is not a pres elements, then it's a sub-bullet of a progressivized slide which isn't added to the linearized list of pres elements since it's not really a pres element.
+        if (!treeViewDoc.presentation_targetDoc) return this.childDocs; // if treeViewDoc is not a pres elements, then it's a sub-bullet of a progressivized slide which isn't added to the linearized list of pres elements since it's not really a pres element.
         if (!this._unmounting && this.isTree) {
             this._treeViewMap.delete(treeViewDoc);
             this.dataDoc[this.presFieldKey] = new List<Doc>(this.sort(this._treeViewMap));
         }
     };
 
-    sort = (treeViewMap: Map<Doc, number>) => [...treeViewMap.entries()].sort((a: [Doc, number], b: [Doc, number]) => (a[1] > b[1] ? 1 : a[1] < b[1] ? -1 : 0)).map(kv => kv[0]);
+    sort = (treeView_Map: Map<Doc, number>) => [...treeView_Map.entries()].sort((a: [Doc, number], b: [Doc, number]) => (a[1] > b[1] ? 1 : a[1] < b[1] ? -1 : 0)).map(kv => kv[0]);
 
     render() {
         // needed to ensure that the childDocs are loaded for looking up fields
         this.childDocs.slice();
         const mode = StrCast(this.rootDoc._type_collection) as CollectionViewType;
-        const presEnd = !this.layoutDoc.presLoop && this.itemIndex === this.childDocs.length - 1 && (this.activeItem.presIndexed === undefined || NumCast(this.activeItem.presIndexed) === (this.progressivizedItems(this.activeItem)?.length ?? 0));
+        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));
         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 className="miniPres" onClick={e => e.stopPropagation()} onPointerEnter={action(e => (this._forceKeyEvents = true))}>
@@ -2420,9 +2446,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     <div className="presPanel-button" style={{ opacity: presStart ? 0.4 : 1 }} onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, this.prevClicked, false, false)}>
                         <FontAwesomeIcon icon={'arrow-left'} />
                     </div>
-                    <Tooltip title={<div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? 'Pause' : 'Autoplay'}</div>}>
+                    <Tooltip title={<div className="dash-tooltip">{this.layoutDoc.presentation_status === PresStatus.Autoplay ? 'Pause' : 'Autoplay'}</div>}>
                         <div className="presPanel-button" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, () => this.startOrPause(true), false, false)}>
-                            <FontAwesomeIcon icon={this.layoutDoc.presStatus === 'auto' ? 'pause' : 'play'} />
+                            <FontAwesomeIcon icon={this.layoutDoc.presentation_status === 'auto' ? 'pause' : 'play'} />
                         </div>
                     </Tooltip>
                     <div className="presPanel-button" style={{ opacity: presEnd ? 0.4 : 1 }} onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, this.nextClicked, false, false)}>
@@ -2436,7 +2462,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     </Tooltip>
                     <div className="presPanel-button-text">
                         Slide {this.itemIndex + 1}
-                        {this.activeItem?.presIndexed !== undefined ? `(${this.activeItem.presIndexed}/${this.progressivizedItems(this.activeItem)?.length})` : ''} / {this.childDocs.length}
+                        {this.activeItem?.presentation_indexed !== undefined ? `(${this.activeItem.presentation_indexed}/${this.progressivizedItems(this.activeItem)?.length})` : ''} / {this.childDocs.length}
                     </div>
                     <div className="presPanel-divider" />
                     <div className="presPanel-button-text" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, this.exitClicked, false, false)}>
@@ -2460,6 +2486,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 moveDocument={returnFalse}
                                 ignoreUnrendered={true}
                                 childDragAction="move"
+                                setContentView={emptyFunction}
                                 //childLayoutFitWidth={returnTrue}
                                 childOpacity={returnOne}
                                 childClickScript={PresBox.navigateToDocScript}
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index f31cf6147..aa514be3b 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -58,13 +58,13 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return this.props.DocumentView?.().props.docViewPath().lastElement()?.rootDoc;
     }
     @computed get targetDoc() {
-        return Cast(this.rootDoc.presentationTargetDoc, Doc, null) || this.rootDoc;
+        return Cast(this.rootDoc.presentation_targetDoc, Doc, null) || this.rootDoc;
     }
 
     componentDidMount() {
         this.layoutDoc.layout_hideLinkButton = true;
         this._heightDisposer = reaction(
-            () => ({ expand: this.rootDoc.presExpandInlineButton, height: this.collapsedHeight }),
+            () => ({ expand: this.rootDoc.presentation_expandInlineButton, height: this.collapsedHeight }),
             ({ expand, height }) => (this.layoutDoc._height = height + (expand ? this.expandViewHeight : 0)),
             { fireImmediately: true }
         );
@@ -79,7 +79,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     ScreenToLocalListTransform = (xCord: number, yCord: number) => [xCord, yCord];
 
     @action
-    presExpandDocumentClick = () => (this.rootDoc.presExpandInlineButton = !this.rootDoc.presExpandInlineButton);
+    presExpandDocumentClick = () => (this.rootDoc.presentation_expandInlineButton = !this.rootDoc.presentation_expandInlineButton);
 
     embedHeight = (): number => this.collapsedHeight + this.expandViewHeight;
     // embedWidth = () => this.props.PanelWidth();
@@ -94,7 +94,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
      * presentation element.
      */
     @computed get renderEmbeddedInline() {
-        return !this.rootDoc.presExpandInlineButton || !this.targetDoc ? null : (
+        return !this.rootDoc.presentation_expandInlineButton || !this.targetDoc ? null : (
             <div className="presItem-embedded" style={{ height: this.embedHeight(), width: '50%' }}>
                 <DocumentView
                     Document={PresBox.targetRenderedDoc(this.rootDoc)}
@@ -158,9 +158,9 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @computed get transition() {
         let transitionInS: number;
-        if (this.rootDoc.presTransition) transitionInS = NumCast(this.rootDoc.presTransition) / 1000;
+        if (this.rootDoc.presentation_transition) transitionInS = NumCast(this.rootDoc.presentation_transition) / 1000;
         else transitionInS = 0.5;
-        return this.rootDoc.presMovement === PresMovement.Jump || this.rootDoc.presMovement === PresMovement.None ? null : 'M: ' + transitionInS + 's';
+        return this.rootDoc.presentation_movement === PresMovement.Jump || this.rootDoc.presentation_movement === PresMovement.None ? null : 'M: ' + transitionInS + 's';
     }
 
     private _itemRef: React.RefObject<HTMLDivElement> = React.createRef();
@@ -238,7 +238,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     onPointerMove = (e: PointerEvent) => {
         const slide = this._itemRef.current!;
-        const dragIsPresItem = DragManager.docsBeingDragged.some(d => d.presentationTargetDoc);
+        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.
@@ -296,12 +296,12 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     updateCapturedContainerLayout = (presTargetDoc: Doc, activeItem: Doc) => {
         const targetDoc = DocCast(presTargetDoc.annotationOn) ?? presTargetDoc;
-        activeItem.presX = NumCast(targetDoc.x);
-        activeItem.presY = NumCast(targetDoc.y);
-        activeItem.presRotation = NumCast(targetDoc.rotation);
-        activeItem.presWidth = NumCast(targetDoc.width);
-        activeItem.presHeight = NumCast(targetDoc.height);
-        activeItem.presPinLayout = true;
+        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 = true;
     };
     /**
      * Method called for updating the view of the currently selected document
@@ -421,7 +421,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 <div
                     className="slideButton"
                     onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this.updateCapturedContainerLayout(targetDoc, activeItem), true)}
-                    style={{ opacity: activeItem.presPinLayout ? 1 : 0.5, fontWeight: 700, display: 'flex' }}>
+                    style={{ opacity: activeItem.config_pinLayout ? 1 : 0.5, fontWeight: 700, display: 'flex' }}>
                     L
                 </div>
             </Tooltip>
@@ -431,7 +431,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 <div
                     className="slideButton"
                     onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this.updateCapturedViewContents(targetDoc, activeItem))}
-                    style={{ opacity: activeItem.presPinData || activeItem.presPinView ? 1 : 0.5, fontWeight: 700, display: 'flex' }}>
+                    style={{ opacity: activeItem.config_pinData || activeItem.config_pinView ? 1 : 0.5, fontWeight: 700, display: 'flex' }}>
                     C
                 </div>
             </Tooltip>
@@ -445,19 +445,19 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         );
         if (this.indexInPres !== 0) {
             items.push(
-                <Tooltip key="arrow" title={<div className="dash-tooltip">{activeItem.groupWithUp ? 'Ungroup' : 'Group with up'}</div>}>
+                <Tooltip key="arrow" title={<div className="dash-tooltip">{activeItem.presentation_groupWithUp ? 'Ungroup' : 'Group with up'}</div>}>
                     <div
                         className="slideButton"
-                        onClick={() => (activeItem.groupWithUp = (NumCast(activeItem.groupWithUp) + 1) % 3)}
+                        onClick={() => (activeItem.presentation_groupWithUp = (NumCast(activeItem.presentation_groupWithUp) + 1) % 3)}
                         style={{
                             zIndex: 1000 - this.indexInPres,
                             fontWeight: 700,
-                            backgroundColor: activeItem.groupWithUp ? (presColorBool ? presBoxColor : Colors.MEDIUM_BLUE) : undefined,
-                            outline: NumCast(activeItem.groupWithUp) > 1 ? 'solid black 1px' : undefined,
-                            height: activeItem.groupWithUp ? 53 : 18,
-                            transform: activeItem.groupWithUp ? 'translate(0, -17px)' : undefined,
+                            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.groupWithUp ? 'rotate(180deg) translate(0, -17.5px)' : 'rotate(0deg)' }}>
+                        <div style={{ transform: activeItem.presentation_groupWithUp ? 'rotate(180deg) translate(0, -17.5px)' : 'rotate(0deg)' }}>
                             <FontAwesomeIcon icon={'arrow-up'} onPointerDown={e => e.stopPropagation()} />
                         </div>
                     </div>
@@ -465,14 +465,14 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
             );
         }
         items.push(
-            <Tooltip key="eye" title={<div className="dash-tooltip">{this.rootDoc.presExpandInlineButton ? 'Minimize' : 'Expand'}</div>}>
+            <Tooltip key="eye" title={<div className="dash-tooltip">{this.rootDoc.presentation_expandInlineButton ? 'Minimize' : 'Expand'}</div>}>
                 <div
                     className="slideButton"
                     onClick={e => {
                         e.stopPropagation();
                         this.presExpandDocumentClick();
                     }}>
-                    <FontAwesomeIcon icon={this.rootDoc.presExpandInlineButton ? 'eye-slash' : 'eye'} onPointerDown={e => e.stopPropagation()} />
+                    <FontAwesomeIcon icon={this.rootDoc.presentation_expandInlineButton ? 'eye-slash' : 'eye'} onPointerDown={e => e.stopPropagation()} />
                 </div>
             </Tooltip>
         );
@@ -534,7 +534,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             style={{
                                 display: 'inline-flex',
                                 pointerEvents: isSelected ? undefined : 'none',
-                                width: `calc(100% ${this.rootDoc.presExpandInlineButton ? '- 50%' : ''} - ${this.presButtons.length * 22}px`,
+                                width: `calc(100% ${this.rootDoc.presentation_expandInlineButton ? '- 50%' : ''} - ${this.presButtons.length * 22}px`,
                                 cursor: isSelected ? 'text' : 'grab',
                             }}>
                             <div
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index db6b1f011..caa72c9dc 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -24,7 +24,7 @@ export class Annotation extends React.Component<IAnnotationProps> {
     render() {
         return (
             <div style={{ display: this.props.anno.textCopied && !Doc.isBrushedHighlightedDegree(this.props.anno) ? 'none' : undefined }}>
-                {DocListCast(this.props.anno.textInlineAnnotations).map(a => (
+                {DocListCast(this.props.anno.text_inlineAnnotations).map(a => (
                     <RegionAnnotation pointerEvents={this.props.pointerEvents} {...this.props} document={a} key={a[Id]} />
                 ))}
             </div>
@@ -61,7 +61,7 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
     isTargetToggler = () => BoolCast(this.annoTextRegion.followLinkToggle);
     @undoBatch
     showTargetTrail = (anchor: Doc) => {
-        const trail = DocCast(anchor.presTrail);
+        const trail = DocCast(anchor.presentationTrail);
         if (trail) {
             Doc.ActivePresentation = trail;
             this.props.addDocTab(trail, OpenWhere.replaceRight);
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 1319a236d..395fbd1ca 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -68,7 +68,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
     private _styleRule: any; // stylesheet rule for making hyperlinks clickable
     private _retries = 0; // number of times tried to create the PDF viewer
     private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void);
-    private _setBrushViewer: undefined | ((view: { width: number; height: number; panX: number; panY: number }) => void);
+    private _setBrushViewer: undefined | ((view: { width: number; height: number; panX: number; panY: number }, transTime: number) => void);
     private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
     private _disposers: { [name: string]: IReactionDisposer } = {};
     private _viewer: React.RefObject<HTMLDivElement> = React.createRef();
@@ -95,7 +95,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         return DocUtils.FilterDocs(DocListCast(this.props.dataDoc[this.props.fieldKey + '_annotations']), this.props.childFilters(), this.props.childFiltersByRanges());
     }
     @computed get inlineTextAnnotations() {
-        return this.allAnnotations.filter(a => a.textInlineAnnotations);
+        return this.allAnnotations.filter(a => a.text_inlineAnnotations);
     }
 
     componentDidMount = async () => {
@@ -195,7 +195,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         return focusSpeed;
     };
     crop = (region: Doc | undefined, addCrop?: boolean) => this.props.crop(region, addCrop);
-    brushView = (view: { width: number; height: number; panX: number; panY: number }) => this._setBrushViewer?.(view);
+    brushView = (view: { width: number; height: number; panX: number; panY: number }, transTime: number) => this._setBrushViewer?.(view, transTime);
 
     @action
     setupPdfJsViewer = async () => {
@@ -470,7 +470,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
     };
 
     setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => (this._setPreviewCursor = func);
-    setBrushViewer = (func?: (view: { width: number; height: number; panX: number; panY: number }) => void) => (this._setBrushViewer = func);
+    setBrushViewer = (func?: (view: { width: number; height: number; panX: number; panY: number }, transTime: number) => void) => (this._setBrushViewer = func);
 
     @action
     onZoomWheel = (e: React.WheelEvent) => {
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index eb52cff88..d3912b8a0 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1363,9 +1363,9 @@ export namespace Doc {
             UnhighlightWatchers.push(watcher);
         } else watcher();
     }
-    export function linkFollowHighlight(destDoc: Doc | Doc[], dataAndDisplayDocs = true, presEffect?: Doc) {
+    export function linkFollowHighlight(destDoc: Doc | Doc[], dataAndDisplayDocs = true, presentation_effect?: Doc) {
         linkFollowUnhighlight();
-        (destDoc instanceof Doc ? [destDoc] : destDoc).forEach(doc => Doc.HighlightDoc(doc, dataAndDisplayDocs, presEffect));
+        (destDoc instanceof Doc ? [destDoc] : destDoc).forEach(doc => Doc.HighlightDoc(doc, dataAndDisplayDocs, presentation_effect));
         document.removeEventListener('pointerdown', linkFollowUnhighlight);
         document.addEventListener('pointerdown', linkFollowUnhighlight);
         if (UnhighlightTimer) clearTimeout(UnhighlightTimer);
@@ -1380,11 +1380,11 @@ export namespace Doc {
         if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate || doc.opacity === 0) return false;
         return doc[Highlight] || Doc.GetProto(doc)[Highlight];
     }
-    export function HighlightDoc(doc: Doc, dataAndDisplayDocs = true, presEffect?: Doc) {
+    export function HighlightDoc(doc: Doc, dataAndDisplayDocs = true, presentation_effect?: Doc) {
         runInAction(() => {
             highlightedDocs.add(doc);
             doc[Highlight] = true;
-            doc[Animation] = presEffect;
+            doc[Animation] = presentation_effect;
             if (dataAndDisplayDocs) {
                 highlightedDocs.add(Doc.GetProto(doc));
                 Doc.GetProto(doc)[Highlight] = true;
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index 76b287be7..e33a17416 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -72,11 +72,11 @@ export const documentSchema = createSchema({
     stroke_endMarker: 'string',
     stroke_dash: 'string',
     textTransform: 'string',
-    treeViewOpen: 'boolean', //  flag denoting whether the documents sub-tree (contents) is visible or hidden
-    treeViewExpandedView: 'string', // name of field whose contents are being displayed as the document's subtree
-    treeViewExpandedViewLock: 'boolean', // whether the expanded view can be changed
-    treeViewOpenIsTransient: 'boolean', // ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document)
-    treeViewType: 'string', // whether tree view is an outline, file syste or (default) hierarchy. For outline, clicks edit document titles immediately since double-click opening is turned off
+    treeView_Open: 'boolean', //  flag denoting whether the documents sub-tree (contents) is visible or hidden
+    treeView_ExpandedView: 'string', // name of field whose contents are being displayed as the document's subtree
+    treeView_ExpandedViewLock: 'boolean', // whether the expanded view can be changed
+    treeView_OpenIsTransient: 'boolean', // ignores the treeView_Open flag (for allowing a view to not be slaved to other views of the document)
+    treeView_Type: 'string', // whether tree view is an outline, file syste or (default) hierarchy. For outline, clicks edit document titles immediately since double-click opening is turned off
 
     // interaction and linking properties
     ignoreClick: 'boolean', // whether documents ignores input clicks (but does not ignore manipulation and other events)
-- 
cgit v1.2.3-70-g09d2


From d3ecace5f03233e5d5ab2354c3f482418287ca9a Mon Sep 17 00:00:00 2001
From: Sophie Zhang <sophie_zhang@brown.edu>
Date: Thu, 17 Aug 2023 16:14:53 -0400
Subject: broken version

---
 package-lock.json                                  |  97 +++++++++++++++--
 src/client/util/CurrentUserUtils.ts                |   2 +
 src/client/views/MainView.tsx                      |   2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   1 +
 src/client/views/global/globalScripts.ts           |   8 +-
 src/client/views/nodes/FontIconBox/FontIconBox.tsx |  28 +++++
 src/client/views/nodes/ImageBox.tsx                |   6 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  76 +++++++-------
 .../views/nodes/generativeFill/GenerativeFill.tsx  | 115 +++++++++++++--------
 .../nodes/generativeFill/GenerativeFillButtons.tsx |   6 +-
 src/client/views/pdf/AnchorMenu.tsx                |   1 +
 src/client/views/pdf/GPTPopup/GPTPopup.scss        |   2 +
 src/client/views/pdf/GPTPopup/GPTPopup.tsx         |  99 +++++++++---------
 src/client/views/pdf/PDFViewer.tsx                 |   4 -
 14 files changed, 301 insertions(+), 146 deletions(-)

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

diff --git a/package-lock.json b/package-lock.json
index 658e783f9..20d582e5b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6507,9 +6507,9 @@
             }
         },
         "browndash-components": {
-            "version": "0.0.92",
-            "resolved": "https://registry.npmjs.org/browndash-components/-/browndash-components-0.0.92.tgz",
-            "integrity": "sha512-eE/6WQNZiLnaXUKyoaMm0PDYjExUsFJ9VTAIIxROpYPosIBKWNZ743xaOfmehib5us9hEXJb0CvUFJQb8rzDVw==",
+            "version": "0.0.93",
+            "resolved": "https://registry.npmjs.org/browndash-components/-/browndash-components-0.0.93.tgz",
+            "integrity": "sha512-JL9Hi7Nq+zHZtjLCQSuGhID0Hd7X6sARutyJqbECfsLWACsIC/JfeEQZdUeH5CqO+R7pJKeoWM82AIkWGtN2Xw==",
             "requires": {
                 "@emotion/react": "^11.11.1",
                 "@emotion/styled": "^11.11.0",
@@ -7063,12 +7063,97 @@
                                         "strip-ansi": "^7.0.1"
                                     }
                                 },
+                                "string-width-cjs": {
+                                    "version": "npm:string-width@4.2.3",
+                                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+                                    "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+                                    "requires": {
+                                        "emoji-regex": "^8.0.0",
+                                        "is-fullwidth-code-point": "^3.0.0",
+                                        "strip-ansi": "^6.0.1"
+                                    },
+                                    "dependencies": {
+                                        "ansi-regex": {
+                                            "version": "5.0.1",
+                                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+                                            "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+                                        },
+                                        "emoji-regex": {
+                                            "version": "8.0.0",
+                                            "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+                                            "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+                                        },
+                                        "strip-ansi": {
+                                            "version": "6.0.1",
+                                            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+                                            "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+                                            "requires": {
+                                                "ansi-regex": "^5.0.1"
+                                            }
+                                        }
+                                    }
+                                },
                                 "strip-ansi": {
                                     "version": "7.1.0",
                                     "bundled": true,
                                     "requires": {
                                         "ansi-regex": "^6.0.1"
                                     }
+                                },
+                                "strip-ansi-cjs": {
+                                    "version": "npm:strip-ansi@6.0.1",
+                                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+                                    "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+                                    "requires": {
+                                        "ansi-regex": "^5.0.1"
+                                    },
+                                    "dependencies": {
+                                        "ansi-regex": {
+                                            "version": "5.0.1",
+                                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+                                            "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+                                        }
+                                    }
+                                },
+                                "wrap-ansi-cjs": {
+                                    "version": "npm:wrap-ansi@7.0.0",
+                                    "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+                                    "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+                                    "requires": {
+                                        "ansi-styles": "^4.0.0",
+                                        "string-width": "^4.1.0",
+                                        "strip-ansi": "^6.0.0"
+                                    },
+                                    "dependencies": {
+                                        "ansi-regex": {
+                                            "version": "5.0.1",
+                                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+                                            "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+                                        },
+                                        "emoji-regex": {
+                                            "version": "8.0.0",
+                                            "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+                                            "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+                                        },
+                                        "string-width": {
+                                            "version": "4.2.3",
+                                            "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+                                            "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+                                            "requires": {
+                                                "emoji-regex": "^8.0.0",
+                                                "is-fullwidth-code-point": "^3.0.0",
+                                                "strip-ansi": "^6.0.1"
+                                            }
+                                        },
+                                        "strip-ansi": {
+                                            "version": "6.0.1",
+                                            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+                                            "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+                                            "requires": {
+                                                "ansi-regex": "^5.0.1"
+                                            }
+                                        }
+                                    }
                                 }
                             }
                         },
@@ -8573,7 +8658,7 @@
                             }
                         },
                         "string-width-cjs": {
-                            "version": "npm:string-width@4.2.3",
+                            "version": "npm:string-width-cjs@4.2.3",
                             "bundled": true,
                             "requires": {
                                 "emoji-regex": "^8.0.0",
@@ -8596,7 +8681,7 @@
                             }
                         },
                         "strip-ansi-cjs": {
-                            "version": "npm:strip-ansi@6.0.1",
+                            "version": "npm:strip-ansi-cjs@6.0.1",
                             "bundled": true,
                             "requires": {
                                 "ansi-regex": "^5.0.1"
@@ -8755,7 +8840,7 @@
                             }
                         },
                         "wrap-ansi-cjs": {
-                            "version": "npm:wrap-ansi@7.0.0",
+                            "version": "npm:wrap-ansi-cjs@7.0.0",
                             "bundled": true,
                             "requires": {
                                 "ansi-styles": "^4.0.0",
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index c9a5175bb..6592ecffc 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -633,6 +633,8 @@ export class CurrentUserUtils {
             { title: "Snap", icon: "th",                    toolTip: "Show Snap Lines",     btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"snaplines",  funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
             { title: "Grid",          icon: "border-all",   toolTip: "Show Grid",           btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"grid",       funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
             { title: "View All",   icon: "object-group", toolTip: "Fit all Docs to View",   btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"viewAll",    funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
+            // want the same style as toggle button, but don't want it to act as an actual toggle, so set disableToggle to true,
+            { title: "Fit All",   icon: "arrows-left-right", toolTip: "Fit all Docs to View (persistent)", btnType: ButtonType.IconClickButton, ignoreClick: false, expertMode: false, toolType:"viewAllPersist",    funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
             { title: "Clusters",      icon: "braille",      toolTip: "Show Doc Clusters",   btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"clusters",   funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
             { title: "Cards",         icon: "brain",        toolTip: "Flashcards",          btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"flashcards", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
             { title: "Arrange",icon:"arrow-down-short-wide",toolTip:"Toggle Auto Arrange",  btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"arrange",    funcs: {hidden: 'IsNoviceMode()'}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 60eb64caa..8fba9b886 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -80,7 +80,6 @@ export class MainView extends React.Component {
     @observable public imageEditorSource: string = '';
     @action public setImageEditorSource = (source: string) => (this.imageEditorSource = source);
     @observable public imageRootDoc: Doc | undefined;
-    @action public setImageRootDoc = (doc: Doc) => (this.imageRootDoc = doc);
     @observable public addDoc: ((doc: Doc | Doc[], annotationKey?: string) => boolean) | undefined;
 
     @observable public LastButton: Opt<Doc>;
@@ -350,6 +349,7 @@ export class MainView extends React.Component {
                 fa.faMousePointer,
                 fa.faMusic,
                 fa.faObjectGroup,
+                fa.faArrowsLeftRight,
                 fa.faPause,
                 fa.faPen,
                 fa.faPenNib,
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 29122cb91..f353dfab7 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1599,6 +1599,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                     () => this.rootDoc.fitContentOnce,
                     fitContentOnce => {
                         if (fitContentOnce) this.fitContentOnce();
+                        this.rootDoc.fitContentOnce = false;
                     },
                     { fireImmediately: true, name: 'fitContent' }
                 );
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index 256377758..38bf1042d 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -85,10 +85,10 @@ ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) {
     selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log('[FontIconBox.tsx] toggleOverlay failed');
 });
 
-ScriptingGlobals.add(function showFreeform(attr: 'flashcards' | 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll', checkResult?: boolean) {
+ScriptingGlobals.add(function showFreeform(attr: 'flashcards' | 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll' | 'viewAllPersist', checkResult?: boolean) {
     const selected = SelectionManager.Docs().lastElement();
     // prettier-ignore
-    const map: Map<'flashcards' | 'grid' | 'snaplines' | 'clusters' | 'arrange'| 'viewAll', { waitForRender?: boolean, checkResult: (doc:Doc) => any; setDoc: (doc:Doc) => void;}> = new Map([
+    const map: Map<'flashcards' | 'grid' | 'snaplines' | 'clusters' | 'arrange'| 'viewAll' | 'viewAllPersist', { waitForRender?: boolean, checkResult: (doc:Doc) => any; setDoc: (doc:Doc) => void;}> = new Map([
         ['grid', {
             checkResult: (doc:Doc) => BoolCast(doc._freeform_backgroundGrid, false),
             setDoc: (doc:Doc) => doc._freeform_backgroundGrid = !doc._freeform_backgroundGrid,
@@ -101,6 +101,10 @@ ScriptingGlobals.add(function showFreeform(attr: 'flashcards' | 'grid' | 'snapli
             checkResult: (doc:Doc) => BoolCast(doc._freeform_fitContentsToBox, false),
             setDoc: (doc:Doc) => doc._freeform_fitContentsToBox = !doc._freeform_fitContentsToBox,
         }],
+        ['viewAllPersist', {
+            checkResult: (doc:Doc) => false,
+            setDoc: (doc:Doc) => doc.fitContentOnce = true
+        }],
         ['clusters', {
             waitForRender: true, // flags that undo batch should terminate after a re-render giving the script the chance to fire
             checkResult: (doc:Doc) => BoolCast(doc._freeform_useClusters, false),
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index 91eac675f..90ec212fc 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -29,6 +29,7 @@ export enum ButtonType {
     DropdownList = 'dropdownList',
     DropdownButton = 'dropdownBtn',
     ClickButton = 'clickBtn',
+    IconClickButton = 'iconClickBtn',
     ToggleButton = 'toggleBtn',
     ColorButton = 'colorBtn',
     ToolButton = 'toolBtn',
@@ -346,6 +347,32 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         );
     }
 
+    @computed get iconClickButton() {
+        // Determine the type of toggle button
+        const buttonText: string = StrCast(this.rootDoc.buttonText);
+        const tooltip: string = StrCast(this.rootDoc.toolTip);
+
+        const script = ScriptCast(this.rootDoc.onClick);
+        const toggleStatus = script ? script.script.run({ this: this.layoutDoc, self: this.rootDoc, value: undefined, _readOnly_: true }).result : false;
+        // Colors
+        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
+        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
+
+        return (
+            <Toggle
+                tooltip={`${tooltip}`}
+                toggleType={ToggleType.BUTTON}
+                type={Type.PRIM}
+                toggleStatus={toggleStatus}
+                text={buttonText}
+                color={color}
+                icon={this.Icon(color)!}
+                label={this.label}
+                onPointerDown={() => script.script.run({ this: this.layoutDoc, self: this.rootDoc, value: !toggleStatus, _readOnly_: false })}
+            />
+        );
+    }
+
     /**
      * Default
      */
@@ -393,6 +420,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
             case ButtonType.DropdownButton:     return this.dropdownButton;
             case ButtonType.MultiToggleButton:  return this.multiToggleButton;
             case ButtonType.ToggleButton:       return this.toggleButton;
+            case ButtonType.IconClickButton: return this.iconClickButton;
             case ButtonType.ClickButton:
             case ButtonType.ToolButton: return <IconButton {...btnProps} size={Size.LARGE} color={color} />;
             case ButtonType.TextButton: return <Button     {...btnProps} text={StrCast(this.rootDoc.buttonText)}/>;
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 4ce359f3f..352e0fbdc 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -252,8 +252,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             funcs.push({
                 description: 'Open Image Editor',
                 event: action(() => {
-                    MainView.Instance.setImageEditorOpen(true);
-                    MainView.Instance.setImageEditorSource(this.choosePath(field.url));
+                    MainView.Instance.imageEditorOpen = true;
+                    MainView.Instance.imageEditorSource = this.choosePath(field.url);
                     MainView.Instance.addDoc = this.props.addDocument;
                     MainView.Instance.imageRootDoc = this.rootDoc;
                 }),
@@ -306,7 +306,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     choosePath(url: URL) {
         const lower = url.href.toLowerCase();
         if (url.protocol === 'data') return url.href;
-        if (url.href.indexOf(window.location.origin) === -1 && url.href.indexOf("dashblobstore") === -1) return Utils.CorsProxy(url.href);
+        if (url.href.indexOf(window.location.origin) === -1 && url.href.indexOf('dashblobstore') === -1) return Utils.CorsProxy(url.href);
         if (!/\.(png|jpg|jpeg|gif|webp)$/.test(lower)) return `/assets/unknown-file-icon-hi.png`;
 
         const ext = extname(url.href);
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 332f0f467..5acc45d02 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -946,44 +946,46 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         console.log('Generate image from text: ', (this.dataDoc.text as RichTextField)?.Text);
         GPTPopup.Instance?.setTextAnchor(this.getAnchor(false));
         GPTPopup.Instance?.setImgTargetDoc(this.rootDoc);
-        GPTPopup.Instance.setImgUrls([]);
-        GPTPopup.Instance.setMode(GPTPopupMode.IMAGE);
-        GPTPopup.Instance.setVisible(true);
+        // GPTPopup.Instance.setImgUrls([]);
+        // GPTPopup.Instance.setMode(GPTPopupMode.IMAGE);
+        // GPTPopup.Instance.setVisible(true);
         GPTPopup.Instance.addToCollection = this.props.addDocument;
-        GPTPopup.Instance.setLoading(true);
-
-        try {
-            // make this support multiple images
-            let image_urls = await gptImageCall((this.dataDoc.text as RichTextField)?.Text);
-            console.log(image_urls);
-            if (image_urls) {
-                const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [image_urls[0]] });
-                const source = Utils.prepend(result.accessPaths.agnostic.client);
-                GPTPopup.Instance.setImgUrls([source]);
-
-                // const newDoc = Docs.Create.ImageDocument(source, {
-                //     x: NumCast(this.rootDoc.x) + NumCast(this.layoutDoc._width) + 10,
-                //     y: NumCast(this.rootDoc.y),
-                //     _height: 200,
-                //     _width: 200,
-                //     data_nativeWidth: result.nativeWidth,
-                //     data_nativeHeight: result.nativeHeight,
-                // });
-                // if (Doc.IsInMyOverlay(this.rootDoc)) {
-                //     newDoc.overlayX = this.rootDoc.x;
-                //     newDoc.overlayY = NumCast(this.rootDoc.y) + NumCast(this.rootDoc._height);
-                //     Doc.AddToMyOverlay(newDoc);
-                // } else {
-                //     this.props.addDocument?.(newDoc);
-                // }
-                // // Create link between prompt and image
-                // DocUtils.MakeLink(this.rootDoc, newDoc, { link_relationship: 'Image Prompt' });
-            }
-        } catch (err) {
-            console.log(err);
-            return '';
-        }
-        GPTPopup.Instance.setLoading(false);
+        // GPTPopup.Instance.setLoading(true);
+        GPTPopup.Instance.setImgDesc((this.dataDoc.text as RichTextField)?.Text);
+        GPTPopup.Instance.generateImage();
+
+        // try {
+        //     // make this support multiple images
+        //     let image_urls = await gptImageCall((this.dataDoc.text as RichTextField)?.Text);
+        //     console.log(image_urls);
+        //     if (image_urls) {
+        //         const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [image_urls[0]] });
+        //         const source = Utils.prepend(result.accessPaths.agnostic.client);
+        //         GPTPopup.Instance.setImgUrls([source]);
+
+        //         // const newDoc = Docs.Create.ImageDocument(source, {
+        //         //     x: NumCast(this.rootDoc.x) + NumCast(this.layoutDoc._width) + 10,
+        //         //     y: NumCast(this.rootDoc.y),
+        //         //     _height: 200,
+        //         //     _width: 200,
+        //         //     data_nativeWidth: result.nativeWidth,
+        //         //     data_nativeHeight: result.nativeHeight,
+        //         // });
+        //         // if (Doc.IsInMyOverlay(this.rootDoc)) {
+        //         //     newDoc.overlayX = this.rootDoc.x;
+        //         //     newDoc.overlayY = NumCast(this.rootDoc.y) + NumCast(this.rootDoc._height);
+        //         //     Doc.AddToMyOverlay(newDoc);
+        //         // } else {
+        //         //     this.props.addDocument?.(newDoc);
+        //         // }
+        //         // // Create link between prompt and image
+        //         // DocUtils.MakeLink(this.rootDoc, newDoc, { link_relationship: 'Image Prompt' });
+        //     }
+        // } catch (err) {
+        //     console.log(err);
+        //     return '';
+        // }
+        // GPTPopup.Instance.setLoading(false);
     };
 
     breakupDictation = () => {
diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.tsx b/src/client/views/nodes/generativeFill/GenerativeFill.tsx
index f8f9fe077..99068d647 100644
--- a/src/client/views/nodes/generativeFill/GenerativeFill.tsx
+++ b/src/client/views/nodes/generativeFill/GenerativeFill.tsx
@@ -3,7 +3,7 @@ import React = require('react');
 import { useEffect, useRef, useState } from 'react';
 import { APISuccess, ImageUtility } from './generativeFillUtils/ImageHandler';
 import { BrushHandler } from './generativeFillUtils/BrushHandler';
-import { Box, IconButton, Slider, TextField } from '@mui/material';
+import { Box, Checkbox, FormControlLabel, IconButton, Slider, TextField } from '@mui/material';
 import { CursorData, Point } from './generativeFillUtils/generativeFillInterfaces';
 import { activeColor, canvasSize, eraserColor, freeformRenderSize, newCollectionSize, offsetDistanceY, offsetX } from './generativeFillUtils/generativeFillConstants';
 import { PointerHandler } from './generativeFillUtils/PointerHandler';
@@ -19,6 +19,7 @@ import { Cast, DocCast, NumCast } from '../../../../fields/Types';
 import { CollectionDockingView } from '../../collections/CollectionDockingView';
 import { OpenWhere, OpenWhereMod } from '../DocumentView';
 import { Oval } from 'react-loader-spinner';
+import { CheckBox } from '../../search/CheckBox';
 
 /**
  * For images not 1024x1024 fill in the rest in solid black, or a
@@ -72,6 +73,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
     const [input, setInput] = useState('');
     const [loading, setLoading] = useState(false);
     const [saveLoading, setSaveLoading] = useState(false);
+    const [isNewCollection, setIsNewCollection] = useState(false);
     // the current image in the main canvas
     const currImg = useRef<HTMLImageElement | null>(null);
     // the unedited version of each generation (parent)
@@ -81,9 +83,6 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
     // stores redo stack
     const redoStack = useRef<string[]>([]);
 
-    // early stage properly, likely will get rid of
-    const freeformPosition = useRef<number[]>([0, 0]);
-
     // which urls were already saved to canvas
     const savedSrcs = useRef<Set<string>>(new Set());
 
@@ -91,7 +90,6 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
     const newCollectionRef = useRef<Doc | null>(null);
     const parentDoc = useRef<Doc | null>(null);
     const childrenDocs = useRef<Doc[]>([]);
-    const addToExistingCollection = useRef<boolean>(false);
 
     // Undo and Redo
     const handleUndo = () => {
@@ -111,10 +109,13 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
     };
 
     const handleRedo = () => {
-        // TODO: handle undo as well
+        const ctx = ImageUtility.getCanvasContext(canvasRef);
+        if (!ctx || !currImg.current || !canvasRef.current) return;
+
         const target = redoStack.current[redoStack.current.length - 1];
         if (!target) {
         } else {
+            undoStack.current = [...undoStack.current, canvasRef.current?.toDataURL()];
             const img = new Image();
             img.src = target;
             ImageUtility.drawImgToCanvas(img, canvasRef);
@@ -188,7 +189,6 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
         ImageUtility.drawImgToCanvas(img, canvasRef);
         currImg.current = img;
         originalImg.current = img;
-        freeformPosition.current = [0, 0];
 
         return () => {
             console.log('cleanup');
@@ -197,7 +197,6 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
             childrenDocs.current = [];
             currImg.current = null;
             originalImg.current = null;
-            freeformPosition.current = [0, 0];
             undoStack.current = [];
             redoStack.current = [];
         };
@@ -266,24 +265,25 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
 
             // create first image
             if (!newCollectionRef.current) {
-                if (addToExistingCollection.current) {
+                if (!isNewCollection) {
+                    // newcollection should stay null
+                } else {
+                    if (!(originalImg.current && imageRootDoc)) return;
+                    // create new collection and add it to the view
+                    newCollectionRef.current = Docs.Create.FreeformDocument([], {
+                        x: NumCast(imageRootDoc.x) + NumCast(imageRootDoc._width) + offsetX,
+                        y: NumCast(imageRootDoc.y),
+                        _width: newCollectionSize,
+                        _height: newCollectionSize,
+                        title: 'Image edit collection',
+                    });
+                    DocUtils.MakeLink(imageRootDoc, newCollectionRef.current, { link_relationship: 'Image Edit Version History', link_displayLine: false });
+                    // add the doc to the main freeform
+                    addDoc?.(newCollectionRef.current);
+                    await createNewImgDoc(originalImg.current, true);
                 }
-                if (!(originalImg.current && imageRootDoc)) return;
-                console.log('creating first image');
-                // create new collection and add it to the view
-                newCollectionRef.current = Docs.Create.FreeformDocument([], {
-                    x: NumCast(imageRootDoc.x) + NumCast(imageRootDoc._width) + offsetX,
-                    y: NumCast(imageRootDoc.y),
-                    _width: newCollectionSize,
-                    _height: newCollectionSize,
-                    title: 'Image edit collection',
-                });
-                DocUtils.MakeLink(imageRootDoc, newCollectionRef.current, { link_relationship: 'Image Edit Version History', link_displayLine: false });
-                // add the doc to the main freeform
-                // addDoc?.(newCollectionRef.current);
-                await createNewImgDoc(originalImg.current, true);
             } else {
-                parentDoc.current = childrenDocs.current[childrenDocs.current.length - 1];
+                // parentDoc.current = childrenDocs.current[childrenDocs.current.length - 1];
                 childrenDocs.current = [];
             }
 
@@ -292,12 +292,24 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
             const { urls } = res as APISuccess;
             const image = new Image();
             image.src = urls[0];
+
+            // need to crop images agh
+            // save to dash
+
+            // const imageRes = await Promise.all(
+            //     urls.map(async url => {
+            //         const newImg = new Image();
+            //         newImg.src = url;
+            //         await onSave(newImg);
+            //     })
+            // );
+
+            // map each url to [url, imgDoc]
             setEdits(urls);
+
             ImageUtility.drawImgToCanvas(image, canvasRef);
             currImg.current = image;
-            onSave();
-            freeformPosition.current[0] += 1;
-            freeformPosition.current[1] = 0;
+            // onSave(currImg.current);
         } catch (err) {
             console.log(err);
         }
@@ -324,7 +336,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
 
     // creates a new image document and returns its reference
     const createNewImgDoc = async (img: HTMLImageElement, firstDoc: boolean): Promise<Doc | undefined> => {
-        if (!newCollectionRef.current || !imageRootDoc) return;
+        if (!imageRootDoc) return;
         const src = img.src;
         const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [src] });
         const source = Utils.prepend(result.accessPaths.agnostic.client);
@@ -332,7 +344,6 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
         if (firstDoc) {
             const x = 0;
             const initialY = 0;
-            console.log('first doc');
 
             const newImg = Docs.Create.ImageDocument(source, {
                 x: x,
@@ -343,16 +354,19 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
                 data_nativeHeight: result.nativeHeight,
             });
 
-            Doc.AddDocToList(newCollectionRef.current, undefined, newImg);
+            if (isNewCollection && newCollectionRef.current) {
+                Doc.AddDocToList(newCollectionRef.current, undefined, newImg);
+            } else {
+                addDoc?.(newImg);
+            }
+
             parentDoc.current = newImg;
             return newImg;
         } else {
             if (!parentDoc.current) return;
             const x = NumCast(parentDoc.current.x) + freeformRenderSize + offsetX;
             // dummy position
-            console.log('creating child elements');
             const initialY = 0;
-
             const newImg = Docs.Create.ImageDocument(source, {
                 x: x,
                 y: initialY,
@@ -363,21 +377,26 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
             });
 
             childrenDocs.current.push(newImg);
+
+            if (isNewCollection && newCollectionRef.current) {
+                Doc.AddDocToList(newCollectionRef.current, undefined, newImg);
+            } else {
+                addDoc?.(newImg);
+            }
+
             DocUtils.MakeLink(parentDoc.current, newImg, { link_relationship: 'Image Edit', link_displayLine: true });
             adjustImgPositions();
-
-            Doc.AddDocToList(newCollectionRef.current, undefined, newImg);
             return newImg;
         }
     };
 
     // need to maybe call on every img click, not just when the save btn is clicked
-    const onSave = async () => {
+    const onSave = async (img: HTMLImageElement) => {
         setSaveLoading(true);
-        if (!currImg.current || !originalImg.current || !imageRootDoc) return;
+        // if (!currImg.current || !originalImg.current || !imageRootDoc) return;
         try {
             console.log('creating another image');
-            await createNewImgDoc(currImg.current, false);
+            await createNewImgDoc(img, false);
         } catch (err) {
             console.log(err);
         }
@@ -387,7 +406,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
     const handleViewClose = () => {
         if (newCollectionRef.current) {
             newCollectionRef.current.fitContentOnce = true;
-            CollectionDockingView.AddSplit(newCollectionRef.current, OpenWhereMod.right);
+            // CollectionDockingView.AddSplit(newCollectionRef.current, OpenWhereMod.right);
         }
         MainView.Instance.setImageEditorOpen(false);
         MainView.Instance.setImageEditorSource('');
@@ -399,7 +418,20 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
             <div className="generativeFillControls">
                 <h1>AI Image Editor</h1>
                 <div style={{ display: 'flex', alignItems: 'center', gap: '1.5rem' }}>
-                    <Buttons canvasRef={canvasRef} currImg={currImg} getEdit={getEdit} loading={loading} onSave={onSave} onReset={handleReset} />
+                    <FormControlLabel
+                        control={
+                            <Checkbox
+                                checked={isNewCollection}
+                                onChange={e => {
+                                    setIsNewCollection(prev => !prev);
+                                }}
+                            />
+                        }
+                        label={'Create New Collection'}
+                        labelPlacement="end"
+                        sx={{ whiteSpace: 'nowrap' }}
+                    />
+                    <Buttons canvasRef={canvasRef} currImg={currImg} getEdit={getEdit} loading={loading} onReset={handleReset} />
                     <IconButton onClick={handleViewClose}>
                         <BsX color={activeColor} />
                     </IconButton>
@@ -491,13 +523,14 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
                             height={100}
                             src={edit}
                             onClick={async () => {
-                                // if (savedSrcs.current.has(edit)) return;
                                 const img = new Image();
                                 img.src = edit;
                                 ImageUtility.drawImgToCanvas(img, canvasRef);
                                 currImg.current = img;
                                 savedSrcs.current.add(edit);
-                                await onSave();
+                                undoStack.current = [];
+                                redoStack.current = [];
+                                await onSave(img);
                             }}
                         />
                     ))}
diff --git a/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx b/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx
index 53c6cec84..b4d56b408 100644
--- a/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx
+++ b/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx
@@ -11,14 +11,10 @@ interface ButtonContainerProps {
     currImg: React.MutableRefObject<HTMLImageElement | null>;
     getEdit: () => Promise<void>;
     loading: boolean;
-    onSave: () => Promise<void>;
     onReset: () => void;
 }
 
-const Buttons = ({ canvasRef, currImg, loading, getEdit, onSave, onReset }: ButtonContainerProps) => {
-    const handleSave = () => {
-        onSave();
-    };
+const Buttons = ({ canvasRef, currImg, loading, getEdit, onReset }: ButtonContainerProps) => {
 
     return (
         <div className="generativeFillBtnContainer">
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index 7404650d6..1be8fe6ab 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -130,6 +130,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
      * @param e pointer down event
      */
     gptSummarize = async (e: React.PointerEvent) => {
+        // move this logic to gptpopup, need to implement generate again
         GPTPopup.Instance.setVisible(true);
         this.setHighlightRange(undefined);
         GPTPopup.Instance.setMode(GPTPopupMode.SUMMARY);
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.scss b/src/client/views/pdf/GPTPopup/GPTPopup.scss
index 478b7d4ba..5d966395c 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.scss
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.scss
@@ -123,9 +123,11 @@ $highlightedText: #82e0ff;
         cursor: pointer;
 
         .img-container {
+            pointer-events: none;
             position: relative;
 
             img {
+                pointer-events: all;
                 position: relative;
             }
         }
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index fc6fc1af8..943c38d42 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -10,6 +10,11 @@ import { DocUtils, Docs } from '../../../documents/Documents';
 import { Button, IconButton, Type } from 'browndash-components';
 import { NumCast, StrCast } from '../../../../fields/Types';
 import { CgClose } from 'react-icons/cg';
+import { AnchorMenu } from '../AnchorMenu';
+import { gptAPICall, gptImageCall } from '../../../apis/gpt/GPT';
+import { RichTextField } from '../../../../fields/RichTextField';
+import { Networking } from '../../../Network';
+import { Utils } from '../../../../Utils';
 
 export enum GPTPopupMode {
     SUMMARY,
@@ -43,10 +48,17 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
     };
 
     @observable
-    public imageUrls: string[] = [];
+    public imgDesc: string = '';
     @action
-    public setImgUrls = (imgs: string[]) => {
-        this.imageUrls = imgs;
+    public setImgDesc = (text: string) => {
+        this.imgDesc = text;
+    };
+
+    @observable
+    public imgUrls: string[][] = [];
+    @action
+    public setImgUrls = (imgs: string[][]) => {
+        this.imgUrls = imgs;
     };
 
     @observable
@@ -102,6 +114,30 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
     public addDoc: (doc: Doc | Doc[], sidebarKey?: string | undefined) => boolean = () => false;
     public addToCollection: ((doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) | undefined;
 
+    generateImage = async () => {
+        if (this.imgDesc === '') return;
+        this.setImgUrls([]);
+        this.setMode(GPTPopupMode.IMAGE);
+        this.setVisible(true);
+        this.setLoading(true);
+
+        try {
+            // make this support multiple images
+            let image_urls = await gptImageCall(this.imgDesc);
+            console.log(image_urls);
+            if (image_urls && image_urls[0]) {
+                const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [image_urls[0]] });
+                const source = Utils.prepend(result.accessPaths.agnostic.client);
+                console.log('Source', source);
+                this.setImgUrls([[image_urls[0], source]]);
+            }
+        } catch (err) {
+            console.log(err);
+            return '';
+        }
+        GPTPopup.Instance.setLoading(false);
+    };
+
     /**
      * Transfers the summarization text to a sidebar annotation text document.
      */
@@ -113,8 +149,9 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
             _layout_autoHeight: true,
         });
         this.addDoc(newDoc, this.sidebarId);
-        if (this.targetAnchor) {
-            DocUtils.MakeLink(newDoc, this.targetAnchor, {
+        const anchor = AnchorMenu.Instance?.GetAnchor(undefined, false);
+        if (anchor) {
+            DocUtils.MakeLink(newDoc, anchor, {
                 link_relationship: 'GPT Summary',
             });
         }
@@ -163,17 +200,22 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
             <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
                 {this.heading('GENERATED IMAGE')}
                 <div className="image-content-wrapper">
-                    {this.imageUrls.map(rawSrc => (
+                    {this.imgUrls.map(rawSrc => (
                         <div className="img-wrapper">
                             <div className="img-container">
-                                <img key={rawSrc} src={this.getPreviewUrl(rawSrc)} width={150} height={150} alt="dalle generation" />
+                                <img key={rawSrc[0]} src={rawSrc[0]} width={150} height={150} alt="dalle generation" />
                             </div>
                             <div className="btn-container">
-                                <Button text="Save Image" onClick={() => this.transferToImage(rawSrc)} color={StrCast(Doc.UserDoc().userColor)} type={Type.TERT} />
+                                <Button text="Save Image" onClick={() => this.transferToImage(rawSrc[1])} color={StrCast(Doc.UserDoc().userColor)} type={Type.TERT} />
                             </div>
                         </div>
                     ))}
                 </div>
+                {!this.loading && (
+                    <>
+                        <IconButton tooltip="Generate Again" onClick={this.generateImage} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(Doc.UserDoc().userVariantColor)} />
+                    </>
+                )}
             </div>
         );
     };
@@ -227,43 +269,6 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
         </>
     );
 
-    editBox = () => {
-        const hr = this.highlightRange;
-        return (
-            <>
-                <div>
-                    {this.heading('TEXT EDIT SUGGESTIONS')}
-                    <div className="content-wrapper">
-                        {hr && (
-                            <div>
-                                {this.text.slice(0, hr[0])} <span className="highlighted-text">{this.text.slice(hr[0], hr[1])}</span> {this.text.slice(hr[1])}
-                            </div>
-                        )}
-                    </div>
-                </div>
-                {hr && !this.loading && (
-                    <>
-                        <div className="btns-wrapper">
-                            <>
-                                <button className="icon-btn" onPointerDown={this.callEditApi}>
-                                    <FontAwesomeIcon icon="redo-alt" size="lg" />
-                                </button>
-                                <button
-                                    className="text-btn"
-                                    onClick={e => {
-                                        this.replaceText(this.text);
-                                    }}>
-                                    Replace Text
-                                </button>
-                            </>
-                        </div>
-                        {this.aiWarning()}
-                    </>
-                )}
-            </>
-        );
-    };
-
     aiWarning = () =>
         this.done ? (
             <div className="ai-warning">
@@ -277,14 +282,14 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
     heading = (headingText: string) => (
         <div className="summary-heading">
             <label className="summary-text">{headingText}</label>
-            {this.loading ? <ReactLoading type="spin" color="#bcbcbc" width={14} height={14} /> : <IconButton color={StrCast(Doc.UserDoc().userColor)} tooltip="close" icon={<CgClose size="16px" />} onClick={() => this.setVisible(false)} />}
+            {this.loading ? <ReactLoading type="spin" color="#bcbcbc" width={14} height={14} /> : <IconButton color={StrCast(Doc.UserDoc().userVariantColor)} tooltip="close" icon={<CgClose size="16px" />} onClick={() => this.setVisible(false)} />}
         </div>
     );
 
     render() {
         return (
             <div className="summary-box" style={{ display: this.visible ? 'flex' : 'none' }}>
-                {this.mode === GPTPopupMode.SUMMARY ? this.summaryBox() : this.mode === GPTPopupMode.IMAGE ? this.imageBox() : this.editBox()}
+                {this.mode === GPTPopupMode.SUMMARY ? this.summaryBox() : this.mode === GPTPopupMode.IMAGE ? this.imageBox() : <></>}
             </div>
         );
     }
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index c9fee4813..1319a236d 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -424,10 +424,6 @@ export class PDFViewer extends React.Component<IViewerProps> {
 
         // Changing which document to add the annotation to (the currently selected PDF)
         GPTPopup.Instance.setSidebarId('data_sidebar');
-        const anchor = this._getAnchor(undefined, false);
-        if (anchor) {
-            GPTPopup.Instance.setTargetAnchor(anchor);
-        }
         GPTPopup.Instance.addDoc = this.props.sidebarAddDoc;
     };
 
-- 
cgit v1.2.3-70-g09d2


From f0cca82b98e766474f1df49bacec2258b67aae4e Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 17 Aug 2023 19:58:08 -0400
Subject: updated fieldNames for recording dictation

---
 src/client/views/nodes/AudioBox.tsx                       | 7 ++++---
 src/client/views/nodes/ScreenshotBox.tsx                  | 7 ++++---
 src/client/views/nodes/formattedText/FormattedTextBox.tsx | 6 ++----
 3 files changed, 10 insertions(+), 10 deletions(-)

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

diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 4480de938..7c409c38c 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -359,9 +359,10 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             returnFalse,
             action(() => {
                 const newDoc = DocUtils.GetNewTextDoc('', NumCast(this.rootDoc.x), NumCast(this.rootDoc.y) + NumCast(this.layoutDoc._height) + 10, NumCast(this.layoutDoc._width), 2 * NumCast(this.layoutDoc._height));
-                Doc.GetProto(newDoc).recordingSource = this.dataDoc;
-                Doc.GetProto(newDoc).recordingStart = ComputedField.MakeFunction(`self.recordingSource["${this.fieldKey}_recordingStart"]`);
-                Doc.GetProto(newDoc).mediaState = ComputedField.MakeFunction('self.recordingSource.mediaState');
+                const textField = Doc.LayoutFieldKey(newDoc);
+                Doc.GetProto(newDoc)[`${textField}_recordingSource`] = this.dataDoc;
+                Doc.GetProto(newDoc)[`${textField}_recordingStart`] = ComputedField.MakeFunction(`self.${textField}_recordingSource.${this.fieldKey}_recordingStart`);
+                Doc.GetProto(newDoc).mediaState = ComputedField.MakeFunction(`self.${textField}_recordingSource.mediaState`);
                 if (Doc.IsInMyOverlay(this.rootDoc)) {
                     newDoc.overlayX = this.rootDoc.x;
                     newDoc.overlayY = NumCast(this.rootDoc.y) + NumCast(this.rootDoc._height);
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 8e8a59fd9..0a33a336f 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -272,11 +272,12 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
     setupDictation = () => {
         if (this.dataDoc[this.fieldKey + '_dictation']) return;
         const dictationText = DocUtils.GetNewTextDoc('dictation', NumCast(this.rootDoc.x), NumCast(this.rootDoc.y) + NumCast(this.layoutDoc._height) + 10, NumCast(this.layoutDoc._width), 2 * NumCast(this.layoutDoc._height));
+        const textField = Doc.LayoutFieldKey(dictationText);
         dictationText._layout_autoHeight = false;
         const dictationTextProto = Doc.GetProto(dictationText);
-        dictationTextProto.recordingSource = this.dataDoc;
-        dictationTextProto.recordingStart = ComputedField.MakeFunction(`self.recordingSource["${this.props.fieldKey}_recordingStart"]`);
-        dictationTextProto.mediaState = ComputedField.MakeFunction('self.recordingSource.mediaState');
+        dictationTextProto[`${textField}_recordingSource`] = this.dataDoc;
+        dictationTextProto[`${textField}_recordingStart`] = ComputedField.MakeFunction(`self.${textField}_recordingSource.${this.fieldKey}_recordingStart`);
+        dictationTextProto.mediaState = ComputedField.MakeFunction(`self.${textField}_recordingSource.mediaState`);
         this.dataDoc[this.fieldKey + '_dictation'] = dictationText;
     };
     videoPanelHeight = () => (NumCast(this.dataDoc[this.fieldKey + '_nativeHeight'], this.layoutDoc[Height]()) / NumCast(this.dataDoc[this.fieldKey + '_nativeWidth'], this.layoutDoc[Width]())) * this.props.PanelWidth();
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index eea4f513e..2afbbb457 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -156,7 +156,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         return this.dataDoc?.mediaState === 'recording';
     }
     set _recording(value) {
-        !this.dataDoc.recordingSource && (this.dataDoc.mediaState = value ? 'recording' : undefined);
+        !this.dataDoc[`${this.fieldKey}_recordingSource`] && (this.dataDoc.mediaState = value ? 'recording' : undefined);
     }
     @computed get config() {
         this._keymap = buildKeymap(schema, this.props);
@@ -1262,9 +1262,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 () => this._recording,
                 () => {
                     this.stopDictation(true);
-                    if (this._recording) {
-                        this.recordDictation();
-                    }
+                    this._recording && this.recordDictation();
                 }
             );
             if (this._recording) setTimeout(this.recordDictation);
-- 
cgit v1.2.3-70-g09d2


From ad2f3695ddfb4a7bec17ddbdf6e7011862936bc4 Mon Sep 17 00:00:00 2001
From: Sophie Zhang <sophie_zhang@brown.edu>
Date: Fri, 18 Aug 2023 17:41:51 -0400
Subject: added gen_fill_children field

---
 .../views/nodes/formattedText/FormattedTextBox.tsx | 37 ----------------------
 .../views/nodes/generativeFill/GenerativeFill.tsx  | 24 ++++++++------
 src/client/views/pdf/GPTPopup/GPTPopup.tsx         |  5 +--
 3 files changed, 16 insertions(+), 50 deletions(-)

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

diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index fd30c43d7..daf8719eb 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -946,46 +946,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         console.log('Generate image from text: ', (this.dataDoc.text as RichTextField)?.Text);
         GPTPopup.Instance?.setTextAnchor(this.getAnchor(false));
         GPTPopup.Instance?.setImgTargetDoc(this.rootDoc);
-        // GPTPopup.Instance.setImgUrls([]);
-        // GPTPopup.Instance.setMode(GPTPopupMode.IMAGE);
-        // GPTPopup.Instance.setVisible(true);
         GPTPopup.Instance.addToCollection = this.props.addDocument;
-        // GPTPopup.Instance.setLoading(true);
         GPTPopup.Instance.setImgDesc((this.dataDoc.text as RichTextField)?.Text);
         GPTPopup.Instance.generateImage();
-
-        // try {
-        //     // make this support multiple images
-        //     let image_urls = await gptImageCall((this.dataDoc.text as RichTextField)?.Text);
-        //     console.log(image_urls);
-        //     if (image_urls) {
-        //         const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [image_urls[0]] });
-        //         const source = Utils.prepend(result.accessPaths.agnostic.client);
-        //         GPTPopup.Instance.setImgUrls([source]);
-
-        //         // const newDoc = Docs.Create.ImageDocument(source, {
-        //         //     x: NumCast(this.rootDoc.x) + NumCast(this.layoutDoc._width) + 10,
-        //         //     y: NumCast(this.rootDoc.y),
-        //         //     _height: 200,
-        //         //     _width: 200,
-        //         //     data_nativeWidth: result.nativeWidth,
-        //         //     data_nativeHeight: result.nativeHeight,
-        //         // });
-        //         // if (Doc.IsInMyOverlay(this.rootDoc)) {
-        //         //     newDoc.overlayX = this.rootDoc.x;
-        //         //     newDoc.overlayY = NumCast(this.rootDoc.y) + NumCast(this.rootDoc._height);
-        //         //     Doc.AddToMyOverlay(newDoc);
-        //         // } else {
-        //         //     this.props.addDocument?.(newDoc);
-        //         // }
-        //         // // Create link between prompt and image
-        //         // DocUtils.MakeLink(this.rootDoc, newDoc, { link_relationship: 'Image Prompt' });
-        //     }
-        // } catch (err) {
-        //     console.log(err);
-        //     return '';
-        // }
-        // GPTPopup.Instance.setLoading(false);
     };
 
     breakupDictation = () => {
diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.tsx b/src/client/views/nodes/generativeFill/GenerativeFill.tsx
index 92ce3a8a8..06819d08c 100644
--- a/src/client/views/nodes/generativeFill/GenerativeFill.tsx
+++ b/src/client/views/nodes/generativeFill/GenerativeFill.tsx
@@ -12,12 +12,11 @@ import { CiUndo, CiRedo } from 'react-icons/ci';
 import { MainView } from '../../MainView';
 import { Doc, DocListCast } from '../../../../fields/Doc';
 import { Networking } from '../../../Network';
-import { Utils, returnEmptyDoclist } from '../../../../Utils';
+import { Utils } from '../../../../Utils';
 import { DocUtils, Docs } from '../../../documents/Documents';
-import { DocCast, NumCast } from '../../../../fields/Types';
+import { NumCast } from '../../../../fields/Types';
 import { CollectionDockingView } from '../../collections/CollectionDockingView';
 import { OpenWhereMod } from '../DocumentView';
-import { Oval } from 'react-loader-spinner';
 import Buttons from './GenerativeFillButtons';
 import { List } from '../../../../fields/List';
 
@@ -314,12 +313,13 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
     const adjustImgPositions = () => {
         if (!parentDoc.current) return;
         const startY = NumCast(parentDoc.current.y);
-        const len = childrenDocs.current.length;
+        const children = DocListCast(parentDoc.current.gen_fill_children);
+        const len = children.length;
         let initialYPositions: number[] = [];
         for (let i = 0; i < len; i++) {
             initialYPositions.push(startY + i * offsetDistanceY);
         }
-        childrenDocs.current.forEach((doc, i) => {
+        children.forEach((doc, i) => {
             if (len % 2 === 1) {
                 doc.y = initialYPositions[i] - Math.floor(len / 2) * offsetDistanceY;
             } else {
@@ -368,9 +368,15 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
                 data_nativeWidth: result.nativeWidth,
                 data_nativeHeight: result.nativeHeight,
             });
-            newImg.gen_fill_children = new List<Doc>([]);
-            childrenDocs.current.push(newImg);
-            // DocListCast(parentDoc.current.gen_fill_children).push(newImg);
+
+            const parentList = DocListCast(parentDoc.current.gen_fill_children);
+            if (parentList.length > 0) {
+                parentList.push(newImg);
+                parentDoc.current.gen_fill_children = new List<Doc>(parentList);
+            } else {
+                parentDoc.current.gen_fill_children = new List<Doc>([newImg]);
+            }
+
             DocUtils.MakeLink(parentDoc.current, newImg, { link_relationship: `Image edit; Prompt: ${input}`, link_displayLine: true });
             adjustImgPositions();
 
@@ -408,7 +414,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
     return (
         <div className="generativeFillContainer" style={{ display: imageEditorOpen ? 'flex' : 'none' }}>
             <div className="generativeFillControls">
-                <h1>AI Image Editor</h1>
+                <h1>Image Editor</h1>
                 <div style={{ display: 'flex', alignItems: 'center', gap: '1.5rem' }}>
                     <FormControlLabel
                         control={
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index 943c38d42..43fd8df16 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -11,8 +11,7 @@ import { Button, IconButton, Type } from 'browndash-components';
 import { NumCast, StrCast } from '../../../../fields/Types';
 import { CgClose } from 'react-icons/cg';
 import { AnchorMenu } from '../AnchorMenu';
-import { gptAPICall, gptImageCall } from '../../../apis/gpt/GPT';
-import { RichTextField } from '../../../../fields/RichTextField';
+import { gptImageCall } from '../../../apis/gpt/GPT';
 import { Networking } from '../../../Network';
 import { Utils } from '../../../../Utils';
 
@@ -122,9 +121,7 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
         this.setLoading(true);
 
         try {
-            // make this support multiple images
             let image_urls = await gptImageCall(this.imgDesc);
-            console.log(image_urls);
             if (image_urls && image_urls[0]) {
                 const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [image_urls[0]] });
                 const source = Utils.prepend(result.accessPaths.agnostic.client);
-- 
cgit v1.2.3-70-g09d2


From 6913400a92d518989e831926567429c31703e4b4 Mon Sep 17 00:00:00 2001
From: Sophie Zhang <sophie_zhang@brown.edu>
Date: Fri, 18 Aug 2023 18:46:27 -0400
Subject: cleanup

---
 .../views/nodes/formattedText/FormattedTextBox.tsx   | 20 --------------------
 1 file changed, 20 deletions(-)

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

diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 2562c48e4..da277826a 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -907,7 +907,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     animateRes = (resIndex: number, newText: string) => {
         if (resIndex < newText.length) {
             const marks = this._editorView?.state.storedMarks ?? [];
-            // if (!marks) return;
             this._editorView?.dispatch(this._editorView.state.tr.setStoredMarks(marks).insertText(newText[resIndex]).setStoredMarks(marks));
             setTimeout(() => {
                 this.animateRes(resIndex + 1, newText);
@@ -915,19 +914,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         }
     };
 
-    // breakupdictiation for reference
-    // prepareForTyping
-    // stored marks - userMark and timestamp, setStoredMarks
-    // the._editorView.dispatch(state.tr.setSelection(updated).insertText('\n, to))
-
     askGPT = action(async () => {
-        // const state = this._editorView?.state;
-        // if (!state) return;
-        // const to = state.selection.to;
-        // const updated = TextSelection.create(state.doc, to, to);
-        // this._editorView?.dispatch(state.tr.setSelection(updated).insertText('\n', to));
-        // this._editorView?.dispatch(this._editorView.state.tr.setStoredMarks(marks).insertText('\nTesting').setStoredMarks(marks));
-        // console.log('After ', this._editorView?.state.storedMarks);
         try {
             let res = await gptAPICall((this.dataDoc.text as RichTextField)?.Text, GPTCallType.COMPLETION);
             if (!res) {
@@ -1243,13 +1230,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props);
                     setTimeout(this.autoLink, 20);
                 }
-                // // Accessing editor and text doc for gpt assisted text edits
-                // if (this._editorView && selected) {
-                //     console.log('Setting');
-                //     GPTPopup.Instance?.setTextAnchor(this.getAnchor(false));
-                //     // AnchorMenu.Instance?.setEditorView(this._editorView);
-                //     // AnchorMenu.Instance?.setTextDoc(this.dataDoc);
-                // }
             }),
             { fireImmediately: true }
         );
-- 
cgit v1.2.3-70-g09d2


From cf5ffaf0dcf4c2b8ad9e408026b494106401db8e Mon Sep 17 00:00:00 2001
From: Sophie Zhang <sophie_zhang@brown.edu>
Date: Fri, 25 Aug 2023 13:42:41 -0400
Subject: changed icon, added gen ai doc link

---
 src/client/util/RTFMarkup.tsx                      | 12 +++++++++-
 src/client/views/ContextMenuItem.tsx               | 28 +++++++++++++++-------
 src/client/views/MainView.tsx                      |  3 ++-
 src/client/views/nodes/DocumentView.tsx            |  4 ++--
 .../views/nodes/formattedText/FormattedTextBox.tsx |  4 ++--
 .../nodes/generativeFill/GenerativeFillButtons.tsx |  4 +++-
 6 files changed, 39 insertions(+), 16 deletions(-)

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

diff --git a/src/client/util/RTFMarkup.tsx b/src/client/util/RTFMarkup.tsx
index a0fc617ab..78069d323 100644
--- a/src/client/util/RTFMarkup.tsx
+++ b/src/client/util/RTFMarkup.tsx
@@ -3,6 +3,8 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import { MainViewModal } from '../views/MainViewModal';
 import { SettingsManager } from './SettingsManager';
+import { Doc } from '../../fields/Doc';
+import { StrCast } from '../../fields/Types';
 
 @observer
 export class RTFMarkup extends React.Component<{}> {
@@ -133,6 +135,14 @@ export class RTFMarkup extends React.Component<{}> {
     }
 
     render() {
-        return <MainViewModal contents={this.cheatSheet} isDisplayed={this.isOpen} interactive={true} closeOnExternalClick={this.close} />;
+        return (
+            <MainViewModal
+                dialogueBoxStyle={{ backgroundColor: StrCast(Doc.UserDoc().userBackgroundColor), color: StrCast(Doc.UserDoc().userColor), padding: '16px' }}
+                contents={this.cheatSheet}
+                isDisplayed={this.isOpen}
+                interactive={true}
+                closeOnExternalClick={this.close}
+            />
+        );
     }
 }
diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx
index daa2c152a..445406a89 100644
--- a/src/client/views/ContextMenuItem.tsx
+++ b/src/client/views/ContextMenuItem.tsx
@@ -11,7 +11,7 @@ export interface OriginalMenuProps {
     description: string;
     event: (stuff?: any) => void;
     undoable?: boolean;
-    icon: IconProp; //maybe should be optional (icon?)
+    icon: IconProp | JSX.Element; //maybe should be optional (icon?)
     closeMenu?: () => void;
 }
 
@@ -82,19 +82,28 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
         );
     };
 
+    isJSXElement(val: any): val is JSX.Element {
+        return React.isValidElement(val);
+    }
+
     render() {
         if ('event' in this.props) {
             return (
                 <div className={'contextMenu-item' + (this.props.selected ? ' contextMenu-itemSelected' : '')} onPointerDown={this.handleEvent}>
                     {this.props.icon ? (
-                        <span className="contextMenu-item-icon-background">
-                            <FontAwesomeIcon icon={this.props.icon} size="sm" />
-                        </span>
+                        this.isJSXElement(this.props.icon) ? (
+                            <span className="contextMenu-item-icon-background">{this.props.icon}</span>
+                        ) : (
+                            <span className="contextMenu-item-icon-background">
+                                <FontAwesomeIcon icon={this.props.icon} size="sm" />
+                            </span>
+                        )
                     ) : null}
                     <div className="contextMenu-description">{this.props.description.replace(':', '')}</div>
-                    <div className={`contextMenu-item-background`}
+                    <div
+                        className={`contextMenu-item-background`}
                         style={{
-                            background: StrCast(Doc.UserDoc().userColor)
+                            background: StrCast(Doc.UserDoc().userColor),
                         }}
                     />
                 </div>
@@ -110,7 +119,7 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
                     style={{
                         marginLeft: window.innerHeight - this._overPosX - 50 > 0 ? '90%' : '20%',
                         marginTop,
-                        background: StrCast(Doc.UserDoc().userBackgroundColor)
+                        background: StrCast(Doc.UserDoc().userBackgroundColor),
                     }}>
                     {this._items.map(prop => (
                         <ContextMenuItem {...prop} key={prop.description} closeMenu={this.props.closeMenu} />
@@ -141,9 +150,10 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
                         {this.props.description}
                         <FontAwesomeIcon icon={'angle-right'} size="lg" style={{ position: 'absolute', right: '10px' }} />
                     </div>
-                    <div className={`contextMenu-item-background`}
+                    <div
+                        className={`contextMenu-item-background`}
                         style={{
-                            background: StrCast(Doc.UserDoc().userColor)
+                            background: StrCast(Doc.UserDoc().userColor),
                         }}
                     />
                     {submenu}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index a785ffd42..8565941fd 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -447,6 +447,7 @@ export class MainView extends React.Component {
                 fa.faSortUp,
                 fa.faSortDown,
                 fa.faTable,
+                fa.faTableColumns,
                 fa.faTh,
                 fa.faThList,
                 fa.faProjectDiagram,
@@ -1019,7 +1020,7 @@ export class MainView extends React.Component {
                 <OverlayView />
                 <GPTPopup key="gptpopup" />
                 <GenerativeFill imageEditorOpen={ImageBox.imageEditorOpen} imageEditorSource={ImageBox.imageEditorSource} imageRootDoc={ImageBox.imageRootDoc} addDoc={ImageBox.addDoc} />
-               {/* <NewLightboxView key="newLightbox" PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} /> */}
+                {/* <NewLightboxView key="newLightbox" PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} /> */}
             </div>
         );
     }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 533a047b1..b72864567 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -748,7 +748,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                         event: undoBatch(action(() => (this.rootDoc._raiseWhenDragged = this.rootDoc._raiseWhenDragged === undefined ? false : undefined))),
                         icon: 'hand-point-up',
                     });
-                    !zorders && cm.addItem({ description: 'Z Order...', addDivider: true, noexpand: true, subitems: zorderItems, icon: 'compass' });
+                    !zorders && cm.addItem({ description: 'Z Order...', addDivider: true, noexpand: true, subitems: zorderItems, icon: 'layer-group' });
                 }
 
                 onClicks.push({ description: 'Enter Portal', event: this.makeIntoPortal, icon: 'window-restore' });
@@ -806,7 +806,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 constantItems.push({ description: 'Close', event: this.deleteClicked, icon: 'times' });
             }
         }
-        constantItems.push({ description: 'Show Metadata', event: () => this.props.addDocTab(this.props.Document, (OpenWhere.addRight.toString() + 'KeyValue') as OpenWhere), icon: 'layer-group' });
+        constantItems.push({ description: 'Show Metadata', event: () => this.props.addDocTab(this.props.Document, (OpenWhere.addRight.toString() + 'KeyValue') as OpenWhere), icon: 'table-columns' });
         cm.addItem({ description: 'General...', noexpand: false, subitems: constantItems, icon: 'question' });
 
         const help = cm.findByDescription('Help...');
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index da277826a..200d06a0b 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -71,6 +71,7 @@ import { SummaryView } from './SummaryView';
 import applyDevTools = require('prosemirror-dev-tools');
 import React = require('react');
 import { GPTPopup, GPTPopupMode } from '../../pdf/GPTPopup/GPTPopup';
+import { BsMarkdownFill } from 'react-icons/bs';
 const translateGoogleApi = require('translate-google-api');
 export const GoogleRef = 'googleDocId';
 type PullHandler = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => void;
@@ -899,7 +900,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 event: () => (this.layoutDoc._layout_autoHeight = !this.layoutDoc._layout_autoHeight),
                 icon: this.Document._layout_autoHeight ? 'lock' : 'unlock',
             });
-        optionItems.push({ description: `show markdown options`, event: RTFMarkup.Instance.open, icon: 'text' });
+        optionItems.push({ description: `show markdown options`, event: RTFMarkup.Instance.open, icon: <BsMarkdownFill /> });
         !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'eye' });
         this._downX = this._downY = Number.NaN;
     };
@@ -930,7 +931,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     });
 
     generateImage = async () => {
-        console.log('Generate image from text: ', (this.dataDoc.text as RichTextField)?.Text);
         GPTPopup.Instance?.setTextAnchor(this.getAnchor(false));
         GPTPopup.Instance?.setImgTargetDoc(this.rootDoc);
         GPTPopup.Instance.addToCollection = this.props.addDocument;
diff --git a/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx b/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx
index 0dfcebea3..10eca358e 100644
--- a/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx
+++ b/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx
@@ -2,7 +2,8 @@ import './GenerativeFillButtons.scss';
 import React = require('react');
 import ReactLoading from 'react-loading';
 import { activeColor } from './generativeFillUtils/generativeFillConstants';
-import { Button, Type } from 'browndash-components';
+import { Button, IconButton, Type } from 'browndash-components';
+import { AiOutlineInfo } from 'react-icons/ai';
 
 interface ButtonContainerProps {
     getEdit: () => Promise<void>;
@@ -35,6 +36,7 @@ const Buttons = ({ loading, getEdit, onReset }: ButtonContainerProps) => {
                     }}
                 />
             )}
+            <IconButton type={Type.SEC} color={activeColor} tooltip="Open Documentation" icon={<AiOutlineInfo size={'16px'} />} onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/features/generativeai/#editing', '_blank')} />
         </div>
     );
 };
-- 
cgit v1.2.3-70-g09d2

