From 1eb2c362b020b3cbe446bbc1585108129fda6977 Mon Sep 17 00:00:00 2001
From: Michael Foiani <sotech117@michaels-mbp-3.devices.brown.edu>
Date: Wed, 4 May 2022 01:11:54 -0400
Subject: Get storing pres data working, but is choppy due to mobx usage.

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

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

diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 8560ccb29..517fe097c 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -8,6 +8,7 @@ import { AssignAllExtensions } from "../../extensions/General/Extensions";
 import { Docs } from "../documents/Documents";
 import { CurrentUserUtils } from "../util/CurrentUserUtils";
 import { LinkManager } from "../util/LinkManager";
+import { RecordingApi } from "../util/RecordingApi";
 import { CollectionView } from "./collections/CollectionView";
 import { MainView } from "./MainView";
 
@@ -36,5 +37,6 @@ AssignAllExtensions();
     const expires = "expires=" + d.toUTCString();
     document.cookie = `loadtime=${loading};${expires};path=/`;
     new LinkManager();
+    new RecordingApi;
     ReactDOM.render(<MainView />, document.getElementById('root'));
 })();
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 0e6f5e580b7e64ecc2002534e1a14ccee36cd28e Mon Sep 17 00:00:00 2001
From: Jenny Yu <jennyyu212@outlook.com>
Date: Wed, 8 Jun 2022 22:55:16 -0700
Subject: feat: basic dashboard view

---
 src/client/views/DashboardView.scss             |  39 +++
 src/client/views/DashboardView.tsx              |  50 ++++
 src/client/views/Main.tsx                       |   1 +
 src/client/views/MainView.tsx                   |  20 +-
 src/client/views/global/globalCssVariables.scss |   2 +-
 src/client/views/topbar/TopBar.scss             | 366 ++++++++++++------------
 src/client/views/topbar/TopBar.tsx              |  85 ++++--
 7 files changed, 341 insertions(+), 222 deletions(-)
 create mode 100644 src/client/views/DashboardView.scss
 create mode 100644 src/client/views/DashboardView.tsx

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

diff --git a/src/client/views/DashboardView.scss b/src/client/views/DashboardView.scss
new file mode 100644
index 000000000..1896d7bfb
--- /dev/null
+++ b/src/client/views/DashboardView.scss
@@ -0,0 +1,39 @@
+.dashboard-view {
+      padding: 50px;
+      display: flex;
+      flex-direction: row;
+
+      .left-menu {
+            display: flex;
+            flex-direction: column;
+            width: 300px;
+      }
+
+      .all-dashboards {
+            display: flex;
+            flex-direction: row;
+            flex-wrap: wrap;
+            overflow-y: scroll;
+      }
+}
+
+
+
+.dashboard-container {
+      border-radius: 10px;
+      width: 250px;
+      border: solid .5px grey;
+      display: flex;
+      flex-direction: column;
+      margin: 0 30px 30px 30px;
+      overflow: hidden;
+
+      &:hover {
+            border: solid 1.5px grey;  
+      }
+
+      .title {
+            margin: 10px;
+            font-weight: 500;
+      }
+}
\ No newline at end of file
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
new file mode 100644
index 000000000..5fd9b550d
--- /dev/null
+++ b/src/client/views/DashboardView.tsx
@@ -0,0 +1,50 @@
+import { observable } from "mobx";
+import { observer } from "mobx-react";
+import * as React from 'react';
+import { Doc, DocListCast } from "../../fields/Doc";
+import { Id } from "../../fields/FieldSymbols";
+import { Cast, ImageCast, StrCast } from "../../fields/Types";
+import { CurrentUserUtils } from "../util/CurrentUserUtils";
+import { UndoManager } from "../util/UndoManager";
+import "./DashboardView.scss"
+
+@observer
+export class DashboardView extends React.Component {
+
+      //TODO: delete dashboard, share dashboard, etc.
+
+      newDashboard = async () => {
+            const batch = UndoManager.StartBatch("new dash");
+            await CurrentUserUtils.createNewDashboard(Doc.UserDoc());
+            batch.end();
+      }
+
+      clickDashboard = async (e: React.MouseEvent, dashboard: Doc) => {
+            if (e.detail === 2) {
+                  Doc.UserDoc().activeDashboard = dashboard
+            }
+      }
+
+      render() {
+            const myDashboards = DocListCast(CurrentUserUtils.MyDashboards.data);
+            return <div className="dashboard-view">
+                  <div className="left-menu">
+                        <div onClick={this.newDashboard}>New</div>
+                        <div>All Dashboards</div>
+                  </div>
+                  <div className="all-dashboards">
+                        {myDashboards.map((dashboard) =>
+                              <div className="dashboard-container" key={dashboard[Id]} onClick={(e) => { this.clickDashboard(e, dashboard) }}>
+                                    <img src="https://media.istockphoto.com/photos/hot-air-balloons-flying-over-the-botan-canyon-in-turkey-picture-id1297349747?b=1&k=20&m=1297349747&s=170667a&w=0&h=oH31fJty_4xWl_JQ4OIQWZKP8C6ji9Mz7L4XmEnbqRU="></img>
+                                    <div className="title"> {StrCast(dashboard.title)} </div>
+                              </div>
+
+                        )}
+                        {myDashboards.map((dashboard) => {
+                              console.log(dashboard.thumb)
+                        })}
+
+                  </div>
+            </div>
+      }
+}
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 517fe097c..49c2dcf34 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -10,6 +10,7 @@ import { CurrentUserUtils } from "../util/CurrentUserUtils";
 import { LinkManager } from "../util/LinkManager";
 import { RecordingApi } from "../util/RecordingApi";
 import { CollectionView } from "./collections/CollectionView";
