From 53685a27139886c1df74840cc9f451c046a32de6 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 4 Oct 2019 01:50:57 -0400
Subject: got rid of MainOverlayTextBox!  fixed some things with embedding docs
 in text

---
 src/client/views/InkingControl.tsx | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

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

diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx
index a10df0e75..ee8b77050 100644
--- a/src/client/views/InkingControl.tsx
+++ b/src/client/views/InkingControl.tsx
@@ -10,7 +10,6 @@ import { InkTool } from "../../new_fields/InkField";
 import { Doc } from "../../new_fields/Doc";
 import { undoBatch, UndoManager } from "../util/UndoManager";
 import { StrCast, NumCast, Cast } from "../../new_fields/Types";
-import { MainOverlayTextBox } from "./MainOverlayTextBox";
 import { listSpec } from "../../new_fields/Schema";
 import { List } from "../../new_fields/List";
 import { Utils } from "../../Utils";
@@ -46,7 +45,7 @@ export class InkingControl extends React.Component {
     switchColor = action((color: ColorResult): void => {
         this._selectedColor = color.hex + (color.rgb.a !== undefined ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff");
         if (InkingControl.Instance.selectedTool === InkTool.None) {
-            if (MainOverlayTextBox.Instance.SetColor(color.hex)) return;
+            // if (MainOverlayTextBox.Instance.SetColor(color.hex)) return;
             let selected = SelectionManager.SelectedDocuments();
             let oldColors = selected.map(view => {
                 let targetDoc = view.props.Document.layout instanceof Doc ? view.props.Document.layout : view.props.Document.isTemplate ? view.props.Document : Doc.GetProto(view.props.Document);
-- 
cgit v1.2.3-70-g09d2


From f709ba0ba84ed057d06f9e9ab8a9315388e73c7e Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 13 Oct 2019 00:01:49 -0400
Subject: cleaning up MainView

---
 src/client/util/DictationManager.ts        |  21 ++-
 src/client/util/SharingManager.tsx         |   5 +-
 src/client/views/CollectionLinearView.scss |  74 ++++++++
 src/client/views/CollectionLinearView.tsx  | 106 ++++++++++++
 src/client/views/DictationOverlay.tsx      |  71 ++++++++
 src/client/views/GlobalKeyHandler.ts       |   8 +-
 src/client/views/InkingControl.tsx         |   5 +-
 src/client/views/Main.scss                 | 219 -----------------------
 src/client/views/MainView.scss             |  97 +++++++++++
 src/client/views/MainView.tsx              | 268 +++++------------------------
 src/client/views/nodes/DocumentView.tsx    |   9 +-
 src/client/views/nodes/ImageBox.scss       |   4 +
 12 files changed, 421 insertions(+), 466 deletions(-)
 create mode 100644 src/client/views/CollectionLinearView.scss
 create mode 100644 src/client/views/CollectionLinearView.tsx
 create mode 100644 src/client/views/DictationOverlay.tsx
 create mode 100644 src/client/views/MainView.scss

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

diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts
index 3b2307073..182cfb70a 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -14,6 +14,7 @@ import { HistogramField } from "../northstar/dash-fields/HistogramField";
 import { MainView } from "../views/MainView";
 import { Utils } from "../../Utils";
 import { RichTextField } from "../../new_fields/RichTextField";
+import { DictationOverlay } from "../views/DictationOverlay";
 
 /**
  * This namespace provides a singleton instance of a manager that
@@ -88,12 +89,11 @@ export namespace DictationManager {
 
         export const listen = async (options?: Partial<ListeningOptions>) => {
             let results: string | undefined;
-            let main = MainView.Instance;
 
             let overlay = options !== undefined && options.useOverlay;
             if (overlay) {
-                main.dictationOverlayVisible = true;
-                main.isListening = { interim: false };
+                DictationOverlay.Instance.dictationOverlayVisible = true;
+                DictationOverlay.Instance.isListening = { interim: false };
             }
 
             try {
@@ -101,21 +101,21 @@ export namespace DictationManager {
                 if (results) {
                     Utils.CopyText(results);
                     if (overlay) {
-                        main.isListening = false;
+                        DictationOverlay.Instance.isListening = false;
                         let execute = options && options.tryExecute;
-                        main.dictatedPhrase = execute ? results.toLowerCase() : results;
-                        main.dictationSuccess = execute ? await DictationManager.Commands.execute(results) : true;
+                        DictationOverlay.Instance.dictatedPhrase = execute ? results.toLowerCase() : results;
+                        DictationOverlay.Instance.dictationSuccess = execute ? await DictationManager.Commands.execute(results) : true;
                     }
                     options && options.tryExecute && await DictationManager.Commands.execute(results);
                 }
             } catch (e) {
                 if (overlay) {
-                    main.isListening = false;
-                    main.dictatedPhrase = results = `dictation error: ${"error" in e ? e.error : "unknown error"}`;
-                    main.dictationSuccess = false;
+                    DictationOverlay.Instance.isListening = false;
+                    DictationOverlay.Instance.dictatedPhrase = results = `dictation error: ${"error" in e ? e.error : "unknown error"}`;
+                    DictationOverlay.Instance.dictationSuccess = false;
                 }
             } finally {
-                overlay && main.initiateDictationFade();
+                overlay && DictationOverlay.Instance.initiateDictationFade();
             }
 
             return results;
@@ -146,7 +146,6 @@ export namespace DictationManager {
             recognizer.start();
 
             return new Promise<string>((resolve, reject) => {
-
                 recognizer.onerror = (e: SpeechRecognitionError) => {
                     if (!(indefinite && e.error === "no-speech")) {
                         recognizer.stop();
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index d37cd1b80..2082d6324 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -18,6 +18,7 @@ import { DocumentView } from "../views/nodes/DocumentView";
 import { SelectionManager } from "./SelectionManager";
 import { DocumentManager } from "./DocumentManager";
 import { CollectionView } from "../views/collections/CollectionView";
+import { DictationOverlay } from "../views/DictationOverlay";
 
 library.add(fa.faCopy);
 
@@ -71,7 +72,7 @@ export default class SharingManager extends React.Component<{}> {
         this.populateUsers().then(action(() => {
             this.targetDocView = target;
             this.targetDoc = target.props.Document;
-            MainView.Instance.hasActiveModal = true;
+            DictationOverlay.Instance.hasActiveModal = true;
             this.isOpen = true;
             if (!this.sharingDoc) {
                 this.sharingDoc = new Doc;
@@ -84,7 +85,7 @@ export default class SharingManager extends React.Component<{}> {
         this.users = [];
         setTimeout(action(() => {
             this.copied = false;
-            MainView.Instance.hasActiveModal = false;
+            DictationOverlay.Instance.hasActiveModal = false;
             this.targetDoc = undefined;
         }), 500);
     });
diff --git a/src/client/views/CollectionLinearView.scss b/src/client/views/CollectionLinearView.scss
new file mode 100644
index 000000000..30be07a9f
--- /dev/null
+++ b/src/client/views/CollectionLinearView.scss
@@ -0,0 +1,74 @@
+@import "globalCssVariables";
+@import "nodeModuleOverrides";
+
+.collectionLinearView {
+
+    >label {
+        background: $dark-color;
+        color: $light-color;
+        display: inline-block;
+        border-radius: 18px;
+        font-size: 25px;
+        width: 36px;
+        height: 36px;
+        margin-right: 10px;
+        cursor: pointer;
+        transition: transform 0.2s;
+    }
+
+    label p {
+        padding-left: 10.5px;
+    }
+
+    label:hover {
+        background: $main-accent;
+        transform: scale(1.15);
+    }
+
+    >input {
+        display: none;
+    }
+    >input:not(:checked)~.collectionLinearView-content {
+        display: none;
+    }
+
+    >input:checked~label {
+        transform: rotate(45deg);
+        transition: transform 0.5s;
+        cursor: pointer;
+    }
+
+    .collectionLinearView-content {
+        display: flex;
+        opacity: 1;
+        margin: 0;
+        padding: 0;
+        position: relative;
+        float: right;
+        .collectionFreeFormDocumentView-container {
+            position: relative;
+        } 
+        .collectionLinearView-docBtn {
+            position:relative;
+            margin-right: 10px;
+            width: 35px;
+            height: 35px;
+        }
+        .collectionLinearView-round-button {
+            width: 36px;
+            height: 36px;
+            border-radius: 18px;
+            font-size: 15px;
+        }
+        
+        .collectionLinearView-round-button:hover {
+            transform: scale(1.15);
+        }
+        
+    }
+    
+    .collectionLinearView-add-button {
+        position: relative;
+        margin-right: 10px;
+    }
+}
diff --git a/src/client/views/CollectionLinearView.tsx b/src/client/views/CollectionLinearView.tsx
new file mode 100644
index 000000000..18e3598a5
--- /dev/null
+++ b/src/client/views/CollectionLinearView.tsx
@@ -0,0 +1,106 @@
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, observable, computed } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Doc, DocListCast, Opt } from '../../new_fields/Doc';
+import { InkTool } from '../../new_fields/InkField';
+import { ObjectField } from '../../new_fields/ObjectField';
+import { ScriptField } from '../../new_fields/ScriptField';
+import { NumCast, StrCast } from '../../new_fields/Types';
+import { emptyFunction, returnEmptyString, returnOne, returnTrue, returnFalse } from '../../Utils';
+import { Docs } from '../documents/Documents';
+import { DragManager } from '../util/DragManager';
+import { Transform } from '../util/Transform';
+import { UndoManager } from '../util/UndoManager';
+import { InkingControl } from './InkingControl';
+import { DocumentView } from './nodes/DocumentView';
+import "./CollectionLinearView.scss";
+
+interface CollectionLinearViewProps {
+    Document: Doc;
+    fieldKey: string;
+}
+
+@observer
+export class CollectionLinearView extends React.Component<CollectionLinearViewProps>{
+    @observable public addMenuToggle = React.createRef<HTMLInputElement>();
+    private _dropDisposer?: DragManager.DragDropDisposer;
+
+    componentWillUnmount() {
+        this._dropDisposer && this._dropDisposer();
+    }
+
+    protected createDropTarget = (ele: HTMLLabelElement) => { //used for stacking and masonry view
+        this._dropDisposer && this._dropDisposer();
+        if (ele) {
+            this._dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
+        }
+    }
+
+    drop = action((e: Event, de: DragManager.DropEvent) => {
+        (de.data as DragManager.DocumentDragData).draggedDocuments.map(doc => {
+            if (!doc.onDragStart) {
+                let dbox = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, backgroundColor: StrCast(doc.backgroundColor), title: "Custom", icon: "bolt" });
+                dbox.dragFactory = doc;
+                dbox.removeDropProperties = doc.removeDropProperties instanceof ObjectField ? ObjectField.MakeCopy(doc.removeDropProperties) : undefined;
+                dbox.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)');
+                doc = dbox;
+            }
+            Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc);
+        });
+    });
+
+    selected = (tool: InkTool) => {
+        if (!InkingControl.Instance || InkingControl.Instance.selectedTool === InkTool.None) return { display: "none" };
+        if (InkingControl.Instance.selectedTool === tool) {
+            return { color: "#61aaa3", fontSize: "50%" };
+        }
+        return { fontSize: "50%" };
+    }
+
+    render() {
+        return <div className="collectionLinearView">
+            <input id="collectionLinearView-toggle" type="checkbox" ref={this.addMenuToggle} />
+            <label htmlFor="collectionLinearView-toggle" ref={this.createDropTarget} title="Close Menu"><p>+</p></label>
+
+            <div className="collectionLinearView-content">
+                <button key="undo" className="collectionLinearView-add-button collectionLinearView-round-button" title="Undo" style={{ opacity: UndoManager.CanUndo() ? 1 : 0.5, transition: "0.4s ease all" }} onClick={() => UndoManager.Undo()}><FontAwesomeIcon icon="undo-alt" size="sm" /></button>
+                <button key="redo" className="collectionLinearView-add-button collectionLinearView-round-button" title="Redo" style={{ opacity: UndoManager.CanRedo() ? 1 : 0.5, transition: "0.4s ease all" }} onClick={() => UndoManager.Redo()}><FontAwesomeIcon icon="redo-alt" size="sm" /></button>
+
+                {DocListCast(this.props.Document[this.props.fieldKey]).map(doc => <div className="collectionLinearView-docBtn" key={StrCast(doc.title)} >
+                    <DocumentView
+                        Document={doc}
+                        DataDoc={undefined}
+                        addDocument={undefined}
+                        addDocTab={returnFalse}
+                        pinToPres={emptyFunction}
+                        removeDocument={(doc: Doc) => Doc.RemoveDocFromList(this.props.Document, this.props.fieldKey, doc)}
+                        ruleProvider={undefined}
+                        onClick={undefined}
+                        ScreenToLocalTransform={Transform.Identity}
+                        ContentScaling={() => 35 / NumCast(doc.nativeWidth, 35)}
+                        PanelWidth={() => 35}
+                        PanelHeight={() => 35}
+                        renderDepth={0}
+                        focus={emptyFunction}
+                        backgroundColor={returnEmptyString}
+                        parentActive={returnTrue}
+                        whenActiveChanged={emptyFunction}
+                        bringToFront={emptyFunction}
+                        ContainingCollectionView={undefined}
+                        ContainingCollectionDoc={undefined}
+                        zoomToScale={emptyFunction}
+                        getScale={returnOne}>
+                    </DocumentView>
+                </div>)}
+                {/* <li key="undoTest"><button className="add-button round-button" title="Click if undo isn't working" onClick={() => UndoManager.TraceOpenBatches()}><FontAwesomeIcon icon="exclamation" size="sm" /></button></li> */}
+
+                <button className="collectionLinearView-toolbar-button collectionLinearView-round-button" title="Ink" onClick={() => InkingControl.Instance.toggleDisplay()}><FontAwesomeIcon icon="pen-nib" size="sm" /> </button>
+                <button key="pen" onClick={() => InkingControl.Instance.switchTool(InkTool.Pen)} title="Pen" style={this.selected(InkTool.Pen)}><FontAwesomeIcon icon="pen" size="lg" /></button>
+                <button key="marker" onClick={() => InkingControl.Instance.switchTool(InkTool.Highlighter)} title="Highlighter" style={this.selected(InkTool.Highlighter)}><FontAwesomeIcon icon="highlighter" size="lg" /></button>
+                <button key="eraser" onClick={() => InkingControl.Instance.switchTool(InkTool.Eraser)} title="Eraser" style={this.selected(InkTool.Eraser)}><FontAwesomeIcon icon="eraser" size="lg" /></button>
+                <InkingControl />
+            </div>
+        </div>;
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/DictationOverlay.tsx b/src/client/views/DictationOverlay.tsx
new file mode 100644
index 000000000..2accf9bfd
--- /dev/null
+++ b/src/client/views/DictationOverlay.tsx
@@ -0,0 +1,71 @@
+import { computed, observable, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import "normalize.css";
+import * as React from 'react';
+import { DictationManager } from '../util/DictationManager';
+import "./Main.scss";
+import MainViewModal from './MainViewModal';
+
+@observer
+export class DictationOverlay extends React.Component {
+    public static Instance: DictationOverlay;
+    @observable private _dictationState = DictationManager.placeholder;
+    @observable private _dictationSuccessState: boolean | undefined = undefined;
+    @observable private _dictationDisplayState = false;
+    @observable private _dictationListeningState: DictationManager.Controls.ListeningUIStatus = false;
+
+    public isPointerDown = false;
+    public overlayTimeout: NodeJS.Timeout | undefined;
+    public hasActiveModal = false;
+
+    constructor(props: any) {
+        super(props);
+        DictationOverlay.Instance = this;
+    }
+
+    public initiateDictationFade = () => {
+        let duration = DictationManager.Commands.dictationFadeDuration;
+        this.overlayTimeout = setTimeout(() => {
+            this.dictationOverlayVisible = false;
+            this.dictationSuccess = undefined;
+            DictationOverlay.Instance.hasActiveModal = false;
+            setTimeout(() => this.dictatedPhrase = DictationManager.placeholder, 500);
+        }, duration);
+    }
+    public cancelDictationFade = () => {
+        if (this.overlayTimeout) {
+            clearTimeout(this.overlayTimeout);
+            this.overlayTimeout = undefined;
+        }
+    }
+
+    @computed public get dictatedPhrase() { return this._dictationState; }
+    @computed public get dictationSuccess() { return this._dictationSuccessState; }
+    @computed public get dictationOverlayVisible() { return this._dictationDisplayState; }
+    @computed public get isListening() { return this._dictationListeningState; }
+
+    public set dictatedPhrase(value: string) { runInAction(() => this._dictationState = value); }
+    public set dictationSuccess(value: boolean | undefined) { runInAction(() => this._dictationSuccessState = value); }
+    public set dictationOverlayVisible(value: boolean) { runInAction(() => this._dictationDisplayState = value); }
+    public set isListening(value: DictationManager.Controls.ListeningUIStatus) { runInAction(() => this._dictationListeningState = value); }
+
+    render() {
+        let success = this.dictationSuccess;
+        let result = this.isListening && !this.isListening.interim ? DictationManager.placeholder : `"${this.dictatedPhrase}"`;
+        let dialogueBoxStyle = {
+            background: success === undefined ? "gainsboro" : success ? "lawngreen" : "red",
+            borderColor: this.isListening ? "red" : "black",
+            fontStyle: "italic"
+        };
+        let overlayStyle = {
+            backgroundColor: this.isListening ? "red" : "darkslategrey"
+        };
+        return (<MainViewModal
+            contents={result}
+            isDisplayed={this.dictationOverlayVisible}
+            interactive={false}
+            dialogueBoxStyle={dialogueBoxStyle}
+            overlayStyle={overlayStyle}
+        />);
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 38fce4cf7..f31a29bdc 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -122,10 +122,10 @@ export default class KeyManager {
         let preventDefault = true;
 
         switch (keyname) {
-            case "n":
-                let toggle = MainView.Instance.addMenuToggle.current!;
-                toggle.checked = !toggle.checked;
-                break;
+            // case "n":
+            //     let toggle = MainView.Instance.addMenuToggle.current!;
+            //     toggle.checked = !toggle.checked;
+            //     break;
         }
 
         return {
diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx
index ee8b77050..38734a03d 100644
--- a/src/client/views/InkingControl.tsx
+++ b/src/client/views/InkingControl.tsx
@@ -18,7 +18,7 @@ library.add(faPen, faHighlighter, faEraser, faBan);
 
 @observer
 export class InkingControl extends React.Component {
-    static Instance: InkingControl = new InkingControl({});
+    @observable static Instance: InkingControl;
     @observable private _selectedTool: InkTool = InkTool.None;
     @observable private _selectedColor: string = "rgb(244, 67, 54)";
     @observable private _selectedWidth: string = "5";
@@ -26,7 +26,7 @@ export class InkingControl extends React.Component {
 
     constructor(props: Readonly<{}>) {
         super(props);
-        InkingControl.Instance = this;
+        runInAction(() => InkingControl.Instance = this);
     }
 
     @action
@@ -45,7 +45,6 @@ export class InkingControl extends React.Component {
     switchColor = action((color: ColorResult): void => {
         this._selectedColor = color.hex + (color.rgb.a !== undefined ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff");
         if (InkingControl.Instance.selectedTool === InkTool.None) {
-            // if (MainOverlayTextBox.Instance.SetColor(color.hex)) return;
             let selected = SelectionManager.SelectedDocuments();
             let oldColors = selected.map(view => {
                 let targetDoc = view.props.Document.layout instanceof Doc ? view.props.Document.layout : view.props.Document.isTemplate ? view.props.Document : Doc.GetProto(view.props.Document);
diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index 55195b616..0335e12a5 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -21,7 +21,6 @@ div {
 }
 
 
-
 .jsx-parser {
     width: 100%;
     height: 100%;
@@ -64,228 +63,10 @@ button:hover {
     cursor: pointer;
 }
 
-.clear-db-button {
-    position: absolute;
-    right: 45%;
-    bottom: 3%;
-    font-size: 50%;
-}
-
-.round-button {
-    width: 36px;
-    height: 36px;
-    border-radius: 18px;
-    font-size: 15px;
-}
-
-.round-button:hover {
-    transform: scale(1.15);
-}
-
-.add-button {
-    position: relative;
-    margin-right: 10px;
-}
-
-.main-undoButtons {
-    position: absolute;
-    width: 150px;
-    right: 0px;
-}
-
-//toolbar stuff
-#toolbar {
-    position: absolute;
-    right: 8px;
-    top: 5px;
-
-    .toolbar-button {
-        display: block;
-        margin-bottom: 10px;
-    }
-}
-
-.toolbar-color-picker {
-    background-color: $light-color;
-    border-radius: 5px;
-    padding: 12px;
-    position: absolute;
-    bottom: 36px;
-    left: -3px;
-    box-shadow: $intermediate-color 0.2vw 0.2vw 0.8vw;
-}
-
-.toolbar-color-button {
-    border-radius: 11px;
-    width: 22px;
-    height: 22px;
-    cursor: pointer;
-    text-align: center; //   span {
-    //     color: $light-color;
-    //     font-size: 8px;
-    //     user-select: none;
-    //   }
-    margin-top: -2.55px;
-    margin-left: -2.55px;
-}
-
-// add nodes menu. Note that the + button is actually an input label, not an actual button.
-#add-nodes-menu {
-    position: absolute;
-    bottom: 22px;
-    left: 250px;
-
-    >label {
-        background: $dark-color;
-        color: $light-color;
-        display: inline-block;
-        border-radius: 18px;
-        font-size: 25px;
-        width: 36px;
-        height: 36px;
-        margin-right: 10px;
-        cursor: pointer;
-        transition: transform 0.2s;
-    }
-
-    label p {
-        padding-left: 10.5px;
-    }
-
-    label:hover {
-        background: $main-accent;
-        transform: scale(1.15);
-    }
-
-    >input {
-        display: none;
-    }
-
-    >input:not(:checked)~#add-options-content {
-        display: none;
-    }
-
-    >input:checked~label {
-        transform: rotate(45deg);
-        transition: transform 0.5s;
-        cursor: pointer;
-    }
-}
-
 #root {
     overflow: visible;
 }
 
-#main-div {
-    width: 100%;
-    height: 100%;
-    position: absolute;
-    top: 0;
-    left: 0;
-    overflow: auto;
-    z-index: 1;
-}
-.mainContent {
-    width:100%;
-    height:100%;
-    position:absolute;
-}
-.mainView-flyoutContainer{
-    display:flex;
-    flex-direction: column;
-    position: absolute;
-    width:100%;
-    height:100%;
-    border: black 1px solid;
-    .documentView-node-topmost {
-        background: lightgrey;
-    }
-}
-#mainContent-div {
-    width: 100%;
-    height: 100%;
-    position: absolute;
-    top: 0;
-    left: 0;
-    overflow: hidden;
-}
-
-#add-options-content {
-    display: flex;
-    opacity: 1;
-    margin: 0;
-    padding: 0;
-    position: relative;
-    float: right;
-    .mainView-docBtn {
-        position:relative;
-        margin-right: 10px;
-        width: 35px;
-        height: 35px;
-    }
-    .collectionFreeFormDocumentView-container {
-        position: relative;
-    }
-}
-
-ul#add-options-list {
-    list-style: none;
-    padding: 5 0 0 0;
-
-    >li {
-        display: inline-block;
-        padding: 0;
-    }
-}
-.mainView-logout {
-    position: absolute;
-    right: 0;
-    bottom: 0;
-    font-size: 8px;
-}
-
-.mainView-libraryFlyout {
-    height: 100%;
-    position: absolute;
-    display: flex;
-    flex-direction: column;
-}
-
-.expandFlyoutButton {
-    position: absolute;
-    top: 30px;
-    right: 30px;
-    cursor: pointer;
-}
-
-.mainView-libraryHandle {
-    width: 20px;
-    height: 40px;
-    top: 50%;
-    border: 1px solid black;
-    border-radius: 5px;
-    position: absolute;
-    z-index: 1;
-}
-
 .svg-inline--fa {
     vertical-align: unset;
-}
-
-.mainView-workspace {
-    height: 200px;
-    position: relative;
-    display: flex;
-}
-
-.mainView-library {
-    height: 75%;
-    position: relative;
-    display: flex;
-}
-
-.mainView-recentlyClosed {
-    height: 25%;
-    position: relative;
-    display: flex;
 }
\ No newline at end of file
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
new file mode 100644
index 000000000..e61494e71
--- /dev/null
+++ b/src/client/views/MainView.scss
@@ -0,0 +1,97 @@
+@import "globalCssVariables";
+@import "nodeModuleOverrides";
+
+
+.mainView-tabButtons {
+    position: relative;
+    width:100%;
+}
+// add nodes menu. Note that the + button is actually an input label, not an actual button.
+.mainView-docButtons {
+    position: absolute;
+    bottom: 20px;
+    left: 250px;
+}
+
+.mainView-container {
+    width: 100%;
+    height: 100%;
+    position: absolute;
+    top: 0;
+    left: 0;
+    overflow: auto;
+    z-index: 1;
+}
+.mainView-mainContent {
+    width:100%;
+    height:100%;
+    position:absolute;
+}
+.mainView-flyoutContainer{
+    display:flex;
+    flex-direction: column;
+    position: absolute;
+    width:100%;
+    height:100%;
+    border: black 1px solid;
+    .documentView-node-topmost {
+        background: lightgrey;
+    }
+}
+.mainView-mainDiv {
+    width: 100%;
+    height: 100%;
+    position: absolute;
+    top: 0;
+    left: 0;
+    overflow: hidden;
+}
+
+.mainView-logout {
+    position: absolute;
+    right: 0;
+    bottom: 0;
+    font-size: 8px;
+}
+
+.mainView-libraryFlyout {
+    height: 100%;
+    position: absolute;
+    display: flex;
+    flex-direction: column;
+}
+
+.mainView-expandFlyoutButton {
+    position: absolute;
+    top: 30px;
+    right: 30px;
+    cursor: pointer;
+}
+
+.mainView-libraryHandle {
+    width: 20px;
+    height: 40px;
+    top: 50%;
+    border: 1px solid black;
+    border-radius: 5px;
+    position: absolute;
+    z-index: 1;
+}
+
+.mainView-workspace {
+    height: 200px;
+    position: relative;
+    display: flex;
+}
+
+.mainView-library {
+    height: 75%;
+    position: relative;
+    display: flex;
+}
+
+.mainView-recentlyClosed {
+    height: 25%;
+    position: relative;
+    display: flex;
+}
\ No newline at end of file
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index aac4af9f6..4367785b6 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -8,32 +8,26 @@ import * as React from 'react';
 import Measure from 'react-measure';
 import { Doc, DocListCast, Field, FieldResult, Opt } from '../../new_fields/Doc';
 import { Id } from '../../new_fields/FieldSymbols';
-import { InkTool } from '../../new_fields/InkField';
 import { List } from '../../new_fields/List';
-import { ObjectField } from '../../new_fields/ObjectField';
 import { listSpec } from '../../new_fields/Schema';
-import { ScriptField } from '../../new_fields/ScriptField';
-import { BoolCast, Cast, FieldValue, NumCast, PromiseValue, StrCast } from '../../new_fields/Types';
+import { Cast, FieldValue, StrCast } from '../../new_fields/Types';
 import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
 import { RouteStore } from '../../server/RouteStore';
 import { emptyFunction, returnEmptyString, returnFalse, returnOne, returnTrue, Utils } from '../../Utils';
 import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager';
 import { DocServer } from '../DocServer';
 import { Docs, DocumentOptions } from '../documents/Documents';
-import { DictationManager } from '../util/DictationManager';
-import { DragManager } from '../util/DragManager';
 import { HistoryUtil } from '../util/History';
 import SharingManager from '../util/SharingManager';
 import { Transform } from '../util/Transform';
-import { UndoManager } from '../util/UndoManager';
+import { CollectionLinearView } from './CollectionLinearView';
 import { CollectionBaseView, CollectionViewType } from './collections/CollectionBaseView';
 import { CollectionDockingView } from './collections/CollectionDockingView';
 import { ContextMenu } from './ContextMenu';
+import { DictationOverlay } from './DictationOverlay';
 import { DocumentDecorations } from './DocumentDecorations';
 import KeyManager from './GlobalKeyHandler';
-import { InkingControl } from './InkingControl';
-import "./Main.scss";
-import MainViewModal from './MainViewModal';
+import "./MainView.scss";
 import { MainViewNotifs } from './MainViewNotifs';
 import { DocumentView } from './nodes/DocumentView';
 import { OverlayView } from './OverlayView';
@@ -46,59 +40,17 @@ export class MainView extends React.Component {
     private _buttonBarHeight = 75;
     private _flyoutSizeOnDown = 0;
     private _urlState: HistoryUtil.DocUrl;
-    private _dropDisposer?: DragManager.DragDropDisposer;
-
-    @observable private _dictationState = DictationManager.placeholder;
-    @observable private _dictationSuccessState: boolean | undefined = undefined;
-    @observable private _dictationDisplayState = false;
-    @observable private _dictationListeningState: DictationManager.Controls.ListeningUIStatus = false;
 
     @observable private _panelWidth: number = 0;
     @observable private _panelHeight: number = 0;
     @observable private _flyoutTranslate: boolean = true;
-    @observable public addMenuToggle = React.createRef<HTMLInputElement>();
     @observable public flyoutWidth: number = 250;
 
-    public hasActiveModal = false;
-    public isPointerDown = false;
-    public overlayTimeout: NodeJS.Timeout | undefined;
-
-    private set mainContainer(doc: Opt<Doc>) {
-        if (doc) {
-            !("presentationView" in doc) && (doc.presentationView = new List<Doc>([Docs.Create.TreeDocument([], { title: "Presentation" })]));
-            this.userDoc ? (this.userDoc.activeWorkspace = doc) : (CurrentUserUtils.GuestWorkspace = doc);
-        }
-    }
-
-    @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeWorkspace, Doc)) : CurrentUserUtils.GuestWorkspace; }
     @computed private get userDoc() { return CurrentUserUtils.UserDocument; }
+    @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeWorkspace, Doc)) : CurrentUserUtils.GuestWorkspace; }
     @computed public get mainFreeform(): Opt<Doc> { return (docs => (docs && docs.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); }
 
-    public initiateDictationFade = () => {
-        let duration = DictationManager.Commands.dictationFadeDuration;
-        this.overlayTimeout = setTimeout(() => {
-            this.dictationOverlayVisible = false;
-            this.dictationSuccess = undefined;
-            this.hasActiveModal = false;
-            setTimeout(() => this.dictatedPhrase = DictationManager.placeholder, 500);
-        }, duration);
-    }
-    public cancelDictationFade = () => {
-        if (this.overlayTimeout) {
-            clearTimeout(this.overlayTimeout);
-            this.overlayTimeout = undefined;
-        }
-    }
-
-    @computed public get dictatedPhrase() { return this._dictationState; }
-    @computed public get dictationSuccess() { return this._dictationSuccessState; }
-    @computed public get dictationOverlayVisible() { return this._dictationDisplayState; }
-    @computed public get isListening() { return this._dictationListeningState; }
-
-    public set dictatedPhrase(value: string) { runInAction(() => this._dictationState = value); }
-    public set dictationSuccess(value: boolean | undefined) { runInAction(() => this._dictationSuccessState = value); }
-    public set dictationOverlayVisible(value: boolean) { runInAction(() => this._dictationDisplayState = value); }
-    public set isListening(value: DictationManager.Controls.ListeningUIStatus) { runInAction(() => this._dictationListeningState = value); }
+    public isPointerDown = false;
 
     componentWillMount() {
         var tag = document.createElement('script');
@@ -112,10 +64,8 @@ export class MainView extends React.Component {
 
     componentWillUnMount() {
         window.removeEventListener("keydown", KeyManager.Instance.handle);
-        //close presentation 
         window.removeEventListener("pointerdown", this.globalPointerDown);
         window.removeEventListener("pointerup", this.globalPointerUp);
-        this._dropDisposer && this._dropDisposer();
     }
 
     constructor(props: Readonly<{}>) {
@@ -183,7 +133,6 @@ export class MainView extends React.Component {
     globalPointerUp = () => this.isPointerDown = false;
 
     initEventListeners = () => {
-        // window.addEventListener("pointermove", (e) => this.reportLocation(e))
         window.addEventListener("drop", (e) => e.preventDefault(), false); // drop event handler
         window.addEventListener("dragover", (e) => e.preventDefault(), false); // drag event handler
         // click interactions for the context menu
@@ -202,24 +151,16 @@ export class MainView extends React.Component {
             );
         } else {
             if (received && this._urlState.sharing) {
-                reaction(
-                    () => {
-                        let docking = CollectionDockingView.Instance;
-                        return docking && docking.initialized;
-                    },
-                    initialized => {
-                        if (initialized && received) {
-                            DocServer.GetRefField(received).then(field => {
-                                if (field instanceof Doc && field.viewType !== CollectionViewType.Docking) {
-                                    CollectionDockingView.AddRightSplit(field, undefined);
-                                }
-                            });
+                reaction(() => CollectionDockingView.Instance && CollectionDockingView.Instance.initialized,
+                    initialized => initialized && received && DocServer.GetRefField(received).then(field => {
+                        if (field instanceof Doc && field.viewType !== CollectionViewType.Docking) {
+                            CollectionDockingView.AddRightSplit(field, undefined);
                         }
-                    },
+                    }),
                 );
             }
-            let doc: Opt<Doc>;
-            if (this.userDoc && (doc = await Cast(this.userDoc.activeWorkspace, Doc))) {
+            let doc = this.userDoc && await Cast(this.userDoc.activeWorkspace, Doc);
+            if (doc) {
                 this.openWorkspace(doc);
             } else {
                 this.createNewWorkspace();
@@ -250,17 +191,17 @@ export class MainView extends React.Component {
             mainDoc.title = `Workspace ${DocListCast(workspaces.data).length}`;
         }
         // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
-        setTimeout(() => {
-            this.openWorkspace(mainDoc);
-            // let pendingDocument = Docs.StackingDocument([], { title: "New Mobile Uploads" });
-            // mainDoc.optionalRightCollection = pendingDocument;
-        }, 0);
+        setTimeout(() => this.openWorkspace(mainDoc), 0);
     }
 
     @action
     openWorkspace = async (doc: Doc, fromHistory = false) => {
         CurrentUserUtils.MainDocId = doc[Id];
-        this.mainContainer = doc;
+
+        if (doc) {  // this has the side-effect of setting the main container since we're assigning the active/guest workspace
+            !("presentationView" in doc) && (doc.presentationView = new List<Doc>([Docs.Create.TreeDocument([], { title: "Presentation" })]));
+            this.userDoc ? (this.userDoc.activeWorkspace = doc) : (CurrentUserUtils.GuestWorkspace = doc);
+        }
         let state = this._urlState;
         if (state.sharing === true && !this.userDoc) {
             DocServer.Control.makeReadOnly();
@@ -280,7 +221,7 @@ export class MainView extends React.Component {
                 }
                 CollectionBaseView.SetSafeMode(true);
             } else if (state.nro || state.nro === null || state.readonly === false) {
-            } else if (BoolCast(doc.readOnly)) {
+            } else if (doc.readOnly) {
                 DocServer.Control.makeReadOnly();
             } else {
                 DocServer.Control.makeEditable();
@@ -294,19 +235,6 @@ export class MainView extends React.Component {
         return true;
     }
 
-    drop = action((e: Event, de: DragManager.DropEvent) => {
-        (de.data as DragManager.DocumentDragData).draggedDocuments.map(doc => {
-            if (!doc.onDragStart) {
-                let dbox = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, backgroundColor: StrCast(doc.backgroundColor), title: "Custom", icon: "bolt" });
-                dbox.dragFactory = doc;
-                dbox.removeDropProperties = doc.removeDropProperties instanceof ObjectField ? ObjectField.MakeCopy(doc.removeDropProperties) : undefined;
-                dbox.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)');
-                doc = dbox;
-            }
-            Doc.AddDocToList(CurrentUserUtils.UserDocument, "docButtons", doc);
-        });
-    });
-
     onDrop = (e: React.DragEvent<HTMLDivElement>) => {
         e.preventDefault();
         e.stopPropagation();
@@ -323,14 +251,12 @@ export class MainView extends React.Component {
     getContentsHeight = () => this._panelHeight - this._buttonBarHeight;
 
     @computed get dockingContent() {
+        const mainContainer = this.mainContainer;
         return <Measure offset onResize={this.onResize}>
             {({ measureRef }) =>
-                <div ref={measureRef} id="mainContent-div" style={{
-                    width: `calc(100% - ${this._flyoutTranslate || 0}px`,
-                    transform: `translate(${this._flyoutTranslate || 0}px, 0px)`
-                }} onDrop={this.onDrop}>
-                    {!this.mainContainer ? (null) :
-                        <DocumentView Document={this.mainContainer}
+                <div ref={measureRef} className="mainView-mainDiv" onDrop={this.onDrop}>
+                    {!mainContainer ? (null) :
+                        <DocumentView Document={mainContainer}
                             DataDoc={undefined}
                             addDocument={undefined}
                             addDocTab={this.addDocTabFunc}
@@ -391,8 +317,7 @@ export class MainView extends React.Component {
     @action
     onPointerUp = (e: PointerEvent) => {
         if (Math.abs(e.clientX - this._flyoutSizeOnDown) < 4) {
-            if (this.flyoutWidth < 5) this.flyoutWidth = 250;
-            else this.flyoutWidth = 0;
+            this.flyoutWidth = this.flyoutWidth < 5 ? 250 : 0;
         }
         document.removeEventListener("pointermove", this.onPointerMove);
         document.removeEventListener("pointerup", this.onPointerUp);
@@ -418,7 +343,7 @@ export class MainView extends React.Component {
         let libraryButtonDoc = Cast(CurrentUserUtils.UserDocument.libraryButtons, Doc) as Doc;
         libraryButtonDoc.columnWidth = this.flyoutWidth / 3 - 30;
         return <div className="mainView-flyoutContainer">
-            <div style={{ position: "relative", height: `${this._buttonBarHeight}px`, width: "100%" }}>
+            <div className="mainView-tabButtons" style={{ height: `${this._buttonBarHeight}px` }}>
                 <DocumentView
                     Document={libraryButtonDoc}
                     DataDoc={undefined}
@@ -479,8 +404,8 @@ export class MainView extends React.Component {
     get mainContent() {
         const sidebar = this.userDoc && this.userDoc.sidebarContainer;
         return !this.userDoc || !(sidebar instanceof Doc) ? (null) : (
-            <div className="mainContent" >
-                <div onPointerLeave={this.pointerLeaveDragger}>
+            <div className="mainView-mainContent" >
+                <div className="mainView-flyoutContainer" onPointerLeave={this.pointerLeaveDragger}>
                     <div className="mainView-libraryHandle"
                         style={{ cursor: "ew-resize", left: `${(this.flyoutWidth * (this._flyoutTranslate ? 1 : 0)) - 10}px`, backgroundColor: `${StrCast(sidebar.backgroundColor, "lightGray")}` }}
                         onPointerDown={this.onPointerDown} onPointerOver={this.pointerOverDragger}>
@@ -508,7 +433,7 @@ export class MainView extends React.Component {
     }
 
     @computed get expandButton() {
-        return !this._flyoutTranslate ? (<div className="expandFlyoutButton" title="Re-attach sidebar" onPointerDown={() => {
+        return !this._flyoutTranslate ? (<div className="mainView-expandFlyoutButton" title="Re-attach sidebar" onPointerDown={() => {
             runInAction(() => {
                 this.flyoutWidth = 250;
                 this._flyoutTranslate = true;
@@ -516,126 +441,25 @@ export class MainView extends React.Component {
         }}><FontAwesomeIcon icon="chevron-right" color="grey" size="lg" /></div>) : (null);
     }
 
-    selected = (tool: InkTool) => {
-        if (!InkingControl.Instance || InkingControl.Instance.selectedTool === InkTool.None) return { display: "none" };
-        if (InkingControl.Instance.selectedTool === tool) {
-            return { color: "#61aaa3", fontSize: "50%" };
-        }
-        return { fontSize: "50%" };
-    }
-
-    setWriteMode = (mode: DocServer.WriteMode) => {
-        console.log(DocServer.WriteMode[mode]);
-        const mode1 = mode;
-        const mode2 = mode === DocServer.WriteMode.Default ? mode : DocServer.WriteMode.Playground;
-        DocServer.setFieldWriteMode("x", mode1);
-        DocServer.setFieldWriteMode("y", mode1);
-        DocServer.setFieldWriteMode("width", mode1);
-        DocServer.setFieldWriteMode("height", mode1);
-
-        DocServer.setFieldWriteMode("panX", mode2);
-        DocServer.setFieldWriteMode("panY", mode2);
-        DocServer.setFieldWriteMode("scale", mode2);
-        DocServer.setFieldWriteMode("viewType", mode2);
-    }
-
-    protected createDropTarget = (ele: HTMLLabelElement) => { //used for stacking and masonry view
-        this._dropDisposer && this._dropDisposer();
-        if (ele) {
-            this._dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
-        }
-    }
-
-    /* for the expandable add nodes menu. Not included with the miscbuttons because once it expands it expands the whole div with it, making canvas interactions limited. */
-    nodesMenu() {
-        // let youtubeurl = "https://www.youtube.com/embed/TqcApsGRzWw";
-        // let addYoutubeSearcher = action(() => Docs.Create.YoutubeDocument(youtubeurl, { width: 600, height: 600, title: "youtube search" }));
-
-        // let googlePhotosSearch = () => GooglePhotosClientUtils.CollectionFromSearch(Docs.Create.MasonryDocument, { included: [GooglePhotosClientUtils.ContentCategories.LANDSCAPES] });
-
-        return < div id="add-nodes-menu" style={{ left: (this._flyoutTranslate ? this.flyoutWidth : 0) + 20, bottom: 20 }} >
-
+    @computed get docButtons() {
+        return <div className="mainView-docButtons" style={{ left: (this._flyoutTranslate ? this.flyoutWidth : 0) + 20 }} >
             <MainViewNotifs />
-            <input type="checkbox" id="add-menu-toggle" ref={this.addMenuToggle} />
-            <label htmlFor="add-menu-toggle" ref={this.createDropTarget} title="Close Menu"><p>+</p></label>
-
-            <div id="add-options-content">
-                <button key="undo" className="add-button round-button" title="Undo" style={{ opacity: UndoManager.CanUndo() ? 1 : 0.5, transition: "0.4s ease all" }} onClick={() => UndoManager.Undo()}><FontAwesomeIcon icon="undo-alt" size="sm" /></button>
-                <button key="redo" className="add-button round-button" title="Redo" style={{ opacity: UndoManager.CanRedo() ? 1 : 0.5, transition: "0.4s ease all" }} onClick={() => UndoManager.Redo()}><FontAwesomeIcon icon="redo-alt" size="sm" /></button>
-
-                {DocListCast(CurrentUserUtils.UserDocument.docButtons).map(doc => <div className="mainView-docBtn" key={StrCast(doc.title)} >
-                    <DocumentView
-                        Document={doc}
-                        DataDoc={undefined}
-                        addDocument={undefined}
-                        addDocTab={this.addDocTabFunc}
-                        pinToPres={emptyFunction}
-                        removeDocument={(doc: Doc) => Doc.RemoveDocFromList(CurrentUserUtils.UserDocument, "docButtons", doc)}
-                        ruleProvider={undefined}
-                        onClick={undefined}
-                        ScreenToLocalTransform={Transform.Identity}
-                        ContentScaling={() => 35 / NumCast(doc.nativeWidth, 35)}
-                        PanelWidth={() => 35}
-                        PanelHeight={() => 35}
-                        renderDepth={0}
-                        focus={emptyFunction}
-                        backgroundColor={returnEmptyString}
-                        parentActive={returnTrue}
-                        whenActiveChanged={emptyFunction}
-                        bringToFront={emptyFunction}
-                        ContainingCollectionView={undefined}
-                        ContainingCollectionDoc={undefined}
-                        zoomToScale={emptyFunction}
-                        getScale={returnOne}>
-                    </DocumentView>
-                </div>)}
-                {/* <li key="undoTest"><button className="add-button round-button" title="Click if undo isn't working" onClick={() => UndoManager.TraceOpenBatches()}><FontAwesomeIcon icon="exclamation" size="sm" /></button></li> */}
-
-                <button className="toolbar-button round-button" title="Ink" onClick={() => InkingControl.Instance.toggleDisplay()}><FontAwesomeIcon icon="pen-nib" size="sm" /> </button>
-                <button key="pen" onClick={() => InkingControl.Instance.switchTool(InkTool.Pen)} title="Pen" style={this.selected(InkTool.Pen)}><FontAwesomeIcon icon="pen" size="lg" /></button>
-                <button key="marker" onClick={() => InkingControl.Instance.switchTool(InkTool.Highlighter)} title="Highlighter" style={this.selected(InkTool.Highlighter)}><FontAwesomeIcon icon="highlighter" size="lg" /></button>
-                <button key="eraser" onClick={() => InkingControl.Instance.switchTool(InkTool.Eraser)} title="Eraser" style={this.selected(InkTool.Eraser)}><FontAwesomeIcon icon="eraser" size="lg" /></button>
-                <InkingControl key="inkControls" />
-            </div>
-        </div >;
-    }
-
-    @computed private get dictationOverlay() {
-        let success = this.dictationSuccess;
-        let result = this.isListening && !this.isListening.interim ? DictationManager.placeholder : `"${this.dictatedPhrase}"`;
-        let dialogueBoxStyle = {
-            background: success === undefined ? "gainsboro" : success ? "lawngreen" : "red",
-            borderColor: this.isListening ? "red" : "black",
-            fontStyle: "italic"
-        };
-        let overlayStyle = {
-            backgroundColor: this.isListening ? "red" : "darkslategrey"
-        };
-        return (
-            <MainViewModal
-                contents={result}
-                isDisplayed={this.dictationOverlayVisible}
-                interactive={false}
-                dialogueBoxStyle={dialogueBoxStyle}
-                overlayStyle={overlayStyle}
-            />
-        );
+            <CollectionLinearView Document={CurrentUserUtils.UserDocument} fieldKey={"docButtons"} />
+        </div>;
     }
 
     render() {
-        return (
-            <div id="main-div">
-                {this.dictationOverlay}
-                <SharingManager />
-                <GoogleAuthenticationManager />
-                <DocumentDecorations />
-                {this.mainContent}
-                <PreviewCursor />
-                <ContextMenu />
-                {this.nodesMenu()}
-                <PDFMenu />
-                <OverlayView />
-            </div >
-        );
+        return (<div className="mainView-container">
+            <DictationOverlay />
+            <SharingManager />
+            <GoogleAuthenticationManager />
+            <DocumentDecorations />
+            {this.mainContent}
+            <PreviewCursor />
+            <ContextMenu />
+            {this.docButtons}
+            <PDFMenu />
+            <OverlayView />
+        </div >);
     }
-}
+}
\ No newline at end of file
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 98ae7442f..7334de92c 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -26,7 +26,6 @@ import { ContextMenu } from "../ContextMenu";
 import { ContextMenuProps } from '../ContextMenuItem';
 import { DocComponent } from "../DocComponent";
 import { EditableView } from '../EditableView';
-import { MainView } from '../MainView';
 import { OverlayView } from '../OverlayView';
 import { ScriptBox } from '../ScriptBox';
 import { ScriptingRepl } from '../ScriptingRepl';
@@ -39,6 +38,7 @@ import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
 import { ImageField } from '../../../new_fields/URLField';
 import SharingManager from '../../util/SharingManager';
 import { Scripting } from '../../util/Scripting';
+import { DictationOverlay } from '../DictationOverlay';
 
 library.add(fa.faEdit);
 library.add(fa.faTrash);
@@ -423,10 +423,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         Doc.GetProto(this.props.Document).transcript = await DictationManager.Controls.listen({
             continuous: { indefinite: true },
             interimHandler: (results: string) => {
-                let main = MainView.Instance;
-                main.dictationSuccess = true;
-                main.dictatedPhrase = results;
-                main.isListening = { interim: true };
+                DictationOverlay.Instance.dictationSuccess = true;
+                DictationOverlay.Instance.dictatedPhrase = results;
+                DictationOverlay.Instance.isListening = { interim: true };
             }
         });
     }
diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss
index 97d858f58..2b81c16c0 100644
--- a/src/client/views/nodes/ImageBox.scss
+++ b/src/client/views/nodes/ImageBox.scss
@@ -9,6 +9,10 @@
     pointer-events: none;
 }
 
+.imageBox-container {
+    border-radius: inherit;
+}
+
 .imageBox-cont-interactive {
     pointer-events: all;
 }
-- 
cgit v1.2.3-70-g09d2