+import { DashboardView } from './DashboardView';
 import { MainView } from "./MainView";
 
 AssignAllExtensions();
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 15e1dbe18..cdf341c3b 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -37,6 +37,7 @@ import { CollectionView, CollectionViewType } from './collections/CollectionView
 import "./collections/TreeView.scss";
 import { ComponentDecorations } from './ComponentDecorations';
 import { ContextMenu } from './ContextMenu';
+import { DashboardView } from './DashboardView';
 import { DictationOverlay } from './DictationOverlay';
 import { DocumentDecorations } from './DocumentDecorations';
 import { GestureOverlay } from './GestureOverlay';
@@ -659,12 +660,19 @@ export class MainView extends React.Component {
             {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
             {DocumentLinksButton.LinkEditorDocView ? <LinkMenu clearLinkEditor={action(() => DocumentLinksButton.LinkEditorDocView = undefined)} docView={DocumentLinksButton.LinkEditorDocView} /> : (null)}
             {LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : (null)}
-            <div style={{ position: "relative", display: LightboxView.LightboxDoc ? "none" : undefined, zIndex: 1999 }} >
-                <CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} />
-            </div>
-            <GestureOverlay >
-                {this.mainDashboardArea}
-            </GestureOverlay>
+            
+            {Doc.UserDoc().activeDashboard ? 
+            <>
+                  <div style={{ position: "relative", display: LightboxView.LightboxDoc ? "none" : undefined, zIndex: 1999 }} >
+                        <CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} />
+                  </div> 
+                   <GestureOverlay >
+                         {this.mainDashboardArea} 
+                  </GestureOverlay>
+            </> :
+                  <DashboardView/>
+            }
+            
             <PreviewCursor />
             <TaskCompletionBox />
             <ContextMenu />
diff --git a/src/client/views/global/globalCssVariables.scss b/src/client/views/global/globalCssVariables.scss
index a14634bdc..d41f9e06a 100644
--- a/src/client/views/global/globalCssVariables.scss
+++ b/src/client/views/global/globalCssVariables.scss
@@ -38,7 +38,7 @@ $small-text: 9px;
 // misc values
 $border-radius: 0.3em;
 $search-thumnail-size: 130;
-$topbar-height: 32px;
+$topbar-height: 50px;
 $antimodemenu-height: 36px;
 
 // dragged items
diff --git a/src/client/views/topbar/TopBar.scss b/src/client/views/topbar/TopBar.scss
index 923f1892e..9a8e7c2e5 100644
--- a/src/client/views/topbar/TopBar.scss
+++ b/src/client/views/topbar/TopBar.scss
@@ -1,29 +1,30 @@
 @import "../global/globalCssVariables";
 
 .topbar-container {
-    display: flex;
-    flex-direction: column;
-    width: 100%;
-    position: relative;
-    font-size: 10px;
-    line-height: 1;
-    overflow-y: auto;
-    overflow-x: visible;
-    background: $dark-gray;
-    overflow: visible;
-    z-index: 1000;
-
-    .topbar-bar {
-        height: $topbar-height;
-        display: grid;
-        grid-auto-columns: 33.3% 33.3% 33.3%;
-        align-items: center;
-        background-color: $dark-gray;
-
-        .topBar-icon {
+      display: flex;
+      flex-direction: column;
+      width: 100%;
+      position: relative;
+      font-size: 10px;
+      line-height: 1;
+      overflow-y: auto;
+      overflow-x: visible;
+      background: $dark-gray;
+      overflow: visible;
+      z-index: 1000;
+
+      .topbar-button-text {
+            color: $white;
+            padding: 10px;
+            size: 15;
+
+            &:hover {
+                  font-weight: 500;
+            }
+      }
+
+      .topbar-button-icon {
             cursor: pointer;
-            font-size: 12.5px;
-            font-family: 'Roboto';
             width: fit-content;
             display: flex;
             justify-content: center;
@@ -31,190 +32,179 @@
             align-items: center;
             justify-self: center;
             align-self: center;
-            border-radius: $standard-border-radius;
             padding: 5px;
             transition: linear 0.2s;
-            color: $black;
-            background-color: $light-gray;
+            color: $white;
 
             &:hover {
-                background-color: darken($color: $light-gray, $amount: 20);
+                  background-color: darken($color: $light-gray, $amount: 20);
             }
-        }
-
-
-
-
-        .topbar-center {
-            grid-column: 2;
-            display: inline-flex;
-            justify-content: center;
+      }
+
+      .topbar-title {
+            color: $white;
+            font-size: 17;
+            font-weight: 500;
+      }
+
+      .topbar-bar {
+            height: $topbar-height;
+            display: grid;
+            grid-auto-columns: 33.3% 33.3% 33.3%;
             align-items: center;
-            gap: 5px;
-
-            .topbar-dashboards {
-                display: flex;
-                flex-direction: row;
-                gap: 5px;
+            background-color: $dark-gray;
+
+            .topbar-center {
+                  grid-column: 2;
+                  display: inline-flex;
+                  justify-content: center;
+                  align-items: center;
+                  gap: 5px;
+
+                  .topbar-dashboards {
+                        display: flex;
+                        flex-direction: row;
+                        gap: 5px;
+                  }
             }
 
-            .topbar-lozenge-dashboard {
-                display: flex;
-
-
-
-                .topbar-dashSelect {
-                    border: none;
-                    background-color: $dark-gray;
-                    color: $white;
-                    font-family: 'Roboto';
-                    font-size: 17;
-                    font-weight: 500;
-
-                    &:hover {
-                        cursor: pointer;
-                    }
-                }
-            }
-        }
-
-
-        .topbar-right {
-            grid-column: 3;
-            position: relative;
-            display: flex;
-            justify-content: flex-end;
-            gap: 5px;
-            margin-right: 5px;
-        }
-
-        .topbar-left {
-            grid-column: 1;
-            color: black;
-            font-family: 'Roboto';
-            position: relative;
-            display: flex;
-            width: fit-content;
-            gap: 5px;
 
-            .topBar-icon:hover {
-                background-color: $close-red;
+            .topbar-right {
+                  grid-column: 3;
+                  position: relative;
+                  display: flex;
+                  justify-content: flex-end;
+                  gap: 5px;
+                  margin-right: 5px;
             }
 
-            .topbar-lozenge-user,
-            .topbar-lozenge {
-                height: 23;
-                font-size: 12;
-                color: white;
-                font-family: 'Roboto';
-                font-weight: 400;
-                padding: 4px;
-                align-self: center;
-                margin-left: 7px;
-                display: flex;
-                align-items: center;
-
-                .topbar-dashSelect {
-                    border: none;
-                    background-color: transparent;
-                    color: black;
-                    font-family: 'Roboto';
-                    font-size: 17;
-                    font-weight: 500;
-
-                    &:hover {
+            .topbar-left {
+                  grid-column: 1;
+                  color: black;
+                  font-family: 'Roboto';
+                  position: relative;
+                  display: flex;
+                  width: fit-content;
+                  gap: 5px;
+
+                  .topBar-icon:hover {
+                        background-color: $close-red;
+                  }
+
+                  .topbar-lozenge-user,
+                  .topbar-lozenge {
+                        height: 23;
+                        font-size: 12;
+                        color: white;
+                        font-family: 'Roboto';
+                        font-weight: 400;
+                        padding: 4px;
+                        align-self: center;
+                        margin-left: 7px;
+                        display: flex;
+                        align-items: center;
+
+                        .topbar-dashSelect {
+                              border: none;
+                              background-color: transparent;
+                              color: black;
+                              font-family: 'Roboto';
+                              font-size: 17;
+                              font-weight: 500;
+
+                              &:hover {
+                                    cursor: pointer;
+                              }
+                        }
+                  }
+
+                  .topbar-logoff {
+                        border-radius: 3px;
+                        background: olivedrab;
+                        color: white;
+                        display: none;
+                        margin-left: 5px;
+                        padding: 1px 2px 1px 2px;
                         cursor: pointer;
-                    }
-                }
-            }
-
-            .topbar-logoff {
-                border-radius: 3px;
-                background: olivedrab;
-                color: white;
-                display: none;
-                margin-left: 5px;
-                padding: 1px 2px 1px 2px;
-                cursor: pointer;
-            }
-
-            .topbar-logoff {
-                background: red;
-            }
-
-            .topbar-lozenge-user:hover {
-                .topbar-logoff {
-                    display: inline-block;
-                }
-            }
-        }
-
-        .topbar-barChild {
+                  }
 
-            &.topbar-collection {
-                flex: 0 1 auto;
-                margin-left: 2px;
-                margin-right: 2px
-            }
-
-            &.topbar-input {
-                margin: 5px;
-                border-radius: 20px;
-                border: $dark-gray;
-                display: block;
-                width: 130px;
-                -webkit-transition: width 0.4s;
-                transition: width 0.4s;
-                /* align-self: stretch; */
-                outline: none;
-
-                &:focus {
-                    width: 500px;
-                    outline: none;
-                }
-            }
-
-            &.topbar-filter {
-                align-self: stretch;
-
-                button {
-                    transform: none;
-
-                    &:hover {
-                        transform: none;
-                    }
-                }
-            }
+                  .topbar-logoff {
+                        background: red;
+                  }
 
-            &.topbar-submit {
-                margin-left: 2px;
-                margin-right: 2px
+                  .topbar-lozenge-user:hover {
+                        .topbar-logoff {
+                              display: inline-block;
+                        }
+                  }
             }
 
-            &.topbar-close {
-                color: $white;
-                max-height: $topbar-height;
+            .topbar-barChild {
+
+                  &.topbar-collection {
+                        flex: 0 1 auto;
+                        margin-left: 2px;
+                        margin-right: 2px
+                  }
+
+                  &.topbar-input {
+                        margin: 5px;
+                        border-radius: 20px;
+                        border: $dark-gray;
+                        display: block;
+                        width: 130px;
+                        -webkit-transition: width 0.4s;
+                        transition: width 0.4s;
+                        /* align-self: stretch; */
+                        outline: none;
+
+                        &:focus {
+                              width: 500px;
+                              outline: none;
+                        }
+                  }
+
+                  &.topbar-filter {
+                        align-self: stretch;
+
+                        button {
+                              transform: none;
+
+                              &:hover {
+                                    transform: none;
+                              }
+                        }
+                  }
+
+                  &.topbar-submit {
+                        margin-left: 2px;
+                        margin-right: 2px
+                  }
+
+                  &.topbar-close {
+                        color: $white;
+                        max-height: $topbar-height;
+                  }
             }
-        }
-    }
+      }
 }
 
 .topbar-results {
-    display: flex;
-    flex-direction: column;
-    top: 300px;
-    display: flex;
-    flex-direction: column;
-    height: 100%;
-    overflow: visible;
-
-    .no-result {
-        width: 500px;
-        background: $light-gray;
-        padding: 10px;
-        height: 50px;
-        text-transform: uppercase;
-        text-align: left;
-        font-weight: bold;
-    }
+      display: flex;
+      flex-direction: column;
+      top: 300px;
+      display: flex;
+      flex-direction: column;
+      height: 100%;
+      overflow: visible;
+
+      .no-result {
+            width: 500px;
+            background: $light-gray;
+            padding: 10px;
+            height: 50px;
+            text-transform: uppercase;
+            text-align: left;
+            font-weight: bold;
+      }
 }
\ No newline at end of file
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index e486bcb52..1a11925dc 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -5,7 +5,7 @@ import { observer } from "mobx-react";
 import * as React from 'react';
 import { Doc, DocListCast } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
-import { StrCast } from '../../../fields/Types';
+import { Cast, StrCast } from '../../../fields/Types';
 import { Utils } from '../../../Utils';
 import { CurrentUserUtils } from "../../util/CurrentUserUtils";
 import { SettingsManager } from "../../util/SettingsManager";
@@ -20,29 +20,54 @@ import "./TopBar.scss";
  */
 @observer
 export class TopBar extends React.Component {
-    render() {
-        const myDashboards = DocListCast(CurrentUserUtils.MyDashboards.data);
-        return (
-            //TODO:glr Add support for light / dark mode
-            <div style={{ pointerEvents: "all" }} className="topbar-container">
-                <div className="topbar-bar" style={{ background: Colors.DARK_GRAY, borderBottom: Borders.STANDARD }}>
-                    <div className="topbar-left">
+      navigateToHome = () => {
+            Doc.UserDoc().activeDashboard = undefined
+      }
+      render() {
+            const myDashboards = DocListCast(CurrentUserUtils.MyDashboards.data);
+            const activeDashboard = Cast(Doc.UserDoc().activeDashboard, Doc, null)
+            return (
+                  //TODO:glr Add support for light / dark mode
+                  <div style={{ pointerEvents: "all" }} className="topbar-container">
+                        <div className="topbar-bar" style={{ background: Colors.DARK_GRAY, borderBottom: Borders.STANDARD }}>
+                              <div className="topbar-left">
+                                    <div className="topbar-button-text" onClick={this.navigateToHome}>Home</div>
+                              </div>
+                              <div className="topbar-center" >
+                                    <div className="topbar-title">
+                                          {activeDashboard ? StrCast(activeDashboard.title) : "Dash"}
+                                    </div>
+                              </div>
+                              <div className="topbar-right" >
+                                    <div className="topbar-button-icon" >
+                                          <FontAwesomeIcon icon="question-circle" />
+                                    </div>
+                                    <div className="topbar-button-icon" onClick={() => SettingsManager.Instance.open()}>
+                                          <FontAwesomeIcon icon="cog" />
+                                    </div>
+                              </div>
+
+                              {/* <div className="topbar-left">
                         <div className="topbar-lozenge-user">
                             {`${Doc.CurrentUserEmail}`}
                         </div>
                         <div className="topbar-icon" onClick={() => window.location.assign(Utils.prepend("/logout"))}>
                             {"Log out"}
                         </div>
-                    </div>
-                    <div className="topbar-center" >
+                    </div> */}
+                              {/* <div className="topbar-center" >
                         <div className="topbar-lozenge-dashboard">
-                            <select className="topbar-dashSelect" onChange={undoBatch(e => CurrentUserUtils.openDashboard(Doc.UserDoc(), myDashboards[Number(e.target.value)]))}
+                              <div style={{ color: Colors.WHITE }}>
+                                    {activeDashboard ? StrCast(activeDashboard.title) : "Dash"}
+                              </div> */}
+
+                              {/* <select className="topbar-dashSelect" onChange={undoBatch(e => CurrentUserUtils.openDashboard(Doc.UserDoc(), myDashboards[Number(e.target.value)]))}
                                 value={myDashboards.indexOf(CurrentUserUtils.ActiveDashboard)}
                                 style={{ color: Colors.WHITE }}>
                                 {myDashboards.map((dash, i) => <option key={dash[Id]} value={i}> {StrCast(dash.title)} </option>)}
-                            </select>
-                        </div>
-                        <div className="topbar-dashboards">
+                            </select> */}
+                              {/* </div> */}
+                              {/* <div className="topbar-dashboards">
                             <Tooltip title={<div className="dash-tooltip">Create a new dashboard </div>} placement="bottom"><div className="topbar-icon" onClick={async () => {
                                 const batch = UndoManager.StartBatch("new dash");
                                 await CurrentUserUtils.createNewDashboard(Doc.UserDoc());
@@ -65,19 +90,25 @@ export class TopBar extends React.Component {
                                     Explore<FontAwesomeIcon icon="map" />
                                 </div>
                             </Tooltip>
-                        </div>
-                    </div>
-                    <div className="topbar-right" >
-                        <div className="topbar-icon" onClick={() => window.open(
+                        </div> */}
+                              {/* </div> */}
+                              {/* <div className="topbar-right" > */}
+                              {/* <div onClick={() => window.open(
                             "https://brown-dash.github.io/Dash-Documentation/", "_blank")}>
-                            {"Help"}<FontAwesomeIcon icon="question-circle" />
-                        </div>
-                        <div className="topbar-icon" onClick={() => SettingsManager.Instance.open()}>
-                            {"Settings"}<FontAwesomeIcon icon="cog" />
+                              <FontAwesomeIcon icon="question-circle" />
+                          </div>
+                          <div onClick={() => SettingsManager.Instance.open()}>
+                          <FontAwesomeIcon icon="cog" />
+                          </div> */}
+                              {/* <div className="topbar-icon" >
+                                          <FontAwesomeIcon icon="question-circle" />
+                                    </div>
+                                    <div className="topbar-icon" onClick={() => SettingsManager.Instance.open()}>
+                                          <FontAwesomeIcon icon="cog" />
+                                    </div>
+                              </div> */}
                         </div>
-                    </div>
-                </div>
-            </div >
-        );
-    }
+                  </div >
+            );
+      }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 550b5e93097e0708199a9cf88714754d197b3b97 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 27 Jun 2022 12:48:54 -0400
Subject: fixed initialization issues with creating user documents
 unnecessarily. fixed updating doc icon templates to apply to documents
 already created.

---
 src/client/documents/Documents.ts                  |  10 +-
 src/client/util/CurrentUserUtils.ts                |  64 ++++++-----
 src/client/util/LinkManager.ts                     | 123 +++++++++++----------
 src/client/views/Main.tsx                          |   3 +-
 .../collections/collectionFreeForm/MarqueeView.tsx |   2 +-
 src/fields/Doc.ts                                  |   2 +-
 6 files changed, 104 insertions(+), 100 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 84e6258ac..3780df5b9 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -105,7 +105,6 @@ export class DocumentOptions {
     userColor?: STRt = new StrInfo("color associated with a Dash user (seen in header fields of shared documents)");
     color?: STRt = new StrInfo("foreground color data doc");
     backgroundColor?: STRt = new StrInfo("background color for data doc");
-    _backgroundColor?: STRt = new StrInfo("background color for each template layout doc (overrides backgroundColor)");
     _autoHeight?: BOOLt = new BoolInfo("whether document automatically resizes vertically to display contents");
     _headerHeight?: NUMt = new NumInfo("height of document header used for displaying title");
     _headerFontSize?: NUMt = new NumInfo("font size of header of custom notes");
@@ -328,6 +327,7 @@ export class DocumentOptions {
     text?: string;
     textTransform?: string; // is linear view expanded
     letterSpacing?: string; // is linear view expanded
+    iconTemplate?: string; // name of icon template style
     selectedIndex?: number; // which item in a linear view has been selected using the "thumb doc" ui
     clipboard?: Doc;
     searchQuery?: string; // for quersyBox
@@ -1288,9 +1288,7 @@ export namespace DocUtils {
         const batch = UndoManager.StartBatch("makeCustomViewClicked");
         runInAction(() => {
             doc.layoutKey = "layout_" + templateSignature;
-            if (doc[doc.layoutKey] === undefined) {
-                createCustomView(doc, creator, templateSignature, docLayoutTemplate);
-            }
+            createCustomView(doc, creator, templateSignature, docLayoutTemplate);
         });
         batch.end();
         return doc;
@@ -1318,7 +1316,9 @@ export namespace DocUtils {
         const options = { title: "data", backgroundColor: StrCast(doc.backgroundColor), _autoHeight: true, _width, x: -_width / 2, y: - _height / 2, _showSidebar: false };
 
         if (docLayoutTemplate) {
-            Doc.ApplyTemplateTo(docLayoutTemplate, doc, customName, undefined);
+            if (docLayoutTemplate !== doc[customName]) { 
+                Doc.ApplyTemplateTo(docLayoutTemplate, doc, customName, undefined);
+            }
         } else {
             let fieldTemplate: Opt<Doc>;
             if (doc.data instanceof RichTextField || typeof (doc.data) === "string") {
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 5c77041e0..f585277d8 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -205,29 +205,37 @@ export class CurrentUserUtils {
         const reqdOpts = { title: "icon templates", _height: 75, system: true };
         const templateIconsDoc = this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts));
 
-        const makeIconTemplate = (type: DocumentType | undefined, templateField: string, iconTemplate: (opts:DocumentOptions) => Doc) => {
+        const makeIconTemplate = (type: DocumentType | undefined, templateField: string, opts:DocumentOptions) => {
             const iconFieldName = "icon" + (type ? "_" + type : "");
-            return DocCast(templateIconsDoc[iconFieldName] ?? (templateIconsDoc[iconFieldName] = MakeTemplate(iconTemplate({onClick:deiconifyScript(), system: true}), true, iconFieldName, templateField))) ;
+            const curIcon = DocCast(templateIconsDoc[iconFieldName]);
+            let creator = labelBox;
+            switch (opts.iconTemplate) {
+                case DocumentType.IMG : creator = imageBox; break;
+                case DocumentType.FONTICON: creator = fontBox; break;
+            }
+            const allopts = {system: true, ...opts};
+            return this.AssignScripts(  (curIcon?.iconTemplate === opts.iconTemplate ? 
+                      this.AssignOpts(curIcon, allopts):undefined) ?? ((templateIconsDoc[iconFieldName] = MakeTemplate(creator(allopts), true, iconFieldName, templateField))),
+                {onClick:"deiconifyView(documentView)"});
         };
-        const deiconifyScript = () => ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" });
-        const labelBox = (opts: object) => Docs.Create.LabelDocument({
+        const labelBox = (opts: DocumentOptions, data?:string) => Docs.Create.LabelDocument({
             textTransform: "unset", letterSpacing: "unset", _singleLine: false, _minFontSize: 14, _maxFontSize: 24, borderRounding: "5px",  _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...opts
         });
-        const imageBox = (url: Opt<string>, opts: object) => Docs.Create.ImageDocument(url ?? "http://www.cs.brown.edu/~bcz/noImage.png", { "icon-nativeWidth": 360 / 4, "icon-nativeHeight": 270 / 4, _width: 360 / 4, _height: 270 / 4,  _showTitle: "title", ...opts });
-        const fontBox = (opts:DocumentOptions) => Docs.Create.FontIconDocument({ _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, ...opts  });
+        const imageBox = (opts: DocumentOptions, url?:string) => Docs.Create.ImageDocument(url ?? "http://www.cs.brown.edu/~bcz/noImage.png", { "icon-nativeWidth": 360 / 4, "icon-nativeHeight": 270 / 4, iconTemplate:DocumentType.IMG,  _width: 360 / 4, _height: 270 / 4,  _showTitle: "title", ...opts });
+        const fontBox = (opts:DocumentOptions, data?:string) => Docs.Create.FontIconDocument({ _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, ...opts  });
         const iconTemplates = [
-            makeIconTemplate(undefined, "title", (opts) => labelBox({ ...opts, _backgroundColor: "dimgray" })),
-            makeIconTemplate(DocumentType.AUDIO, "title", (opts) => labelBox({ ...opts, _backgroundColor: "lightgreen" })),
-            makeIconTemplate(DocumentType.PDF, "title", (opts) => labelBox({ ...opts, _backgroundColor: "pink" })),
-            makeIconTemplate(DocumentType.WEB, "title", (opts) => labelBox({...opts,  _backgroundColor: "brown" })),
-            makeIconTemplate(DocumentType.RTF, "text", (opts) => labelBox({ ...opts, _showTitle: "creationDate" })),
-            makeIconTemplate(DocumentType.IMG, "data", (opts) => imageBox("", { ...opts, _height: undefined, })),
-            makeIconTemplate(DocumentType.COL, "icon", (opts) => imageBox(undefined, opts)),
-            makeIconTemplate(DocumentType.VID, "icon", (opts) => imageBox(undefined, opts)),
-            makeIconTemplate(DocumentType.BUTTON, "data", (opts) => fontBox(opts)),
+            makeIconTemplate(undefined,          "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "dimgray"}),
+            makeIconTemplate(DocumentType.AUDIO, "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "lightgreen"}),
+            makeIconTemplate(DocumentType.PDF,   "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "pink"}),
+            makeIconTemplate(DocumentType.WEB,   "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "brown"}),
+            makeIconTemplate(DocumentType.RTF,   "text",  { iconTemplate:DocumentType.LABEL, _showTitle: "creationDate"}),
+            makeIconTemplate(DocumentType.IMG,   "data",  { iconTemplate:DocumentType.IMG,   _height: undefined}),
+            makeIconTemplate(DocumentType.COL,   "icon",  { iconTemplate:DocumentType.IMG}),
+            makeIconTemplate(DocumentType.VID,   "icon",  { iconTemplate:DocumentType.IMG}),
+            makeIconTemplate(DocumentType.BUTTON,"data",  { iconTemplate:DocumentType.FONTICON}),
             //nasty hack .. templates are looked up exclusively by type -- but we want a template for a document with a certain field (transcription) .. so this hack and the companion hack in createCustomView does this for now
-            makeIconTemplate("transcription" as any, "transcription", (opts) => labelBox({ ...opts, _backgroundColor: "orange" })),
-            // makeIconTemplate(DocumentType.PDF, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {}))
+            makeIconTemplate("transcription" as any, "transcription", { iconTemplate:DocumentType.LABEL, backgroundColor: "orange" }),
+            //makeIconTemplate(DocumentType.PDF, "icon", {iconTemplate:DocumentType.IMG}, (opts) => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", opts))
         ].filter(d => d).map(d => d!);
         this.AssignOpts(DocCast(doc[field]), {}, iconTemplates);
     }
@@ -779,23 +787,23 @@ export class CurrentUserUtils {
 
     /// Initializes all the default buttons for the top bar context menu
     static setupContextMenuButtons(doc: Doc, field="myContextMenuBtns") {
-        const ctxtMenuBtnsDoc = DocCast(doc[field]);
+        const reqdCtxtOpts = { title: "context menu buttons", flexGap: 0, childDontRegisterViews: true,  linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 };
+        const ctxtMenuBtnsDoc = this.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), reqdCtxtOpts, undefined);
         const ctxtMenuBtns = CurrentUserUtils.contextMenuTools().map(params => {
             const menuBtnDoc = DocListCast(ctxtMenuBtnsDoc?.data).find(doc => doc.title === params.title);
             if (!params.subMenu) {
                 return this.setupContextMenuButton(params, menuBtnDoc);
             } else {
-                const reqdSubMenuOpts = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, title:"submenu",
+                const reqdSubMenuOpts = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit,
                     childDontRegisterViews: true, flexGap: 0, _height: 30, ignoreClick: true,
                     linearViewSubMenu: true, linearViewExpandable: true, };
                 return this.AssignScripts(this.AssignOpts(menuBtnDoc, reqdSubMenuOpts) ??
-                        this.linearButtonList(reqdSubMenuOpts, params.subMenu.map(sub =>
+                        (ctxtMenuBtnsDoc[StrCast(params.title)]= this.linearButtonList(reqdSubMenuOpts, params.subMenu.map(sub =>
                             this.setupContextMenuButton(sub, DocListCast(menuBtnDoc?.data).find(doc => doc.title === sub.title))
-                        )), undefined, params.funcs);
+                        ))), undefined, params.funcs);
             }
         });
-        const reqdCtxtOpts = { title: "context menu buttons", flexGap: 0, childDontRegisterViews: true,  linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 };
-        return this.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), reqdCtxtOpts, ctxtMenuBtns);
+        return this.AssignOpts(ctxtMenuBtnsDoc, reqdCtxtOpts, ctxtMenuBtns);
     }
 
     /// collection of documents rendered in the overlay layer above all tabs and other UI
@@ -957,12 +965,13 @@ export class CurrentUserUtils {
         this.AssignDocField(doc, "globalScriptDatabase", (opts) =>  Docs.Prototypes.MainScriptDocument(), {});
         this.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "header bar", system: true }); // drop down panel at top of dashboard for stashing documents
         
-        setTimeout(() => DocServer.UPDATE_SERVER_CACHE(), 2500);
         if (doc.activeDashboard instanceof Doc) {
             // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
             doc.activeDashboard.colorScheme = doc.activeDashboard.colorScheme === ColorScheme.Light ? undefined : doc.activeDashboard.colorScheme;
         }
+        new LinkManager();
 
+        DocServer.UPDATE_SERVER_CACHE();
         return doc;
     }
     static setupFieldInfos(doc:Doc, field="fieldInfos") {
@@ -1009,12 +1018,7 @@ export class CurrentUserUtils {
                     await Docs.Prototypes.initialize();
                     const userDoc = Docs.newAccount ? new Doc(userDocumentId, true) : field as Doc;
                     Docs.newAccount &&(userDoc.activePage = "home");
-                    const updated = this.updateUserDocument(Doc.SetUserDoc(userDoc), sharingDocumentId, linkDatabaseId);
-                    (await DocListCastAsync(Doc.LinkDBDoc()?.data))?.forEach(async link => { // make sure anchors are loaded to avoid incremental updates to computedFn's in LinkManager
-                        const a1 = await Cast(link?.anchor1, Doc, null);
-                        const a2 = await Cast(link?.anchor2, Doc, null);
-                    });
-                    return updated;
+                    return this.updateUserDocument(Doc.SetUserDoc(userDoc), sharingDocumentId, linkDatabaseId);
                 });
             } else {
                 throw new Error("There should be a user id! Why does Dash think there isn't one?");
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 2100b1195..d51cd350d 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -1,7 +1,6 @@
-import { validationResult } from "express-validator/check";
 import { action, observable, observe } from "mobx";
 import { computedFn } from "mobx-utils";
-import { DirectLinksSym, Doc, DocListCast, Field, Opt } from "../../fields/Doc";
+import { DirectLinksSym, Doc, DocListCast, DocListCastAsync, Field, Opt } from "../../fields/Doc";
 import { List } from "../../fields/List";
 import { ProxyField } from "../../fields/Proxy";
 import { BoolCast, Cast, StrCast } from "../../fields/Types";
@@ -35,72 +34,74 @@ export class LinkManager {
     constructor() {
         LinkManager._instance = this;
         this.createLinkrelationshipLists();
-        setTimeout(() => {
-            LinkManager.userLinkDBs = [];
-            const addLinkToDoc = (link: Doc) => {
-                const a1Prom = link?.anchor1;
-                const a2Prom = link?.anchor2;
-                Promise.all([a1Prom, a2Prom]).then((all) => {
-                    const a1 = all[0];
-                    const a2 = all[1];
-                    const a1ProtoProm = (link?.anchor1 as Doc)?.proto;
-                    const a2ProtoProm = (link?.anchor2 as Doc)?.proto;
-                    Promise.all([a1ProtoProm, a2ProtoProm]).then(action((all) => {
-                        if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) {
-                            Doc.GetProto(a1)[DirectLinksSym].add(link);
-                            Doc.GetProto(a2)[DirectLinksSym].add(link);
-                            Doc.GetProto(link)[DirectLinksSym].add(link);
-                        }
-                    }));
-                });
-            };
-            const remLinkFromDoc = (link: Doc) => {
-                const a1 = link?.anchor1;
-                const a2 = link?.anchor2;
-                Promise.all([a1, a2]).then(action(() => {
+        LinkManager.userLinkDBs = [];
+        const addLinkToDoc = (link: Doc) => {
+            const a1Prom = link?.anchor1;
+            const a2Prom = link?.anchor2;
+            Promise.all([a1Prom, a2Prom]).then((all) => {
+                const a1 = all[0];
+                const a2 = all[1];
+                const a1ProtoProm = (link?.anchor1 as Doc)?.proto;
+                const a2ProtoProm = (link?.anchor2 as Doc)?.proto;
+                Promise.all([a1ProtoProm, a2ProtoProm]).then(action((all) => {
                     if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) {
-                        Doc.GetProto(a1)[DirectLinksSym].delete(link);
-                        Doc.GetProto(a2)[DirectLinksSym].delete(link);
-                        Doc.GetProto(link)[DirectLinksSym].delete(link);
+                        Doc.GetProto(a1)[DirectLinksSym].add(link);
+                        Doc.GetProto(a2)[DirectLinksSym].add(link);
+                        Doc.GetProto(link)[DirectLinksSym].add(link);
                     }
                 }));
-            };
-            const watchUserLinkDB = (userLinkDBDoc: Doc) => {
-                LinkManager.links.push(...DocListCast(userLinkDBDoc.data));
-                const toRealField = (field: Field) => field instanceof ProxyField ? field.value() : field;  // see List.ts.  data structure is not a simple list of Docs, but a list of ProxyField/Fields
-                observe(userLinkDBDoc.data as Doc, change => {  // observe pushes/splices on a user link DB 'data' field (should only happen for local changes)
-                    switch (change.type as any) {
-                        case "splice":
-                            (change as any).added.forEach((link: any) => addLinkToDoc(toRealField(link)));
-                            (change as any).removed.forEach((link: any) => remLinkFromDoc(toRealField(link)));
-                            break;
-                        case "update": //let oldValue = change.oldValue;
-                    }
-                }, true);
-                observe(userLinkDBDoc, "data",  // obsever when a new array of links is assigned as the link DB 'data' field (should happen whenever a remote user adds/removes a link)
-                    change => {
-                        switch (change.type as any) {
-                            case "update":
-                                Promise.all([...(change.oldValue as any as Doc[] || []), ...(change.newValue as any as Doc[] || [])]).then(doclist => {
-                                    const oldDocs = doclist.slice(0, (change.oldValue as any as Doc[] || []).length);
-                                    const newDocs = doclist.slice((change.oldValue as any as Doc[] || []).length, doclist.length);
-
-                                    const added = newDocs?.filter(link => !(oldDocs || []).includes(link));
-                                    const removed = oldDocs?.filter(link => !(newDocs || []).includes(link));
-                                    added?.forEach((link: any) => addLinkToDoc(toRealField(link)));
-                                    removed?.forEach((link: any) => remLinkFromDoc(toRealField(link)));
-                                });
-                        }
-                    }, true);
-            };
-            observe(LinkManager.userLinkDBs, change => {
+            });
+        };
+        const remLinkFromDoc = (link: Doc) => {
+            const a1 = link?.anchor1;
+            const a2 = link?.anchor2;
+            Promise.all([a1, a2]).then(action(() => {
+                if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) {
+                    Doc.GetProto(a1)[DirectLinksSym].delete(link);
+                    Doc.GetProto(a2)[DirectLinksSym].delete(link);
+                    Doc.GetProto(link)[DirectLinksSym].delete(link);
+                }
+            }));
+        };
+        const watchUserLinkDB = (userLinkDBDoc: Doc) => {
+            LinkManager.links.push(...DocListCast(userLinkDBDoc.data));
+            const toRealField = (field: Field) => field instanceof ProxyField ? field.value() : field;  // see List.ts.  data structure is not a simple list of Docs, but a list of ProxyField/Fields
+            observe(userLinkDBDoc.data as Doc, change => {  // observe pushes/splices on a user link DB 'data' field (should only happen for local changes)
                 switch (change.type as any) {
-                    case "splice": (change as any).added.forEach(watchUserLinkDB); break;
+                    case "splice":
+                        (change as any).added.forEach((link: any) => addLinkToDoc(toRealField(link)));
+                        (change as any).removed.forEach((link: any) => remLinkFromDoc(toRealField(link)));
+                        break;
                     case "update": //let oldValue = change.oldValue;
                 }
             }, true);
-            LinkManager.addLinkDB(Doc.LinkDBDoc());
-        });
+            observe(userLinkDBDoc, "data",  // obsever when a new array of links is assigned as the link DB 'data' field (should happen whenever a remote user adds/removes a link)
+                change => {
+                    switch (change.type as any) {
+                        case "update":
+                            Promise.all([...(change.oldValue as any as Doc[] || []), ...(change.newValue as any as Doc[] || [])]).then(doclist => {
+                                const oldDocs = doclist.slice(0, (change.oldValue as any as Doc[] || []).length);
+                                const newDocs = doclist.slice((change.oldValue as any as Doc[] || []).length, doclist.length);
+
+                                const added = newDocs?.filter(link => !(oldDocs || []).includes(link));
+                                const removed = oldDocs?.filter(link => !(newDocs || []).includes(link));
+                                added?.forEach((link: any) => addLinkToDoc(toRealField(link)));
+                                removed?.forEach((link: any) => remLinkFromDoc(toRealField(link)));
+                            });
+                    }
+                }, true);
+        };
+        observe(LinkManager.userLinkDBs, change => {
+            switch (change.type as any) {
+                case "splice": (change as any).added.forEach(watchUserLinkDB); break;
+                case "update": //let oldValue = change.oldValue;
+            }
+        }, true);
+        LinkManager.addLinkDB(Doc.LinkDBDoc());
+        DocListCastAsync(Doc.LinkDBDoc()?.data).then(dblist => dblist?.forEach(async link => { // make sure anchors are loaded to avoid incremental updates to computedFn's in LinkManager
+            const a1 = await Cast(link?.anchor1, Doc, null);
+            const a2 = await Cast(link?.anchor2, Doc, null);
+        }));
     }
 
 
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 49c2dcf34..17841c055 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -10,7 +10,6 @@ import { CurrentUserUtils } from "../util/CurrentUserUtils";
 import { LinkManager } from "../util/LinkManager";
 import { RecordingApi } from "../util/RecordingApi";
 import { CollectionView } from "./collections/CollectionView";
-import { DashboardView } from './DashboardView';
 import { MainView } from "./MainView";
 
 AssignAllExtensions();
@@ -24,6 +23,7 @@ AssignAllExtensions();
         await CurrentUserUtils.loadUserDocument(info.id);
     } else {
         await Docs.Prototypes.initialize();
+        new LinkManager();
     }
     document.getElementById('root')!.addEventListener('wheel', event => {
         if (event.ctrlKey) {
@@ -37,7 +37,6 @@ AssignAllExtensions();
     d.setTime(d.getTime() + (100 * 24 * 60 * 60 * 1000));
     const expires = "expires=" + d.toUTCString();
     document.cookie = `loadtime=${loading};${expires};path=/`;
-    new LinkManager();
     new RecordingApi;
     ReactDOM.render(<MainView />, document.getElementById('root'));
 })();
\ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 081a1a924..17f1106d0 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -517,7 +517,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             d.y = NumCast(d.y) - this.Bounds.top;
             return d;
         });
-        const summary = Docs.Create.TextDocument("", { _backgroundColor: "#e2ad32", x: this.Bounds.left, y: this.Bounds.top, isPushpin: true, _width: 200, _height: 200, _fitContentsToBox: true, _showSidebar: true, title: "overview" });
+        const summary = Docs.Create.TextDocument("", { backgroundColor: "#e2ad32", x: this.Bounds.left, y: this.Bounds.top, isPushpin: true, _width: 200, _height: 200, _fitContentsToBox: true, _showSidebar: true, title: "overview" });
         const portal = Docs.Create.FreeformDocument(selected, { x: this.Bounds.left + 200, y: this.Bounds.top, isGroup: true, backgroundColor: "transparent" });
         DocUtils.MakeLink({ doc: summary }, { doc: portal }, "summary of:summarized by", "");
 
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 4fe6eb1e7..a5d952176 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -21,7 +21,7 @@ import { FieldId, RefField } from "./RefField";
 import { RichTextField } from "./RichTextField";
 import { listSpec } from "./Schema";
 import { ComputedField, ScriptField } from "./ScriptField";
-import { BoolCast, Cast, FieldValue, NumCast, StrCast, ToConstructor } from "./Types";
+import { Cast, FieldValue, NumCast, StrCast, ToConstructor } from "./Types";
 import { AudioField, ImageField, MapField, PdfField, VideoField, WebField } from "./URLField";
 import { deleteProperty, GetEffectiveAcl, getField, getter, inheritParentAcls, makeEditable, makeReadOnly, normalizeEmail, setter, SharingPermissions, updateFunction } from "./util";
 import JSZip = require("jszip");
-- 
cgit v1.2.3-70-g09d2

