From bdabb0eb1aeac9ea9d4f1fa40889b8d30937c1f0 Mon Sep 17 00:00:00 2001
From: monoguitari <113245090+monoguitari@users.noreply.github.com>
Date: Sat, 19 Aug 2023 03:22:16 -0400
Subject: Toggling icon for mic and added webcapturedoc to dropdown

---
 src/client/views/nodes/FontIconBox/FontIconBox.tsx | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

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

diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index 5ff5f7bfa..41ad90155 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -6,7 +6,7 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc';
 import { ScriptField } from '../../../../fields/ScriptField';
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
+import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
 import { SelectionManager } from '../../../util/SelectionManager';
 import { undoable, UndoManager } from '../../../util/UndoManager';
@@ -242,7 +242,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         const list: IListItemProps[] = this.buttonList
             .filter(value => !Doc.noviceMode || !noviceList.length || noviceList.includes(value))
             .map(value => ({
-                text: value.charAt(0).toUpperCase() + value.slice(1),
+                text: value === "string" ? value.charAt(0).toUpperCase() + value.slice(1) : StrCast(DocCast(value)?.title),
                 val: value,
                 style: getStyle(value),
                 onClick: undoable(() => script.script.run({ this: this.layoutDoc, self: this.rootDoc, value }), value),
-- 
cgit v1.2.3-70-g09d2


From 2466587655888d5894159b942f09661ab73ebe66 Mon Sep 17 00:00:00 2001
From: monoguitari <113245090+monoguitari@users.noreply.github.com>
Date: Sat, 19 Aug 2023 08:33:14 -0400
Subject: Revert "Toggling icon for mic and added webcapturedoc to dropdown"

This reverts commit bdabb0eb1aeac9ea9d4f1fa40889b8d30937c1f0.
---
 src/client/util/CurrentUserUtils.ts                |  8 +---
 src/client/views/UndoStack.tsx                     |  4 +-
 src/client/views/nodes/FontIconBox/FontIconBox.tsx |  4 +-
 .../views/nodes/RecordingBox/RecordingBox.tsx      | 47 ++--------------------
 .../views/nodes/RecordingBox/RecordingView.tsx     |  3 +-
 src/client/views/nodes/trails/PresElementBox.tsx   |  2 +-
 6 files changed, 12 insertions(+), 56 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 54828867e..25c8f511b 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -714,9 +714,7 @@ export class CurrentUserUtils {
             { title: "View",    icon: "View",          toolTip: "View tools",           subMenu: CurrentUserUtils.viewTools(),   expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available
             { title: "Web",     icon: "Web",           toolTip: "Web functions",        subMenu: CurrentUserUtils.webTools(),    expertMode: false, toolType:DocumentType.WEB,            funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Web is selected
             { title: "Schema",  icon: "Schema",linearBtnWidth:58,toolTip: "Schema functions",subMenu: CurrentUserUtils.schemaTools(), expertMode: false, toolType:CollectionViewType.Schema,   funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Schema is selected
-            { title: "Audio",   icon: 'microphone',    toolTip: "Dictate",              btnType: ButtonType.ToggleButton,        expertMode: false,                                       funcs: {hidden: `getIsRecording()`},                                                                  ignoreClick: true,             scripts: { onClick:  'return toggleRecording(_readOnly_)'},},
-            { title: "StopRec", icon: "stop",          toolTip: "Stop",                 btnType: ButtonType.ToggleButton,        expertMode: false,                                       funcs: {hidden: `!getIsRecording()`},                                                                  ignoreClick: true,             scripts: { onClick: `return toggleRecording(_readOnly_)`}},
-            { title: "Dropdown",                       toolTip: "Workspace Recordings", btnType: ButtonType.DropdownList,        expertMode: false,                                       funcs: {btnList: `getWorkspaceRecordings()`},                                                         ignoreClick: true,             scripts: { script: `toggleRecPlayback(value)`}, },
+            { title: "Audio",   icon: "microphone",    toolTip: "Dictate",              btnType: ButtonType.ToggleButton,        expertMode: false,                                                                                                          ignoreClick: true,             scripts: { onClick:  'return toggleRecording(_readOnly_)'}, funcs: { }}
         ];
     }
 
@@ -739,8 +737,6 @@ export class CurrentUserUtils {
     static setupContextMenuBtn(params:Button, menuDoc:Doc):Doc {
         const menuBtnDoc = DocListCast(menuDoc?.data).find(doc => doc.title === params.title);
         const subMenu = params.subMenu;
-        Doc.UserDoc().workspaceRecordings = new List<Doc>;
-        Doc.UserDoc().isRecording = false;
         if (!subMenu) { // button does not have a sub menu
             return this.setupContextMenuButton(params, menuBtnDoc);
         }  
@@ -1003,4 +999,4 @@ ScriptingGlobals.add(function createNewPresentation() { return MainView.Instance
 ScriptingGlobals.add(function openPresentation(pres:Doc) { return MainView.Instance.openPresentation(pres); }, "creates a new presentation when called");
 ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.createNewFolder(); }, "creates a new folder in myFiles when called");
 ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); }, "imports files from device directly into the import sidebar");
-ScriptingGlobals.add(function setInkToolDefaults() { Doc.ActiveTool = InkTool.None; });
+ScriptingGlobals.add(function setInkToolDefaults() { Doc.ActiveTool = InkTool.None; });
\ No newline at end of file
diff --git a/src/client/views/UndoStack.tsx b/src/client/views/UndoStack.tsx
index 813cdd050..a551e5332 100644
--- a/src/client/views/UndoStack.tsx
+++ b/src/client/views/UndoStack.tsx
@@ -37,7 +37,7 @@ export class UndoStack extends React.Component<UndoStackProps> {
                             }}>
                             {UndoManager.undoStackNames.map((name, i) => (
                                 <div className="undoStack-resultContainer" key={i}>
-                                    <div className="undoStack-commandString">{StrCast(name).replace(/[^\.]*\./, '')}</div>
+                                    <div className="undoStack-commandString">{name.replace(/[^\.]*\./, '')}</div>
                                 </div>
                             ))}
                             {Array.from(UndoManager.redoStackNames)
@@ -45,7 +45,7 @@ export class UndoStack extends React.Component<UndoStackProps> {
                                 .map((name, i) => (
                                     <div className="undoStack-resultContainer" key={i}>
                                         <div className="undoStack-commandString" style={{ fontWeight: 'bold', color: 'red' }}>
-                                            {StrCast(name).replace(/[^\.]*\./, '')}
+                                            {name.replace(/[^\.]*\./, '')}
                                         </div>
                                     </div>
                                 ))}
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index 41ad90155..5ff5f7bfa 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -6,7 +6,7 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc';
 import { ScriptField } from '../../../../fields/ScriptField';
-import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
+import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
 import { SelectionManager } from '../../../util/SelectionManager';
 import { undoable, UndoManager } from '../../../util/UndoManager';
@@ -242,7 +242,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         const list: IListItemProps[] = this.buttonList
             .filter(value => !Doc.noviceMode || !noviceList.length || noviceList.includes(value))
             .map(value => ({
-                text: value === "string" ? value.charAt(0).toUpperCase() + value.slice(1) : StrCast(DocCast(value)?.title),
+                text: value.charAt(0).toUpperCase() + value.slice(1),
                 val: value,
                 style: getStyle(value),
                 onClick: undoable(() => script.script.run({ this: this.layoutDoc, self: this.rootDoc, value }), value),
diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
index 3237ce6cd..8fa2861b6 100644
--- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
@@ -15,8 +15,6 @@ import { BoolCast, DocCast } from '../../../../fields/Types';
 import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
 import { DocumentManager } from '../../../util/DocumentManager';
 import { Docs } from '../../../documents/Documents';
-import { CollectionFreeFormView } from '../../collections/collectionFreeForm/CollectionFreeFormView';
-import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
 
 @observer
 export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -84,64 +82,27 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
     static screengrabber: RecordingBox | undefined;
 }
 ScriptingGlobals.add(function toggleRecording(_readOnly_: boolean) {
-    console.log(_readOnly_)
-    console.log(RecordingBox.screengrabber)
     if (_readOnly_) return RecordingBox.screengrabber ? true : false;
     if (RecordingBox.screengrabber) {
-        //if recordingbox is true; when we press the stop button. changed vals temporarily to see if changes happening
-        console.log('grabbing screen!')
         RecordingBox.screengrabber.Pause?.();
         setTimeout(() => {
             RecordingBox.screengrabber?.Finish?.();
-            RecordingBox.screengrabber!.rootDoc.overlayX = 400; //was 100
+            RecordingBox.screengrabber!.rootDoc.overlayX = 100;
             RecordingBox.screengrabber!.rootDoc.overlayY = 100;
-            console.log(RecordingBox.screengrabber?.rootDoc)
             RecordingBox.screengrabber = undefined;
         }, 100);
-        Doc.UserDoc().isRecording = false;
-        // console.log(RecordingBox.screengrabber.dataDoc);
-        // Doc.AddDocToList(Doc.UserDoc(), "workspaceRecordings", RecordingBox.screengrabber.dataDoc);
-        Doc.AddDocToList(Doc.UserDoc(), "workspaceRecordings", RecordingBox.screengrabber.rootDoc);
-        // console.log(RecordingBox.screengrabber.rootDoc)
-        // console.log(RecordingBox.screengrabber.dataDoc.data?.valueOf);
     } else {
-        //when we first press mic
         const screengrabber = Docs.Create.WebCamDocument('', {
             _width: 384,
             _height: 216,
         });
-        // Doc.UserDoc().currentScreenGrab = screengrabber;
-        screengrabber.overlayX = 100; //was -400
-        screengrabber.overlayY = 100; //was 0
+        screengrabber.overlayX = -400;
+        screengrabber.overlayY = 0;
         screengrabber[Doc.LayoutFieldKey(screengrabber) + '_trackScreen'] = true;
-        Doc.AddToMyOverlay(screengrabber);  //just adds doc to overlay
+        Doc.AddToMyOverlay(screengrabber);
         DocumentManager.Instance.AddViewRenderedCb(screengrabber, docView => {
             RecordingBox.screengrabber = docView.ComponentView as RecordingBox;
             RecordingBox.screengrabber.Record?.();
         });
-        // Doc.AddDocToList(Doc.UserDoc(), "workspaceRecordings", screengrabber);
-        Doc.UserDoc().isRecording = true;
-        
     }
 }, 'toggle recording');
-ScriptingGlobals.add(function toggleRecPlayback(value: Doc) {
-    console.log(value)
-    const screenvid = Docs.Create.VideoDocument('', {_width: 384, _height: 216});
-    
-    let ffView = Array.from(DocumentManager.Instance.DocumentViews).find(view => view.ComponentView instanceof CollectionFreeFormView);
-    (ffView?.ComponentView as CollectionFreeFormView).props.addDocument?.(value); 
-
-});
-
-ScriptingGlobals.add(function getCurrRecording() {
-    console.log(RecordingBox.screengrabber);
-});
-ScriptingGlobals.add(function getWorkspaceRecordings() {
-    return Doc.UserDoc().workspaceRecordings
-});
-ScriptingGlobals.add(function getIsRecording() {
-    return Doc.UserDoc().isRecording;
-})
-
-
-
diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx
index 8c0f5efef..0e386b093 100644
--- a/src/client/views/nodes/RecordingBox/RecordingView.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx
@@ -163,9 +163,8 @@ export function RecordingView(props: IRecordingViewProps) {
     // if this is called, then we're done recording all the segments
     const finish = () => {
         // call stop on the video recorder if active
-        console.log(videoRecorder.current?.state);
         videoRecorder.current?.state !== 'inactive' && videoRecorder.current?.stop();
-        console.log("this it")
+
         // end the streams (audio/video) to remove recording icon
         const stream = videoElementRef.current!.srcObject;
         stream instanceof MediaStream && stream.getTracks().forEach(track => track.stop());
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 5de51dbe9..aa514be3b 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -365,7 +365,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @undoBatch
     @action
-    startRecording =    (e: React.MouseEvent, activeItem: Doc) => {
+    startRecording = (e: React.MouseEvent, activeItem: Doc) => {
         e.stopPropagation();
         if (PresElementBox.videoIsRecorded(activeItem)) {
             // if we already have an existing recording
-- 
cgit v1.2.3-70-g09d2


From 5e8cc0a6a7f8dfc0c33fbdfa8879de88f057233e Mon Sep 17 00:00:00 2001
From: monoguitari <113245090+monoguitari@users.noreply.github.com>
Date: Sat, 19 Aug 2023 08:39:45 -0400
Subject: Revert "Revert "Toggling icon for mic and added webcapturedoc to
 dropdown""

This reverts commit 2466587655888d5894159b942f09661ab73ebe66.
---
 src/client/util/CurrentUserUtils.ts                |  8 +++-
 src/client/views/UndoStack.tsx                     |  4 +-
 src/client/views/nodes/FontIconBox/FontIconBox.tsx |  4 +-
 .../views/nodes/RecordingBox/RecordingBox.tsx      | 47 ++++++++++++++++++++--
 .../views/nodes/RecordingBox/RecordingView.tsx     |  3 +-
 src/client/views/nodes/trails/PresElementBox.tsx   |  2 +-
 6 files changed, 56 insertions(+), 12 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 25c8f511b..54828867e 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -714,7 +714,9 @@ export class CurrentUserUtils {
             { title: "View",    icon: "View",          toolTip: "View tools",           subMenu: CurrentUserUtils.viewTools(),   expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available
             { title: "Web",     icon: "Web",           toolTip: "Web functions",        subMenu: CurrentUserUtils.webTools(),    expertMode: false, toolType:DocumentType.WEB,            funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Web is selected
             { title: "Schema",  icon: "Schema",linearBtnWidth:58,toolTip: "Schema functions",subMenu: CurrentUserUtils.schemaTools(), expertMode: false, toolType:CollectionViewType.Schema,   funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Schema is selected
-            { title: "Audio",   icon: "microphone",    toolTip: "Dictate",              btnType: ButtonType.ToggleButton,        expertMode: false,                                                                                                          ignoreClick: true,             scripts: { onClick:  'return toggleRecording(_readOnly_)'}, funcs: { }}
+            { title: "Audio",   icon: 'microphone',    toolTip: "Dictate",              btnType: ButtonType.ToggleButton,        expertMode: false,                                       funcs: {hidden: `getIsRecording()`},                                                                  ignoreClick: true,             scripts: { onClick:  'return toggleRecording(_readOnly_)'},},
+            { title: "StopRec", icon: "stop",          toolTip: "Stop",                 btnType: ButtonType.ToggleButton,        expertMode: false,                                       funcs: {hidden: `!getIsRecording()`},                                                                  ignoreClick: true,             scripts: { onClick: `return toggleRecording(_readOnly_)`}},
+            { title: "Dropdown",                       toolTip: "Workspace Recordings", btnType: ButtonType.DropdownList,        expertMode: false,                                       funcs: {btnList: `getWorkspaceRecordings()`},                                                         ignoreClick: true,             scripts: { script: `toggleRecPlayback(value)`}, },
         ];
     }
 
@@ -737,6 +739,8 @@ export class CurrentUserUtils {
     static setupContextMenuBtn(params:Button, menuDoc:Doc):Doc {
         const menuBtnDoc = DocListCast(menuDoc?.data).find(doc => doc.title === params.title);
         const subMenu = params.subMenu;
+        Doc.UserDoc().workspaceRecordings = new List<Doc>;
+        Doc.UserDoc().isRecording = false;
         if (!subMenu) { // button does not have a sub menu
             return this.setupContextMenuButton(params, menuBtnDoc);
         }  
@@ -999,4 +1003,4 @@ ScriptingGlobals.add(function createNewPresentation() { return MainView.Instance
 ScriptingGlobals.add(function openPresentation(pres:Doc) { return MainView.Instance.openPresentation(pres); }, "creates a new presentation when called");
 ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.createNewFolder(); }, "creates a new folder in myFiles when called");
 ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); }, "imports files from device directly into the import sidebar");
-ScriptingGlobals.add(function setInkToolDefaults() { Doc.ActiveTool = InkTool.None; });
\ No newline at end of file
+ScriptingGlobals.add(function setInkToolDefaults() { Doc.ActiveTool = InkTool.None; });
diff --git a/src/client/views/UndoStack.tsx b/src/client/views/UndoStack.tsx
index a551e5332..813cdd050 100644
--- a/src/client/views/UndoStack.tsx
+++ b/src/client/views/UndoStack.tsx
@@ -37,7 +37,7 @@ export class UndoStack extends React.Component<UndoStackProps> {
                             }}>
                             {UndoManager.undoStackNames.map((name, i) => (
                                 <div className="undoStack-resultContainer" key={i}>
-                                    <div className="undoStack-commandString">{name.replace(/[^\.]*\./, '')}</div>
+                                    <div className="undoStack-commandString">{StrCast(name).replace(/[^\.]*\./, '')}</div>
                                 </div>
                             ))}
                             {Array.from(UndoManager.redoStackNames)
@@ -45,7 +45,7 @@ export class UndoStack extends React.Component<UndoStackProps> {
                                 .map((name, i) => (
                                     <div className="undoStack-resultContainer" key={i}>
                                         <div className="undoStack-commandString" style={{ fontWeight: 'bold', color: 'red' }}>
-                                            {name.replace(/[^\.]*\./, '')}
+                                            {StrCast(name).replace(/[^\.]*\./, '')}
                                         </div>
                                     </div>
                                 ))}
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index 5ff5f7bfa..41ad90155 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -6,7 +6,7 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc';
 import { ScriptField } from '../../../../fields/ScriptField';
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
+import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
 import { SelectionManager } from '../../../util/SelectionManager';
 import { undoable, UndoManager } from '../../../util/UndoManager';
@@ -242,7 +242,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         const list: IListItemProps[] = this.buttonList
             .filter(value => !Doc.noviceMode || !noviceList.length || noviceList.includes(value))
             .map(value => ({
-                text: value.charAt(0).toUpperCase() + value.slice(1),
+                text: value === "string" ? value.charAt(0).toUpperCase() + value.slice(1) : StrCast(DocCast(value)?.title),
                 val: value,
                 style: getStyle(value),
                 onClick: undoable(() => script.script.run({ this: this.layoutDoc, self: this.rootDoc, value }), value),
diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
index 8fa2861b6..3237ce6cd 100644
--- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
@@ -15,6 +15,8 @@ import { BoolCast, DocCast } from '../../../../fields/Types';
 import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
 import { DocumentManager } from '../../../util/DocumentManager';
 import { Docs } from '../../../documents/Documents';
+import { CollectionFreeFormView } from '../../collections/collectionFreeForm/CollectionFreeFormView';
+import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
 
 @observer
 export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -82,27 +84,64 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
     static screengrabber: RecordingBox | undefined;
 }
 ScriptingGlobals.add(function toggleRecording(_readOnly_: boolean) {
+    console.log(_readOnly_)
+    console.log(RecordingBox.screengrabber)
     if (_readOnly_) return RecordingBox.screengrabber ? true : false;
     if (RecordingBox.screengrabber) {
+        //if recordingbox is true; when we press the stop button. changed vals temporarily to see if changes happening
+        console.log('grabbing screen!')
         RecordingBox.screengrabber.Pause?.();
         setTimeout(() => {
             RecordingBox.screengrabber?.Finish?.();
-            RecordingBox.screengrabber!.rootDoc.overlayX = 100;
+            RecordingBox.screengrabber!.rootDoc.overlayX = 400; //was 100
             RecordingBox.screengrabber!.rootDoc.overlayY = 100;
+            console.log(RecordingBox.screengrabber?.rootDoc)
             RecordingBox.screengrabber = undefined;
         }, 100);
+        Doc.UserDoc().isRecording = false;
+        // console.log(RecordingBox.screengrabber.dataDoc);
+        // Doc.AddDocToList(Doc.UserDoc(), "workspaceRecordings", RecordingBox.screengrabber.dataDoc);
+        Doc.AddDocToList(Doc.UserDoc(), "workspaceRecordings", RecordingBox.screengrabber.rootDoc);
+        // console.log(RecordingBox.screengrabber.rootDoc)
+        // console.log(RecordingBox.screengrabber.dataDoc.data?.valueOf);
     } else {
+        //when we first press mic
         const screengrabber = Docs.Create.WebCamDocument('', {
             _width: 384,
             _height: 216,
         });
-        screengrabber.overlayX = -400;
-        screengrabber.overlayY = 0;
+        // Doc.UserDoc().currentScreenGrab = screengrabber;
+        screengrabber.overlayX = 100; //was -400
+        screengrabber.overlayY = 100; //was 0
         screengrabber[Doc.LayoutFieldKey(screengrabber) + '_trackScreen'] = true;
-        Doc.AddToMyOverlay(screengrabber);
+        Doc.AddToMyOverlay(screengrabber);  //just adds doc to overlay
         DocumentManager.Instance.AddViewRenderedCb(screengrabber, docView => {
             RecordingBox.screengrabber = docView.ComponentView as RecordingBox;
             RecordingBox.screengrabber.Record?.();
         });
+        // Doc.AddDocToList(Doc.UserDoc(), "workspaceRecordings", screengrabber);
+        Doc.UserDoc().isRecording = true;
+        
     }
 }, 'toggle recording');
+ScriptingGlobals.add(function toggleRecPlayback(value: Doc) {
+    console.log(value)
+    const screenvid = Docs.Create.VideoDocument('', {_width: 384, _height: 216});
+    
+    let ffView = Array.from(DocumentManager.Instance.DocumentViews).find(view => view.ComponentView instanceof CollectionFreeFormView);
+    (ffView?.ComponentView as CollectionFreeFormView).props.addDocument?.(value); 
+
+});
+
+ScriptingGlobals.add(function getCurrRecording() {
+    console.log(RecordingBox.screengrabber);
+});
+ScriptingGlobals.add(function getWorkspaceRecordings() {
+    return Doc.UserDoc().workspaceRecordings
+});
+ScriptingGlobals.add(function getIsRecording() {
+    return Doc.UserDoc().isRecording;
+})
+
+
+
diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx
index 0e386b093..8c0f5efef 100644
--- a/src/client/views/nodes/RecordingBox/RecordingView.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx
@@ -163,8 +163,9 @@ export function RecordingView(props: IRecordingViewProps) {
     // if this is called, then we're done recording all the segments
     const finish = () => {
         // call stop on the video recorder if active
+        console.log(videoRecorder.current?.state);
         videoRecorder.current?.state !== 'inactive' && videoRecorder.current?.stop();
-
+        console.log("this it")
         // end the streams (audio/video) to remove recording icon
         const stream = videoElementRef.current!.srcObject;
         stream instanceof MediaStream && stream.getTracks().forEach(track => track.stop());
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index aa514be3b..5de51dbe9 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -365,7 +365,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @undoBatch
     @action
-    startRecording = (e: React.MouseEvent, activeItem: Doc) => {
+    startRecording =    (e: React.MouseEvent, activeItem: Doc) => {
         e.stopPropagation();
         if (PresElementBox.videoIsRecorded(activeItem)) {
             // if we already have an existing recording
-- 
cgit v1.2.3-70-g09d2


From 40a39db25646b513c25554a2abdf35ef977500a8 Mon Sep 17 00:00:00 2001
From: monoguitari <113245090+monoguitari@users.noreply.github.com>
Date: Sat, 19 Aug 2023 08:52:33 -0400
Subject: Revert "Revert "Revert "Toggling icon for mic and added webcapturedoc
 to dropdown"""

This reverts commit 5e8cc0a6a7f8dfc0c33fbdfa8879de88f057233e.
---
 src/client/util/CurrentUserUtils.ts                |  8 +---
 src/client/views/UndoStack.tsx                     |  4 +-
 src/client/views/nodes/FontIconBox/FontIconBox.tsx |  4 +-
 .../views/nodes/RecordingBox/RecordingBox.tsx      | 47 ++--------------------
 .../views/nodes/RecordingBox/RecordingView.tsx     |  3 +-
 src/client/views/nodes/trails/PresElementBox.tsx   |  2 +-
 6 files changed, 12 insertions(+), 56 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 54828867e..25c8f511b 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -714,9 +714,7 @@ export class CurrentUserUtils {
             { title: "View",    icon: "View",          toolTip: "View tools",           subMenu: CurrentUserUtils.viewTools(),   expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available
             { title: "Web",     icon: "Web",           toolTip: "Web functions",        subMenu: CurrentUserUtils.webTools(),    expertMode: false, toolType:DocumentType.WEB,            funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Web is selected
             { title: "Schema",  icon: "Schema",linearBtnWidth:58,toolTip: "Schema functions",subMenu: CurrentUserUtils.schemaTools(), expertMode: false, toolType:CollectionViewType.Schema,   funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Schema is selected
-            { title: "Audio",   icon: 'microphone',    toolTip: "Dictate",              btnType: ButtonType.ToggleButton,        expertMode: false,                                       funcs: {hidden: `getIsRecording()`},                                                                  ignoreClick: true,             scripts: { onClick:  'return toggleRecording(_readOnly_)'},},
-            { title: "StopRec", icon: "stop",          toolTip: "Stop",                 btnType: ButtonType.ToggleButton,        expertMode: false,                                       funcs: {hidden: `!getIsRecording()`},                                                                  ignoreClick: true,             scripts: { onClick: `return toggleRecording(_readOnly_)`}},
-            { title: "Dropdown",                       toolTip: "Workspace Recordings", btnType: ButtonType.DropdownList,        expertMode: false,                                       funcs: {btnList: `getWorkspaceRecordings()`},                                                         ignoreClick: true,             scripts: { script: `toggleRecPlayback(value)`}, },
+            { title: "Audio",   icon: "microphone",    toolTip: "Dictate",              btnType: ButtonType.ToggleButton,        expertMode: false,                                                                                                          ignoreClick: true,             scripts: { onClick:  'return toggleRecording(_readOnly_)'}, funcs: { }}
         ];
     }
 
@@ -739,8 +737,6 @@ export class CurrentUserUtils {
     static setupContextMenuBtn(params:Button, menuDoc:Doc):Doc {
         const menuBtnDoc = DocListCast(menuDoc?.data).find(doc => doc.title === params.title);
         const subMenu = params.subMenu;
-        Doc.UserDoc().workspaceRecordings = new List<Doc>;
-        Doc.UserDoc().isRecording = false;
         if (!subMenu) { // button does not have a sub menu
             return this.setupContextMenuButton(params, menuBtnDoc);
         }  
@@ -1003,4 +999,4 @@ ScriptingGlobals.add(function createNewPresentation() { return MainView.Instance
 ScriptingGlobals.add(function openPresentation(pres:Doc) { return MainView.Instance.openPresentation(pres); }, "creates a new presentation when called");
 ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.createNewFolder(); }, "creates a new folder in myFiles when called");
 ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); }, "imports files from device directly into the import sidebar");
-ScriptingGlobals.add(function setInkToolDefaults() { Doc.ActiveTool = InkTool.None; });
+ScriptingGlobals.add(function setInkToolDefaults() { Doc.ActiveTool = InkTool.None; });
\ No newline at end of file
diff --git a/src/client/views/UndoStack.tsx b/src/client/views/UndoStack.tsx
index 813cdd050..a551e5332 100644
--- a/src/client/views/UndoStack.tsx
+++ b/src/client/views/UndoStack.tsx
@@ -37,7 +37,7 @@ export class UndoStack extends React.Component<UndoStackProps> {
                             }}>
                             {UndoManager.undoStackNames.map((name, i) => (
                                 <div className="undoStack-resultContainer" key={i}>
-                                    <div className="undoStack-commandString">{StrCast(name).replace(/[^\.]*\./, '')}</div>
+                                    <div className="undoStack-commandString">{name.replace(/[^\.]*\./, '')}</div>
                                 </div>
                             ))}
                             {Array.from(UndoManager.redoStackNames)
@@ -45,7 +45,7 @@ export class UndoStack extends React.Component<UndoStackProps> {
                                 .map((name, i) => (
                                     <div className="undoStack-resultContainer" key={i}>
                                         <div className="undoStack-commandString" style={{ fontWeight: 'bold', color: 'red' }}>
-                                            {StrCast(name).replace(/[^\.]*\./, '')}
+                                            {name.replace(/[^\.]*\./, '')}
                                         </div>
                                     </div>
                                 ))}
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index 41ad90155..5ff5f7bfa 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -6,7 +6,7 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc';
 import { ScriptField } from '../../../../fields/ScriptField';
-import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
+import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
 import { SelectionManager } from '../../../util/SelectionManager';
 import { undoable, UndoManager } from '../../../util/UndoManager';
@@ -242,7 +242,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         const list: IListItemProps[] = this.buttonList
             .filter(value => !Doc.noviceMode || !noviceList.length || noviceList.includes(value))
             .map(value => ({
-                text: value === "string" ? value.charAt(0).toUpperCase() + value.slice(1) : StrCast(DocCast(value)?.title),
+                text: value.charAt(0).toUpperCase() + value.slice(1),
                 val: value,
                 style: getStyle(value),
                 onClick: undoable(() => script.script.run({ this: this.layoutDoc, self: this.rootDoc, value }), value),
diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
index 3237ce6cd..8fa2861b6 100644
--- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
@@ -15,8 +15,6 @@ import { BoolCast, DocCast } from '../../../../fields/Types';
 import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
 import { DocumentManager } from '../../../util/DocumentManager';
 import { Docs } from '../../../documents/Documents';
-import { CollectionFreeFormView } from '../../collections/collectionFreeForm/CollectionFreeFormView';
-import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
 
 @observer
 export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -84,64 +82,27 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
     static screengrabber: RecordingBox | undefined;
 }
 ScriptingGlobals.add(function toggleRecording(_readOnly_: boolean) {
-    console.log(_readOnly_)
-    console.log(RecordingBox.screengrabber)
     if (_readOnly_) return RecordingBox.screengrabber ? true : false;
     if (RecordingBox.screengrabber) {
-        //if recordingbox is true; when we press the stop button. changed vals temporarily to see if changes happening
-        console.log('grabbing screen!')
         RecordingBox.screengrabber.Pause?.();
         setTimeout(() => {
             RecordingBox.screengrabber?.Finish?.();
-            RecordingBox.screengrabber!.rootDoc.overlayX = 400; //was 100
+            RecordingBox.screengrabber!.rootDoc.overlayX = 100;
             RecordingBox.screengrabber!.rootDoc.overlayY = 100;
-            console.log(RecordingBox.screengrabber?.rootDoc)
             RecordingBox.screengrabber = undefined;
         }, 100);
-        Doc.UserDoc().isRecording = false;
-        // console.log(RecordingBox.screengrabber.dataDoc);
-        // Doc.AddDocToList(Doc.UserDoc(), "workspaceRecordings", RecordingBox.screengrabber.dataDoc);
-        Doc.AddDocToList(Doc.UserDoc(), "workspaceRecordings", RecordingBox.screengrabber.rootDoc);
-        // console.log(RecordingBox.screengrabber.rootDoc)
-        // console.log(RecordingBox.screengrabber.dataDoc.data?.valueOf);
     } else {
-        //when we first press mic
         const screengrabber = Docs.Create.WebCamDocument('', {
             _width: 384,
             _height: 216,
         });
-        // Doc.UserDoc().currentScreenGrab = screengrabber;
-        screengrabber.overlayX = 100; //was -400
-        screengrabber.overlayY = 100; //was 0
+        screengrabber.overlayX = -400;
+        screengrabber.overlayY = 0;
         screengrabber[Doc.LayoutFieldKey(screengrabber) + '_trackScreen'] = true;
-        Doc.AddToMyOverlay(screengrabber);  //just adds doc to overlay
+        Doc.AddToMyOverlay(screengrabber);
         DocumentManager.Instance.AddViewRenderedCb(screengrabber, docView => {
             RecordingBox.screengrabber = docView.ComponentView as RecordingBox;
             RecordingBox.screengrabber.Record?.();
         });
-        // Doc.AddDocToList(Doc.UserDoc(), "workspaceRecordings", screengrabber);
-        Doc.UserDoc().isRecording = true;
-        
     }
 }, 'toggle recording');
-ScriptingGlobals.add(function toggleRecPlayback(value: Doc) {
-    console.log(value)
-    const screenvid = Docs.Create.VideoDocument('', {_width: 384, _height: 216});
-    
-    let ffView = Array.from(DocumentManager.Instance.DocumentViews).find(view => view.ComponentView instanceof CollectionFreeFormView);
-    (ffView?.ComponentView as CollectionFreeFormView).props.addDocument?.(value); 
-
-});
-
-ScriptingGlobals.add(function getCurrRecording() {
-    console.log(RecordingBox.screengrabber);
-});
-ScriptingGlobals.add(function getWorkspaceRecordings() {
-    return Doc.UserDoc().workspaceRecordings
-});
-ScriptingGlobals.add(function getIsRecording() {
-    return Doc.UserDoc().isRecording;
-})
-
-
-
diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx
index 8c0f5efef..0e386b093 100644
--- a/src/client/views/nodes/RecordingBox/RecordingView.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx
@@ -163,9 +163,8 @@ export function RecordingView(props: IRecordingViewProps) {
     // if this is called, then we're done recording all the segments
     const finish = () => {
         // call stop on the video recorder if active
-        console.log(videoRecorder.current?.state);
         videoRecorder.current?.state !== 'inactive' && videoRecorder.current?.stop();
-        console.log("this it")
+
         // end the streams (audio/video) to remove recording icon
         const stream = videoElementRef.current!.srcObject;
         stream instanceof MediaStream && stream.getTracks().forEach(track => track.stop());
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 5de51dbe9..aa514be3b 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -365,7 +365,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @undoBatch
     @action
-    startRecording =    (e: React.MouseEvent, activeItem: Doc) => {
+    startRecording = (e: React.MouseEvent, activeItem: Doc) => {
         e.stopPropagation();
         if (PresElementBox.videoIsRecorded(activeItem)) {
             // if we already have an existing recording
-- 
cgit v1.2.3-70-g09d2


From ec6a5f5d5684ca66cd21bb8fef91c86852dd126f Mon Sep 17 00:00:00 2001
From: monoguitari <113245090+monoguitari@users.noreply.github.com>
Date: Sat, 19 Aug 2023 08:53:11 -0400
Subject: Revert "Revert "Revert "Revert "Toggling icon for mic and added
 webcapturedoc to dropdown""""

This reverts commit 40a39db25646b513c25554a2abdf35ef977500a8.
---
 src/client/util/CurrentUserUtils.ts                |  8 +++-
 src/client/views/UndoStack.tsx                     |  4 +-
 src/client/views/nodes/FontIconBox/FontIconBox.tsx |  4 +-
 .../views/nodes/RecordingBox/RecordingBox.tsx      | 47 ++++++++++++++++++++--
 .../views/nodes/RecordingBox/RecordingView.tsx     |  3 +-
 src/client/views/nodes/trails/PresElementBox.tsx   |  2 +-
 6 files changed, 56 insertions(+), 12 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 25c8f511b..54828867e 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -714,7 +714,9 @@ export class CurrentUserUtils {
             { title: "View",    icon: "View",          toolTip: "View tools",           subMenu: CurrentUserUtils.viewTools(),   expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available
             { title: "Web",     icon: "Web",           toolTip: "Web functions",        subMenu: CurrentUserUtils.webTools(),    expertMode: false, toolType:DocumentType.WEB,            funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Web is selected
             { title: "Schema",  icon: "Schema",linearBtnWidth:58,toolTip: "Schema functions",subMenu: CurrentUserUtils.schemaTools(), expertMode: false, toolType:CollectionViewType.Schema,   funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Schema is selected
-            { title: "Audio",   icon: "microphone",    toolTip: "Dictate",              btnType: ButtonType.ToggleButton,        expertMode: false,                                                                                                          ignoreClick: true,             scripts: { onClick:  'return toggleRecording(_readOnly_)'}, funcs: { }}
+            { title: "Audio",   icon: 'microphone',    toolTip: "Dictate",              btnType: ButtonType.ToggleButton,        expertMode: false,                                       funcs: {hidden: `getIsRecording()`},                                                                  ignoreClick: true,             scripts: { onClick:  'return toggleRecording(_readOnly_)'},},
+            { title: "StopRec", icon: "stop",          toolTip: "Stop",                 btnType: ButtonType.ToggleButton,        expertMode: false,                                       funcs: {hidden: `!getIsRecording()`},                                                                  ignoreClick: true,             scripts: { onClick: `return toggleRecording(_readOnly_)`}},
+            { title: "Dropdown",                       toolTip: "Workspace Recordings", btnType: ButtonType.DropdownList,        expertMode: false,                                       funcs: {btnList: `getWorkspaceRecordings()`},                                                         ignoreClick: true,             scripts: { script: `toggleRecPlayback(value)`}, },
         ];
     }
 
@@ -737,6 +739,8 @@ export class CurrentUserUtils {
     static setupContextMenuBtn(params:Button, menuDoc:Doc):Doc {
         const menuBtnDoc = DocListCast(menuDoc?.data).find(doc => doc.title === params.title);
         const subMenu = params.subMenu;
+        Doc.UserDoc().workspaceRecordings = new List<Doc>;
+        Doc.UserDoc().isRecording = false;
         if (!subMenu) { // button does not have a sub menu
             return this.setupContextMenuButton(params, menuBtnDoc);
         }  
@@ -999,4 +1003,4 @@ ScriptingGlobals.add(function createNewPresentation() { return MainView.Instance
 ScriptingGlobals.add(function openPresentation(pres:Doc) { return MainView.Instance.openPresentation(pres); }, "creates a new presentation when called");
 ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.createNewFolder(); }, "creates a new folder in myFiles when called");
 ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); }, "imports files from device directly into the import sidebar");
-ScriptingGlobals.add(function setInkToolDefaults() { Doc.ActiveTool = InkTool.None; });
\ No newline at end of file
+ScriptingGlobals.add(function setInkToolDefaults() { Doc.ActiveTool = InkTool.None; });
diff --git a/src/client/views/UndoStack.tsx b/src/client/views/UndoStack.tsx
index a551e5332..813cdd050 100644
--- a/src/client/views/UndoStack.tsx
+++ b/src/client/views/UndoStack.tsx
@@ -37,7 +37,7 @@ export class UndoStack extends React.Component<UndoStackProps> {
                             }}>
                             {UndoManager.undoStackNames.map((name, i) => (
                                 <div className="undoStack-resultContainer" key={i}>
-                                    <div className="undoStack-commandString">{name.replace(/[^\.]*\./, '')}</div>
+                                    <div className="undoStack-commandString">{StrCast(name).replace(/[^\.]*\./, '')}</div>
                                 </div>
                             ))}
                             {Array.from(UndoManager.redoStackNames)
@@ -45,7 +45,7 @@ export class UndoStack extends React.Component<UndoStackProps> {
                                 .map((name, i) => (
                                     <div className="undoStack-resultContainer" key={i}>
                                         <div className="undoStack-commandString" style={{ fontWeight: 'bold', color: 'red' }}>
-                                            {name.replace(/[^\.]*\./, '')}
+                                            {StrCast(name).replace(/[^\.]*\./, '')}
                                         </div>
                                     </div>
                                 ))}
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index 5ff5f7bfa..41ad90155 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -6,7 +6,7 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc';
 import { ScriptField } from '../../../../fields/ScriptField';
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
+import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
 import { SelectionManager } from '../../../util/SelectionManager';
 import { undoable, UndoManager } from '../../../util/UndoManager';
@@ -242,7 +242,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         const list: IListItemProps[] = this.buttonList
             .filter(value => !Doc.noviceMode || !noviceList.length || noviceList.includes(value))
             .map(value => ({
-                text: value.charAt(0).toUpperCase() + value.slice(1),
+                text: value === "string" ? value.charAt(0).toUpperCase() + value.slice(1) : StrCast(DocCast(value)?.title),
                 val: value,
                 style: getStyle(value),
                 onClick: undoable(() => script.script.run({ this: this.layoutDoc, self: this.rootDoc, value }), value),
diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
index 8fa2861b6..3237ce6cd 100644
--- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
@@ -15,6 +15,8 @@ import { BoolCast, DocCast } from '../../../../fields/Types';
 import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
 import { DocumentManager } from '../../../util/DocumentManager';
 import { Docs } from '../../../documents/Documents';
+import { CollectionFreeFormView } from '../../collections/collectionFreeForm/CollectionFreeFormView';
+import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
 
 @observer
 export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -82,27 +84,64 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
     static screengrabber: RecordingBox | undefined;
 }
 ScriptingGlobals.add(function toggleRecording(_readOnly_: boolean) {
+    console.log(_readOnly_)
+    console.log(RecordingBox.screengrabber)
     if (_readOnly_) return RecordingBox.screengrabber ? true : false;
     if (RecordingBox.screengrabber) {
+        //if recordingbox is true; when we press the stop button. changed vals temporarily to see if changes happening
+        console.log('grabbing screen!')
         RecordingBox.screengrabber.Pause?.();
         setTimeout(() => {
             RecordingBox.screengrabber?.Finish?.();
-            RecordingBox.screengrabber!.rootDoc.overlayX = 100;
+            RecordingBox.screengrabber!.rootDoc.overlayX = 400; //was 100
             RecordingBox.screengrabber!.rootDoc.overlayY = 100;
+            console.log(RecordingBox.screengrabber?.rootDoc)
             RecordingBox.screengrabber = undefined;
         }, 100);
+        Doc.UserDoc().isRecording = false;
+        // console.log(RecordingBox.screengrabber.dataDoc);
+        // Doc.AddDocToList(Doc.UserDoc(), "workspaceRecordings", RecordingBox.screengrabber.dataDoc);
+        Doc.AddDocToList(Doc.UserDoc(), "workspaceRecordings", RecordingBox.screengrabber.rootDoc);
+        // console.log(RecordingBox.screengrabber.rootDoc)
+        // console.log(RecordingBox.screengrabber.dataDoc.data?.valueOf);
     } else {
+        //when we first press mic
         const screengrabber = Docs.Create.WebCamDocument('', {
             _width: 384,
             _height: 216,
         });
-        screengrabber.overlayX = -400;
-        screengrabber.overlayY = 0;
+        // Doc.UserDoc().currentScreenGrab = screengrabber;
+        screengrabber.overlayX = 100; //was -400
+        screengrabber.overlayY = 100; //was 0
         screengrabber[Doc.LayoutFieldKey(screengrabber) + '_trackScreen'] = true;
-        Doc.AddToMyOverlay(screengrabber);
+        Doc.AddToMyOverlay(screengrabber);  //just adds doc to overlay
         DocumentManager.Instance.AddViewRenderedCb(screengrabber, docView => {
             RecordingBox.screengrabber = docView.ComponentView as RecordingBox;
             RecordingBox.screengrabber.Record?.();
         });
+        // Doc.AddDocToList(Doc.UserDoc(), "workspaceRecordings", screengrabber);
+        Doc.UserDoc().isRecording = true;
+        
     }
 }, 'toggle recording');
+ScriptingGlobals.add(function toggleRecPlayback(value: Doc) {
+    console.log(value)
+    const screenvid = Docs.Create.VideoDocument('', {_width: 384, _height: 216});
+    
+    let ffView = Array.from(DocumentManager.Instance.DocumentViews).find(view => view.ComponentView instanceof CollectionFreeFormView);
+    (ffView?.ComponentView as CollectionFreeFormView).props.addDocument?.(value); 
+
+});
+
+ScriptingGlobals.add(function getCurrRecording() {
+    console.log(RecordingBox.screengrabber);
+});
+ScriptingGlobals.add(function getWorkspaceRecordings() {
+    return Doc.UserDoc().workspaceRecordings
+});
+ScriptingGlobals.add(function getIsRecording() {
+    return Doc.UserDoc().isRecording;
+})
+
+
+
diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx
index 0e386b093..8c0f5efef 100644
--- a/src/client/views/nodes/RecordingBox/RecordingView.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx
@@ -163,8 +163,9 @@ export function RecordingView(props: IRecordingViewProps) {
     // if this is called, then we're done recording all the segments
     const finish = () => {
         // call stop on the video recorder if active
+        console.log(videoRecorder.current?.state);
         videoRecorder.current?.state !== 'inactive' && videoRecorder.current?.stop();
-
+        console.log("this it")
         // end the streams (audio/video) to remove recording icon
         const stream = videoElementRef.current!.srcObject;
         stream instanceof MediaStream && stream.getTracks().forEach(track => track.stop());
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index aa514be3b..5de51dbe9 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -365,7 +365,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @undoBatch
     @action
-    startRecording = (e: React.MouseEvent, activeItem: Doc) => {
+    startRecording =    (e: React.MouseEvent, activeItem: Doc) => {
         e.stopPropagation();
         if (PresElementBox.videoIsRecorded(activeItem)) {
             // if we already have an existing recording
-- 
cgit v1.2.3-70-g09d2


From 627276eb1c5816ffd3575e2f97a3f36a31f825fa Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 29 Aug 2023 01:33:30 -0400
Subject: fixed toggle button background.

---
 src/client/views/nodes/FontIconBox/FontIconBox.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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

diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index 64f289cf7..f286e5a42 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -343,7 +343,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 toggleStatus={toggleStatus}
                 text={buttonText}
                 color={color}
-                background={SettingsManager.userBackgroundColor}
+                //background={SettingsManager.userBackgroundColor}
                 icon={this.Icon(color)!}
                 label={this.label}
                 onPointerDown={() => script.script.run({ this: this.layoutDoc, self: this.rootDoc, value: !toggleStatus, _readOnly_: false })}
-- 
cgit v1.2.3-70-g09d2


From 0c4f57875c8aaf599ff111a8b8122895d2addab3 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 29 Aug 2023 16:33:37 -0400
Subject: more cleanup

---
 src/client/util/CurrentUserUtils.ts                |   4 +-
 src/client/util/DragManager.ts                     |   7 +-
 src/client/views/DocComponent.tsx                  |   2 +-
 src/client/views/MainView.tsx                      |   2 +-
 src/client/views/ScriptingRepl.tsx                 |  23 +++-
 src/client/views/nodes/DocumentIcon.tsx            |   2 +
 src/client/views/nodes/FontIconBox/FontIconBox.tsx |   2 +-
 .../views/nodes/RecordingBox/RecordingBox.tsx      | 139 ++++++++++++---------
 8 files changed, 105 insertions(+), 76 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index b705bde7f..51ef51508 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -715,14 +715,14 @@ export class CurrentUserUtils {
             { title: "View",    icon: "View",          toolTip: "View tools",           subMenu: CurrentUserUtils.viewTools(),   expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available
             { title: "Web",     icon: "Web",           toolTip: "Web functions",        subMenu: CurrentUserUtils.webTools(),    expertMode: false, toolType:DocumentType.WEB,            funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Web is selected
             { title: "Schema",  icon: "Schema",linearBtnWidth:58,toolTip: "Schema functions",subMenu: CurrentUserUtils.schemaTools(), expertMode: false, toolType:CollectionViewType.Schema,   funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Schema is selected
-            { title: "Video",   icon: 'video',    toolTip: "Dictate",              btnType: ButtonType.ToggleButton,        expertMode: false,                                       funcs: {hidden: `getIsRecording()`},                               ignoreClick: true,             scripts: { onClick:  'return toggleRecording(_readOnly_)'}},
+            { title: "Video",   icon: 'video',         toolTip: "Dictate",              btnType: ButtonType.ToggleButton,        expertMode: false,                                       funcs: {hidden: `getIsRecording()`},                               ignoreClick: true,             scripts: { onClick:  'return toggleRecording(_readOnly_)'}},
             { title: "StopRec", icon: "stop",          toolTip: "Stop",                 btnType: ButtonType.ToggleButton,        expertMode: false,                                       funcs: {hidden: `!getIsRecording()`},                              ignoreClick: true,             scripts: { onClick: `return toggleRecording(_readOnly_)`}},
             { title: "Dropdown",                       toolTip: "Workspace Recordings", btnType: ButtonType.DropdownList,        expertMode: false,                                       funcs: {hidden: `renderDropdown()`, btnList: `getWorkspaceRecordings()`},                      ignoreClick: true,             scripts: { script: `toggleRecPlayback(value)`}},
             { title: "Play Rec",icon: "play",          toolTip: "Play recording",       btnType: ButtonType.ToggleButton,        expertMode: false,                                       funcs: {hidden: `!getIsWorkspaceRecPlaying()`},                            ignoreClick: true,             scripts: { onClick: `return playWorkspaceRec(getCurrentRecording())`}},
             { title: "Pause Rec",icon: "pause",        toolTip: "Pause recording",      btnType: ButtonType.ToggleButton,        expertMode: false,                                       funcs: {hidden: `!getIsWorkspaceRecPaused()`},                            ignoreClick: true,             scripts: { onClick: `return pauseWorkspaceRec(getCurrentRecording())`}},
             { title: "Stop Rec", icon: "stop",         toolTip: "Stop recording",       btnType: ButtonType.ToggleButton,        expertMode: false,                                       funcs: {hidden: `!getIsRecPlayback()`},                            ignoreClick: true,             scripts: { onClick: `return closeWorkspaceRec(getCurrentRecording())`}},
             { title: "Add doc", icon: "down",          toolTip: "add to doc",           btnType: ButtonType.ToggleButton,        expertMode: false,                                       funcs: {hidden: `!getIsRecPlayback()`},                            ignoreClick: true,             scripts: { onClick: `addRectoWorkspace(getCurrentRecording())`}},
-            { title: "Delete Rec", icon: "trash",    toolTip: "delete selected recording", btnType: ButtonType.ToggleButton,   expertMode: false,                                       funcs: {hidden: `!getIsRecPlayback()`},                                                         ignoreClick: true,             scripts: { onClick: `removeWorkspaceRec(getCurrentRecording())`}}
+            { title: "Delete Rec", icon: "trash",      toolTip: "delete selected recording", btnType: ButtonType.ToggleButton,   expertMode: false,                                       funcs: {hidden: `!getIsRecPlayback()`},                                                         ignoreClick: true,             scripts: { onClick: `removeWorkspaceRec(getCurrentRecording())`}}
         ];
     }
 
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 5a11f2dca..065f17139 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -4,7 +4,7 @@ import { Doc, Field, Opt, StrListCast } from '../../fields/Doc';
 import { List } from '../../fields/List';
 import { PrefetchProxy } from '../../fields/Proxy';
 import { ScriptField } from '../../fields/ScriptField';
-import { BoolCast, Cast, ScriptCast, StrCast } from '../../fields/Types';
+import { BoolCast, Cast, DocCast, ScriptCast, StrCast } from '../../fields/Types';
 import { emptyFunction, Utils } from '../../Utils';
 import { Docs, DocUtils } from '../documents/Documents';
 import * as globalCssVariables from '../views/global/globalCssVariables.scss';
@@ -236,8 +236,7 @@ export namespace DragManager {
         return true;
     }
 
-    export function StartDropdownDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, recordingIndex: number, options?: DragOptions, dropEvent?: () => any,) {
-        
+    export function StartDropdownDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, recordingIndex: number, options?: DragOptions, dropEvent?: () => any) {
         const addAudioTag = (dropDoc: any) => {
             dropDoc && !dropDoc.author_date && (dropDoc.author_date = new DateField());
             dropDoc instanceof Doc && DocUtils.MakeLinkToActiveAudio(() => dropDoc);
@@ -245,7 +244,7 @@ export namespace DragManager {
         };
         const finishDrag = async (e: DragCompleteEvent) => {
             Doc.UserDoc().isAddRecToDocMode = false;
-            Doc.RemFromMyOverlay(Doc.UserDoc().currentRecording);
+            Doc.RemFromMyOverlay(DocCast(Doc.UserDoc().currentRecording));
             Doc.UserDoc().currentRecording = undefined;
             Doc.UserDoc().isRecPlayback = false;
             Doc.UserDoc().isWorkspaceRecPlaying = false;
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index e076e69ca..6a1efdf9c 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -201,7 +201,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                     const annoDocs = targetDataDoc[annotationKey ?? this.annotationKey] as List<Doc>;
                     if (annoDocs instanceof List) annoDocs.push(...added.filter(add => !annoDocs.includes(add)));
                     else targetDataDoc[annotationKey ?? this.annotationKey] = new List<Doc>(added);
-                    targetDataDoc[(annotationKey ?? this.annotationKey) + '_modificationDate'] = new DateField(new Date(Date.now()));
+                    targetDataDoc[(annotationKey ?? this.annotationKey) + '_modificationDate'] = new DateField();
                 }
             }
             return true;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index dde04dcc0..01a26559c 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -902,7 +902,7 @@ export class MainView extends React.Component {
                     childFiltersByRanges={returnEmptyFilter}
                     searchFilterDocs={returnEmptyDoclist}
                 />
-                {['watching', 'recording'].includes(String(this.userDoc?.presentationMode) ?? '') ? <div style={{ border: '.5rem solid green', padding: '5px' }}>{StrCast(this.userDoc?.presentationMode)}</div> : <></>}
+                {['watching', 'recording'].includes(StrCast(this.userDoc?.presentationMode)) ? <div style={{ border: '.5rem solid green', padding: '5px' }}>{StrCast(this.userDoc?.presentationMode)}</div> : <></>}
             </div>
         );
     }
diff --git a/src/client/views/ScriptingRepl.tsx b/src/client/views/ScriptingRepl.tsx
index 2bc2d5e6b..9966dbced 100644
--- a/src/client/views/ScriptingRepl.tsx
+++ b/src/client/views/ScriptingRepl.tsx
@@ -5,6 +5,7 @@ import * as React from 'react';
 import { DocumentManager } from '../util/DocumentManager';
 import { CompileScript, Transformer, ts } from '../util/Scripting';
 import { ScriptingGlobals } from '../util/ScriptingGlobals';
+import { SettingsManager } from '../util/SettingsManager';
 import { undoable } from '../util/UndoManager';
 import { DocumentIconContainer } from './nodes/DocumentIcon';
 import { OverlayView } from './OverlayView';
@@ -245,17 +246,29 @@ export class ScriptingRepl extends React.Component {
     render() {
         return (
             <div className="scriptingRepl-outerContainer">
-                <div className="scriptingRepl-commandsContainer" ref={this.commandsRef}>
+                <div className="scriptingRepl-commandsContainer" style={{ background: SettingsManager.userBackgroundColor }} ref={this.commandsRef}>
                     {this.commands.map(({ command, result }, i) => {
                         return (
-                            <div className="scriptingRepl-resultContainer" key={i}>
-                                <div className="scriptingRepl-commandString">{command || <br />}</div>
-                                <div className="scriptingRepl-commandResult">{<ScriptingValueDisplay scrollToBottom={this.maybeScrollToBottom} value={result} />}</div>
+                            <div className="scriptingRepl-resultContainer" style={{ background: SettingsManager.userBackgroundColor }} key={i}>
+                                <div className="scriptingRepl-commandString" style={{ background: SettingsManager.userBackgroundColor }}>
+                                    {command || <br />}
+                                </div>
+                                <div className="scriptingRepl-commandResult" style={{ background: SettingsManager.userBackgroundColor }}>
+                                    {<ScriptingValueDisplay scrollToBottom={this.maybeScrollToBottom} value={result} />}
+                                </div>
                             </div>
                         );
                     })}
                 </div>
-                <input className="scriptingRepl-commandInput" onFocus={this.onFocus} onBlur={this.onBlur} value={this.commandString} onChange={this.onChange} onKeyDown={this.onKeyDown}></input>
+                <input
+                    className="scriptingRepl-commandInput"
+                    style={{ background: SettingsManager.userBackgroundColor }} //
+                    onFocus={this.onFocus}
+                    onBlur={this.onBlur}
+                    value={this.commandString}
+                    onChange={this.onChange}
+                    onKeyDown={this.onKeyDown}
+                />
             </div>
         );
     }
diff --git a/src/client/views/nodes/DocumentIcon.tsx b/src/client/views/nodes/DocumentIcon.tsx
index 6e2ed72b8..bccbd66e8 100644
--- a/src/client/views/nodes/DocumentIcon.tsx
+++ b/src/client/views/nodes/DocumentIcon.tsx
@@ -9,6 +9,7 @@ import { action, observable } from 'mobx';
 import { Id } from '../../../fields/FieldSymbols';
 import { factory } from 'typescript';
 import { LightboxView } from '../LightboxView';
+import { SettingsManager } from '../../util/SettingsManager';
 
 @observer
 export class DocumentIcon extends React.Component<{ view: DocumentView; index: number }> {
@@ -29,6 +30,7 @@ export class DocumentIcon extends React.Component<{ view: DocumentView; index: n
                     pointerEvents: 'all',
                     opacity: this._hovered ? 0.3 : 1,
                     position: 'absolute',
+                    background: SettingsManager.userBackgroundColor,
                     transform: `translate(${(left + right) / 2}px, ${top}px)`,
                 }}>
                 <Tooltip title={<>{this.props.view.rootDoc.title}</>}>
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index 1cf66fbdc..bb2069cc9 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -235,7 +235,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         const list: IListItemProps[] = this.buttonList
             .filter(value => !Doc.noviceMode || !noviceList.length || noviceList.includes(value))
             .map(value => ({
-                text: value === "string" ? value.charAt(0).toUpperCase() + value.slice(1) : StrCast(DocCast(value)?.title),
+                text: typeof value === 'string' ? value.charAt(0).toUpperCase() + value.slice(1) : StrCast(DocCast(value)?.title),
                 val: value,
                 style: getStyle(value),
                 onClick: undoable(() => script.script.run({ this: this.layoutDoc, self: this.rootDoc, value }), value),
diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
index 8a91b8102..9dd673591 100644
--- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
@@ -1,32 +1,27 @@
-import { action, observable, runInAction } from 'mobx';
+import { action, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { VideoField } from '../../../../fields/URLField';
-import { Upload } from '../../../../server/SharedMediaTypes';
-import { ViewBoxBaseComponent } from '../../DocComponent';
-import { FieldView, FieldViewProps } from '../FieldView';
-import { VideoBox } from '../VideoBox';
-import { RecordingView } from './RecordingView';
-import { DocumentType } from '../../../documents/DocumentTypes';
-import { Presentation } from '../../../util/TrackMovements';
+import { formatTime } from '../../../../Utils';
 import { Doc, DocListCast } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
+import { listSpec } from '../../../../fields/Schema';
 import { BoolCast, Cast, DocCast } from '../../../../fields/Types';
-import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
-import { DocumentManager } from '../../../util/DocumentManager';
+import { VideoField } from '../../../../fields/URLField';
+import { Upload } from '../../../../server/SharedMediaTypes';
 import { Docs } from '../../../documents/Documents';
-import { CollectionFreeFormView } from '../../collections/collectionFreeForm/CollectionFreeFormView';
-import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
-import { DocumentView } from '../DocumentView';
-import { SettingsManager } from '../../../util/SettingsManager';
-import { PropertiesView } from '../../PropertiesView';
-import { PropertiesSection } from '../../PropertiesSection';
-import { PropertiesDocContextSelector } from '../../PropertiesDocContextSelector';
-import { listSpec } from '../../../../fields/Schema';
+import { DocumentType } from '../../../documents/DocumentTypes';
+import { DocumentManager } from '../../../util/DocumentManager';
 import { DragManager } from '../../../util/DragManager';
+import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
 import { SelectionManager } from '../../../util/SelectionManager';
-import { AudioBox } from '../AudioBox';
-import { UndoManager, undoBatch } from '../../../util/UndoManager';
+import { Presentation } from '../../../util/TrackMovements';
+import { undoBatch } from '../../../util/UndoManager';
+import { CollectionFreeFormView } from '../../collections/collectionFreeForm/CollectionFreeFormView';
+import { ViewBoxBaseComponent } from '../../DocComponent';
+import { FieldView, FieldViewProps } from '../FieldView';
+import { VideoBox } from '../VideoBox';
+import { RecordingView } from './RecordingView';
+import { DateField } from '../../../../fields/DateField';
 
 @observer
 export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -69,8 +64,8 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     /**
      * This method toggles whether or not we are currently using the RecordingBox to record with the topbar button
-     * @param _readOnly_ 
-     * @returns 
+     * @param _readOnly_
+     * @returns
      */
     @undoBatch
     @action
@@ -78,33 +73,33 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (_readOnly_) return RecordingBox.screengrabber ? true : false;
         if (RecordingBox.screengrabber) {
             //if recordingbox is true; when we press the stop button. changed vals temporarily to see if changes happening
-            console.log('grabbing screen!')
+            console.log('grabbing screen!');
             RecordingBox.screengrabber.Pause?.();
             const remDoc = RecordingBox.screengrabber.rootDoc;
             setTimeout(() => {
                 RecordingBox.screengrabber?.Finish?.();
                 RecordingBox.screengrabber!.rootDoc.overlayX = 70; //was 100
                 RecordingBox.screengrabber!.rootDoc.overlayY = 590;
-                console.log(RecordingBox.screengrabber)
+                console.log(RecordingBox.screengrabber);
                 RecordingBox.screengrabber = undefined;
             }, 100);
             //could break if recording takes too long to turn into videobox. If so, either increase time on setTimeout below or find diff place to do this
             setTimeout(() => {
                 Doc.RemFromMyOverlay(remDoc);
-    
-            }, 1000)
+            }, 1000);
             Doc.UserDoc().isRecording = false;
-            Doc.AddDocToList(Doc.UserDoc(), "workspaceRecordings", RecordingBox.screengrabber.rootDoc);
+            Doc.AddDocToList(Doc.UserDoc(), 'workspaceRecordings', RecordingBox.screengrabber.rootDoc);
         } else {
             //when we first press mic
             const screengrabber = Docs.Create.WebCamDocument('', {
+                title: `${Doc.ActiveDashboard?.title ?? ''} ${new DateField()}`,
                 _width: 205,
                 _height: 115,
             });
             screengrabber.overlayX = 70; //was -400
             screengrabber.overlayY = 590; //was 0
             screengrabber[Doc.LayoutFieldKey(screengrabber) + '_trackScreen'] = true;
-            Doc.AddToMyOverlay(screengrabber);  //just adds doc to overlay
+            Doc.AddToMyOverlay(screengrabber); //just adds doc to overlay
             DocumentManager.Instance.AddViewRenderedCb(screengrabber, docView => {
                 RecordingBox.screengrabber = docView.ComponentView as RecordingBox;
                 RecordingBox.screengrabber.Record?.();
@@ -120,21 +115,21 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @undoBatch
     @action
     public static toggleWorkspaceRecPlayback(value: Doc) {
-        let docval = undefined;
         Doc.UserDoc().currentRecording = value;
-        console.log(value)
-        value.overlayX =70;
+        console.log(value);
+        value.overlayX = 70;
         value.overlayY = 590;
         if (!Doc.UserDoc().isAddRecToDocMode) {
             Doc.UserDoc().isRecPlayback = true;
             Doc.UserDoc().isWorkspaceRecPlaying = true;
             Doc.AddToMyOverlay(value);
             DocumentManager.Instance.AddViewRenderedCb(value, docView => {
-                Doc.UserDoc().currentRecording = docView.ComponentView as RecordingBox;
-                SelectionManager.SelectSchemaViewDoc(value);    
-        })} else {
-            let recordingIndex = Array.from(Doc.UserDoc().workspaceRecordings).indexOf(value);
-            DragManager.StartDropdownDrag([document.createElement('div')],new DragManager.DocumentDragData([value]), 1, 1, recordingIndex);
+                Doc.UserDoc().currentRecording = docView.rootDoc;
+                SelectionManager.SelectSchemaViewDoc(value);
+            });
+        } else {
+            let recordingIndex = DocListCast(Doc.UserDoc().workspaceRecordings).indexOf(value);
+            DragManager.StartDropdownDrag([document.createElement('div')], new DragManager.DocumentDragData([value]), 1, 1, recordingIndex);
         }
     }
 
@@ -145,11 +140,11 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @undoBatch
     @action
     public static addRecToWorkspace(value: RecordingBox) {
-        console.log("adding rec to doc");
+        console.log('adding rec to doc');
         console.log(value);
-         let ffView = Array.from(DocumentManager.Instance.DocumentViews).find(view => view.ComponentView instanceof CollectionFreeFormView);
+        let ffView = Array.from(DocumentManager.Instance.DocumentViews).find(view => view.ComponentView instanceof CollectionFreeFormView);
         (ffView?.ComponentView as CollectionFreeFormView).props.addDocument?.(value.rootDoc);
-        let recordingIndex = Array.from(Doc.UserDoc().workspaceRecordings).indexOf(value.rootDoc);
+        let recordingIndex = DocListCast(Doc.UserDoc().workspaceRecordings).indexOf(value.rootDoc);
         console.log(recordingIndex);
         Cast(Doc.UserDoc().workspaceRecordings, listSpec(Doc), null)?.splice(recordingIndex, 1);
         Doc.UserDoc().isAddRecToDocMode = false;
@@ -187,13 +182,12 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
         Doc.UserDoc().isWorkspaceRecPlaying = false;
         Doc.UserDoc().isWorkspaceRecPaused = false;
         Doc.RemFromMyOverlay(value.rootDoc);
-
     }
 
     @undoBatch
     @action
     public static removeWorkspaceRec(value: VideoBox) {
-        let recordingIndex = Array.from(Doc.UserDoc().workspaceRecordings).indexOf(value.rootDoc);
+        let recordingIndex = DocListCast(Doc.UserDoc().workspaceRecordings).indexOf(value.rootDoc);
         Cast(Doc.UserDoc().workspaceRecordings, listSpec(Doc), null)?.splice(recordingIndex, 1);
         Doc.UserDoc().isAddRecToDocMode = false;
         Doc.RemFromMyOverlay(value.rootDoc);
@@ -202,11 +196,8 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
         Doc.UserDoc().isAddRecToDocMode = false;
         Doc.UserDoc().isWorkspaceRecPlaying = false;
         Doc.UserDoc().isWorkspaceRecPaused = false;
-
     }
 
-    
-
     Record: undefined | (() => void);
     Pause: undefined | (() => void);
     Finish: undefined | (() => void);
@@ -234,26 +225,50 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
     static screengrabber: RecordingBox | undefined;
 }
 
-ScriptingGlobals.add(function toggleRecording(_readOnly_: boolean) { RecordingBox.toggleWorkspaceRecording(_readOnly_); });
-ScriptingGlobals.add(function toggleRecPlayback(value: Doc) { RecordingBox.toggleWorkspaceRecPlayback(value); });
-ScriptingGlobals.add(function addRectoWorkspace(value: RecordingBox) { RecordingBox.addRecToWorkspace(value); });
+ScriptingGlobals.add(function toggleRecording(_readOnly_: boolean) {
+    RecordingBox.toggleWorkspaceRecording(_readOnly_);
+});
+ScriptingGlobals.add(function toggleRecPlayback(value: Doc) {
+    RecordingBox.toggleWorkspaceRecPlayback(value);
+});
+ScriptingGlobals.add(function addRectoWorkspace(value: RecordingBox) {
+    RecordingBox.addRecToWorkspace(value);
+});
 
-ScriptingGlobals.add(function playWorkspaceRec(value: VideoBox) { RecordingBox.playWorkspaceRec(value); });
-ScriptingGlobals.add(function pauseWorkspaceRec(value: VideoBox) { RecordingBox.pauseWorkspaceRec(value); });
-ScriptingGlobals.add(function closeWorkspaceRec(value: VideoBox) { RecordingBox.closeWorkspaceRec(value); });
-ScriptingGlobals.add(function removeWorkspaceRec(value: VideoBox) { RecordingBox.removeWorkspaceRec(value) });
+ScriptingGlobals.add(function playWorkspaceRec(value: VideoBox) {
+    RecordingBox.playWorkspaceRec(value);
+});
+ScriptingGlobals.add(function pauseWorkspaceRec(value: VideoBox) {
+    RecordingBox.pauseWorkspaceRec(value);
+});
+ScriptingGlobals.add(function closeWorkspaceRec(value: VideoBox) {
+    RecordingBox.closeWorkspaceRec(value);
+});
+ScriptingGlobals.add(function removeWorkspaceRec(value: VideoBox) {
+    RecordingBox.removeWorkspaceRec(value);
+});
 
-ScriptingGlobals.add(function getWorkspaceRecordings() { return Doc.UserDoc().workspaceRecordings });
-ScriptingGlobals.add(function getIsRecording() { return Doc.UserDoc().isRecording; })
-ScriptingGlobals.add(function getIsRecPlayback() { return Doc.UserDoc().isRecPlayback; })
-ScriptingGlobals.add(function getCurrentRecording() { return Doc.UserDoc().currentRecording; })
-ScriptingGlobals.add(function getIsWorkspaceRecPlaying() { return Doc.UserDoc().isWorkspaceRecPlaying; })
-ScriptingGlobals.add(function getIsWorkspaceRecPaused() { return Doc.UserDoc().isWorkspaceRecPaused; })
+ScriptingGlobals.add(function getWorkspaceRecordings() {
+    return Doc.UserDoc().workspaceRecordings;
+});
+ScriptingGlobals.add(function getIsRecording() {
+    return Doc.UserDoc().isRecording;
+});
+ScriptingGlobals.add(function getIsRecPlayback() {
+    return Doc.UserDoc().isRecPlayback;
+});
+ScriptingGlobals.add(function getCurrentRecording() {
+    return Doc.UserDoc().currentRecording;
+});
+ScriptingGlobals.add(function getIsWorkspaceRecPlaying() {
+    return Doc.UserDoc().isWorkspaceRecPlaying;
+});
+ScriptingGlobals.add(function getIsWorkspaceRecPaused() {
+    return Doc.UserDoc().isWorkspaceRecPaused;
+});
 ScriptingGlobals.add(function renderDropdown() {
-    if (!Doc.UserDoc().workspaceRecordings || Array.from(Doc.UserDoc().workspaceRecordings).length === 0) {
+    if (!Doc.UserDoc().workspaceRecordings || DocListCast(Doc.UserDoc().workspaceRecordings).length === 0) {
         return true;
     }
     return false;
-})
-
-
+});
-- 
cgit v1.2.3-70-g09d2


From 7c218639c75e22e1270d4198fb940b439175deee Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 31 Aug 2023 11:50:14 -0400
Subject: reworked recording workspace UI and switched to recording window, not
 webcam

---
 src/client/util/CurrentUserUtils.ts                |  58 +++---
 src/client/util/DragManager.ts                     |   7 +-
 src/client/views/DashboardView.tsx                 |   8 +-
 .../collectionLinear/CollectionLinearView.tsx      |   4 +-
 src/client/views/nodes/DocumentView.tsx            |   2 +-
 src/client/views/nodes/FontIconBox/FontIconBox.tsx |  90 ++++-----
 .../views/nodes/RecordingBox/RecordingBox.tsx      | 215 ++++++++++-----------
 src/client/views/nodes/ScreenshotBox.tsx           |   3 +
 src/client/views/topbar/TopBar.tsx                 |  63 ++++--
 src/fields/Doc.ts                                  |   3 +
 10 files changed, 234 insertions(+), 219 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 51ef51508..04f1ff0d2 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -18,6 +18,7 @@ import { CollectionViewType, DocumentType } from "../documents/DocumentTypes";
 import { TreeViewType } from "../views/collections/CollectionTreeView";
 import { DashboardView } from "../views/DashboardView";
 import { Colors } from "../views/global/globalEnums";
+import { media_state } from "../views/nodes/AudioBox";
 import { OpenWhere } from "../views/nodes/DocumentView";
 import { ButtonType } from "../views/nodes/FontIconBox/FontIconBox";
 import { ImportElementBox } from "../views/nodes/importBox/ImportElementBox";
@@ -51,7 +52,7 @@ interface Button {
 
     // fields that do not correspond to DocumentOption fields
     scripts?: { script?: string; onClick?: string; onDoubleClick?: string }
-    funcs?: { [key:string]: string };
+    funcs?: { [key:string]: any};
     subMenu?: Button[];
 }
 
@@ -591,7 +592,7 @@ export class CurrentUserUtils {
     static createToolButton = (opts: DocumentOptions) => Docs.Create.FontIconDocument({
         btnType: ButtonType.ToolButton, _forceActive: true, _layout_hideContextMenu: true, 
         _dropPropertiesToRemove: new List<string>([ "_layout_hideContextMenu"]), 
-        _nativeWidth: 40, _nativeHeight: 40, _width: 40, _height: 40, isSystem: true, ...opts,
+        /*_nativeWidth: 40, _nativeHeight: 40, */ _width: 40, _height: 40, isSystem: true, ...opts,
     })
 
     /// initializes the required buttons in the expanding button menu at the bottom of the Dash window
@@ -693,7 +694,6 @@ export class CurrentUserUtils {
             { title: "URL",     toolTip: "URL", width: 250, btnType: ButtonType.EditableText, icon: "lock", ignoreClick: true, scripts: { script: '{ return webSetURL(value, _readOnly_); }'} },
         ];
     }
-
     static contextMenuTools():Button[] {
         return [
             { btnList: new List<string>([CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Tree,
@@ -701,11 +701,11 @@ export class CurrentUserUtils {
                     CollectionViewType.Multirow, CollectionViewType.Time, CollectionViewType.Carousel,
                     CollectionViewType.Carousel3D, CollectionViewType.Linear, CollectionViewType.Map,
                     CollectionViewType.Grid, CollectionViewType.NoteTaking]),
-              title: "Perspective",                    toolTip: "View",                 btnType: ButtonType.DropdownList,                                                                                                                                    ignoreClick: true, width: 100, scripts: { script: 'setView(value, _readOnly_)'}},
+              title: "Perspective",                    toolTip: "View",                 btnType: ButtonType.DropdownList,                                                                                                                                    ignoreClick: true, width: 100, scripts: { script:  'setView(value, _readOnly_)'}},
             { title: "Pin",     icon: "map-pin",       toolTip: "Pin View to Trail",    btnType: ButtonType.ClickButton,         expertMode: false,                                                                                                                             width: 30,  scripts: { onClick: 'pinWithView(altKey)'}, funcs: {hidden: "IsNoneSelected()"}},
             { title: "Fill",    icon: "fill-drip",     toolTip: "Background Fill Color",btnType: ButtonType.ColorButton,         expertMode: false,                                                                                                          ignoreClick: true, width: 30,  scripts: { script:  'return setBackgroundColor(value, _readOnly_)'}, funcs: {hidden: "IsNoneSelected()"}}, // Only when a document is selected
             { title: "Header",  icon: "heading",       toolTip: "Header Color",         btnType: ButtonType.ColorButton,         expertMode: true,                                                                                                           ignoreClick: true,             scripts: { script:  'return setHeaderColor(value, _readOnly_)'}, funcs: {hidden: "IsNoneSelected()"}},
-            { title: "Overlay", icon: "layer-group",   toolTip: "Overlay",              btnType: ButtonType.ToggleButton,        expertMode: true,  toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode, true)'},       scripts: { onClick:  '{ return toggleOverlay(_readOnly_); }'}}, // Only when floating document is selected in freeform
+            { title: "Overlay", icon: "layer-group",   toolTip: "Overlay",              btnType: ButtonType.ToggleButton,        expertMode: true,  toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode, true)'},       scripts: { onClick: '{ return toggleOverlay(_readOnly_); }'}}, // Only when floating document is selected in freeform
             { title: "Back",    icon: "chevron-left",  toolTip: "Prev Animation Frame", btnType: ButtonType.ClickButton,         expertMode: true,  toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode)'}, width: 30,  scripts: { onClick: 'prevKeyFrame(_readOnly_)'}},
             { title: "Num",     icon:"",               toolTip: "Frame Number (click to toggle edit mode)",    btnType: ButtonType.TextButton,          expertMode: true,  toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode)', buttonText: 'selectedDocs()?.lastElement()?.currentFrame?.toString()'}, width: 20, scripts: { onClick: '{ return curKeyFrame(_readOnly_);}'}},
             { title: "Fwd",     icon: "chevron-right", toolTip: "Next Animation Frame", btnType: ButtonType.ClickButton,         expertMode: true,  toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode)'}, width: 30,  scripts: { onClick: 'nextKeyFrame(_readOnly_)'}},
@@ -714,16 +714,8 @@ export class CurrentUserUtils {
             { title: "Doc",     icon: "Doc",           toolTip: "Freeform Doc tools",   subMenu: CurrentUserUtils.freeTools(),   expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode, true)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available
             { title: "View",    icon: "View",          toolTip: "View tools",           subMenu: CurrentUserUtils.viewTools(),   expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available
             { title: "Web",     icon: "Web",           toolTip: "Web functions",        subMenu: CurrentUserUtils.webTools(),    expertMode: false, toolType:DocumentType.WEB,            funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Web is selected
-            { title: "Schema",  icon: "Schema",linearBtnWidth:58,toolTip: "Schema functions",subMenu: CurrentUserUtils.schemaTools(), expertMode: false, toolType:CollectionViewType.Schema,   funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Schema is selected
-            { title: "Video",   icon: 'video',         toolTip: "Dictate",              btnType: ButtonType.ToggleButton,        expertMode: false,                                       funcs: {hidden: `getIsRecording()`},                               ignoreClick: true,             scripts: { onClick:  'return toggleRecording(_readOnly_)'}},
-            { title: "StopRec", icon: "stop",          toolTip: "Stop",                 btnType: ButtonType.ToggleButton,        expertMode: false,                                       funcs: {hidden: `!getIsRecording()`},                              ignoreClick: true,             scripts: { onClick: `return toggleRecording(_readOnly_)`}},
-            { title: "Dropdown",                       toolTip: "Workspace Recordings", btnType: ButtonType.DropdownList,        expertMode: false,                                       funcs: {hidden: `renderDropdown()`, btnList: `getWorkspaceRecordings()`},                      ignoreClick: true,             scripts: { script: `toggleRecPlayback(value)`}},
-            { title: "Play Rec",icon: "play",          toolTip: "Play recording",       btnType: ButtonType.ToggleButton,        expertMode: false,                                       funcs: {hidden: `!getIsWorkspaceRecPlaying()`},                            ignoreClick: true,             scripts: { onClick: `return playWorkspaceRec(getCurrentRecording())`}},
-            { title: "Pause Rec",icon: "pause",        toolTip: "Pause recording",      btnType: ButtonType.ToggleButton,        expertMode: false,                                       funcs: {hidden: `!getIsWorkspaceRecPaused()`},                            ignoreClick: true,             scripts: { onClick: `return pauseWorkspaceRec(getCurrentRecording())`}},
-            { title: "Stop Rec", icon: "stop",         toolTip: "Stop recording",       btnType: ButtonType.ToggleButton,        expertMode: false,                                       funcs: {hidden: `!getIsRecPlayback()`},                            ignoreClick: true,             scripts: { onClick: `return closeWorkspaceRec(getCurrentRecording())`}},
-            { title: "Add doc", icon: "down",          toolTip: "add to doc",           btnType: ButtonType.ToggleButton,        expertMode: false,                                       funcs: {hidden: `!getIsRecPlayback()`},                            ignoreClick: true,             scripts: { onClick: `addRectoWorkspace(getCurrentRecording())`}},
-            { title: "Delete Rec", icon: "trash",      toolTip: "delete selected recording", btnType: ButtonType.ToggleButton,   expertMode: false,                                       funcs: {hidden: `!getIsRecPlayback()`},                                                         ignoreClick: true,             scripts: { onClick: `removeWorkspaceRec(getCurrentRecording())`}}
-        ];
+            { title: "Schema",  icon: "Schema",linearBtnWidth:58,toolTip: "Schema functions",subMenu: CurrentUserUtils.schemaTools(),expertMode: false,toolType:CollectionViewType.Schema,funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Schema is selected
+           ];
     }
 
     /// initializes a context menu button for the top bar context menu
@@ -749,19 +741,17 @@ export class CurrentUserUtils {
         if (Doc.UserDoc().currentRecording) {
             Doc.RemFromMyOverlay(DocCast(Doc.UserDoc().currentRecording));
         }
-        Doc.UserDoc().isRecording = false;
-        Doc.UserDoc().isRecPlayback = false;
+        Doc.UserDoc().workspaceRecordingState = undefined;
+        Doc.UserDoc().workspaceReplayingState = undefined;
         Doc.UserDoc().currentRecording = undefined;
-        Doc.UserDoc().isPlaybackPlaying = false;
-        Doc.UserDoc().isWorkspaceRecPlaying = false;
-        Doc.UserDoc().isWorkspaceRecPaused = false;
+        Doc.UserDoc().workspaceRecordingState = undefined;
         if (!subMenu) { // button does not have a sub menu
             return this.setupContextMenuButton(params, menuBtnDoc);
         }  
         // linear view
         const reqdSubMenuOpts = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, undoIgnoreFields: new List<string>(['width', "linearView_IsOpen"]),
             childDontRegisterViews: true, flexGap: 0, _height: 30, ignoreClick: params.scripts?.onClick ? false : true,
-            linearView_SubMenu: true, linearView_Expandable: params.btnType !== ButtonType.MultiToggleButton};
+            linearView_SubMenu: true, linearView_Expandable: true};
         
         const items = (menuBtnDoc?:Doc) => !menuBtnDoc ? [] : subMenu.map(sub => this.setupContextMenuBtn(sub, menuBtnDoc) );
         const creator = params.btnType === ButtonType.MultiToggleButton ? this.multiToggleList : this.linearButtonList;
@@ -773,11 +763,34 @@ export class CurrentUserUtils {
 
     /// Initializes all the default buttons for the top bar context menu
     static setupContextMenuButtons(doc: Doc, field="myContextMenuBtns") {
-        const reqdCtxtOpts:DocumentOptions = { title: "context menu buttons", undoIgnoreFields:new List<string>(['width', "linearView_IsOpen"]), flexGap: 0, childDragAction: 'embed', childDontRegisterViews: true,  linearView_IsOpen: true, ignoreClick: true, linearView_Expandable: false, _height: 35 };
+        const reqdCtxtOpts:DocumentOptions = { title: "context menu buttons", undoIgnoreFields:new List<string>(['width', "linearView_IsOpen"]), flexGap: 0, childDragAction: 'embed', childDontRegisterViews: true,  linearView_IsOpen: true, ignoreClick: true, linearView_Expandable: true, _height: 35 };
         const ctxtMenuBtnsDoc = DocUtils.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), reqdCtxtOpts, undefined);
         const ctxtMenuBtns = CurrentUserUtils.contextMenuTools().map(params => this.setupContextMenuBtn(params, ctxtMenuBtnsDoc) );
         return DocUtils.AssignOpts(ctxtMenuBtnsDoc, reqdCtxtOpts, ctxtMenuBtns);
     }
+    /// Initializes all the default buttons for the top bar context menu
+    static setupTopbarButtons(doc: Doc, field="myTopBarBtns") {
+        const dockedBtns = DocCast(doc[field]);
+        const dockBtn = (opts: DocumentOptions, scripts: {[key:string]:string|undefined}, funcs?: {[key:string]:any}) =>
+            DocUtils.AssignScripts(DocUtils.AssignOpts(DocListCast(dockedBtns?.data)?.find(doc => doc.title === opts.title), opts) ?? 
+                            CurrentUserUtils.createToolButton(opts), scripts, funcs);
+        
+        const btnDescs = [// setup reactions to change the highlights on the undo/redo buttons -- would be better to encode this in the undo/redo buttons, but the undo/redo stacks are not wired up that way yet
+            { opts: { title: "Replicate",icon:"camera",toolTip: "Copy dashboard layout",btnType: ButtonType.ClickButton, expertMode: true},                                                                                               scripts: { onClick:  `snapshotDashboard()`}},
+            { opts: { title: "Recordings",            toolTip: "Workspace Recordings", btnType: ButtonType.DropdownList,expertMode: false, ignoreClick: true, width: 100}, funcs: {hidden: `false`, btnList:`getWorkspaceRecordings()`}, scripts: { script:  `{ return replayWorkspace(value, _readOnly_); }`}},
+            { opts: { title: "Stop Rec",icon: "stop", toolTip: "Stop recording",       btnType: ButtonType.ClickButton, expertMode: false}, funcs: {hidden: `!isWorkspaceRecording()`},                                                  scripts: { onClick: `stopWorkspaceRecording()`}},
+            { opts: { title: "Play",    icon: "play", toolTip: "Play recording",       btnType: ButtonType.ClickButton, expertMode: false}, funcs: {hidden: `isWorkspaceReplaying() !== "${media_state.Paused}"`},                       scripts: { onClick: `resumeWorkspaceReplaying(getCurrentRecording())`}},
+            { opts: { title: "Pause",   icon: "pause",toolTip: "Pause playback",       btnType: ButtonType.ClickButton, expertMode: false}, funcs: {hidden: `isWorkspaceReplaying() !== "${media_state.Playing}"`},                      scripts: { onClick: `pauseWorkspaceReplaying(getCurrentRecording())`}},
+            { opts: { title: "Stop",    icon: "stop", toolTip: "Stop playback",        btnType: ButtonType.ClickButton, expertMode: false}, funcs: {hidden: `isWorkspaceReplaying() !== "${media_state.Paused}"`},                       scripts: { onClick: `stopWorkspaceReplaying(getCurrentRecording())`}},
+            { opts: { title: "Delete",  icon: "trash",toolTip: "delete selected rec",  btnType: ButtonType.ClickButton, expertMode: false}, funcs: {hidden: `isWorkspaceReplaying() !== "${media_state.Paused}"`},                       scripts: { onClick: `removeWorkspaceReplaying(getCurrentRecording())`}}
+        ];
+        const btns = btnDescs.map(desc => dockBtn({_width: desc.opts.width??30, _height: 30, defaultDoubleClick: 'ignore', undoIgnoreFields: new List<string>(['opacity']), _dragOnlyWithinContainer: true, ...desc.opts}, desc.scripts, desc.funcs));
+        const dockBtnsReqdOpts:DocumentOptions = {
+            title: "docked buttons", _height: 40, flexGap: 0, layout_boxShadow: "standard", childDragAction: 'move',
+            childDontRegisterViews: true, linearView_IsOpen: true, linearView_Expandable: true, ignoreClick: true
+        };
+        return DocUtils.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), dockBtnsReqdOpts, btns);
+    }
 
     /// collection of documents rendered in the overlay layer above all tabs and other UI
     static setupOverlays(doc: Doc, field = "myOverlayDocs") {
@@ -891,6 +904,7 @@ export class CurrentUserUtils {
         this.setupOverlays(doc);  // sets up the overlay panel where documents and other widgets can be added to float over the rest of the dashboard
         this.setupPublished(doc);  // sets up the list doc of all docs that have been published (meaning that they can be auto-linked by typing their title into another text box)
         this.setupContextMenuButtons(doc); // set up the row of buttons at the top of the dashboard that change depending on what is selected
+        this.setupTopbarButtons(doc);
         this.setupDockedButtons(doc);  // the bottom bar of font icons
         this.setupLeftSidebarMenu(doc); // the left-side column of buttons that open their contents in a flyout panel on the left
         this.setupDocTemplates(doc); // sets up the template menu of templates
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 065f17139..6c0641943 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -243,13 +243,10 @@ export namespace DragManager {
             return dropDoc;
         };
         const finishDrag = async (e: DragCompleteEvent) => {
-            Doc.UserDoc().isAddRecToDocMode = false;
             Doc.RemFromMyOverlay(DocCast(Doc.UserDoc().currentRecording));
             Doc.UserDoc().currentRecording = undefined;
-            Doc.UserDoc().isRecPlayback = false;
-            Doc.UserDoc().isWorkspaceRecPlaying = false;
-            Doc.UserDoc().isWorkspaceRecPaused = false;
-            Doc.UserDoc().isAddRecToDocMode = false;
+            Doc.UserDoc().isWorkspaceReplaying = false;
+            Doc.UserDoc().workspaceRecordingState = undefined;
             Cast(Doc.UserDoc().workspaceRecordings, listSpec(Doc), null)?.splice(recordingIndex, 1);
             const docDragData = e.docDragData;
             dropEvent?.(); // glr: optional additional function to be called - in this case with presentation trails
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 4387c6e96..3ef6c0814 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -18,7 +18,7 @@ import { CollectionViewType } from '../documents/DocumentTypes';
 import { HistoryUtil } from '../util/History';
 import { ScriptingGlobals } from '../util/ScriptingGlobals';
 import { SharingManager } from '../util/SharingManager';
-import { undoBatch } from '../util/UndoManager';
+import { undoBatch, UndoManager } from '../util/UndoManager';
 import { CollectionDockingView } from './collections/CollectionDockingView';
 import { CollectionView } from './collections/CollectionView';
 import { ContextMenu } from './ContextMenu';
@@ -493,6 +493,8 @@ ScriptingGlobals.add(function resetDashboard(dashboard: Doc) {
 ScriptingGlobals.add(function addToDashboards(dashboard: Doc) {
     DashboardView.openDashboard(Doc.MakeEmbedding(dashboard));
 }, 'adds Dashboard to set of Dashboards');
-ScriptingGlobals.add(function snapshotDashboard() {
-    DashboardView.snapshotDashboard();
+ScriptingGlobals.add(async function snapshotDashboard() {
+    const batch = UndoManager.StartBatch('snapshot');
+    await DashboardView.snapshotDashboard();
+    batch.end();
 }, 'creates a snapshot copy of a dashboard');
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index 3481d5130..0cf7d4411 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -229,8 +229,8 @@ export class CollectionLinearView extends CollectionSubView() {
                 <div className="collectionLinearView" ref={this.createDashEventsTarget} onContextMenu={this.myContextMenu} style={{ minHeight: this.dimension(), pointerEvents: 'all' }}>
                     {
                         <>
-                            {menuOpener}
-                            {!this.layoutDoc.linearView_IsOpen ? null : (
+                            {!this.layoutDoc.linearView_Expandable ? null :menuOpener}
+                            {!this.layoutDoc.linearView_IsOpen && !this.layoutDoc.linearView_alwaysOpen ? null : (
                                 <div
                                     className="collectionLinearView-content"
                                     style={{
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 8a0706c30..998024cea 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -798,7 +798,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             !more && moreItems.length && cm.addItem({ description: 'More...', subitems: moreItems, icon: 'compass' });
         }
         const constantItems: ContextMenuProps[] = [];
-        if (!Doc.IsSystem(this.rootDoc)) {
+        if (!Doc.IsSystem(this.rootDoc) && this.rootDoc._type_collection !== CollectionViewType.Docking) {
             constantItems.push({ description: 'Zip Export', icon: 'download', event: async () => Doc.Zip(this.props.Document) });
             (this.rootDoc._type_collection !== CollectionViewType.Docking || !Doc.noviceMode) && constantItems.push({ description: 'Share', event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: 'users' });
             if (this.props.removeDocument && Doc.ActiveDashboard !== this.props.Document) {
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index bb2069cc9..8ef9cd792 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -180,55 +180,45 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
      * Dropdown list
      */
     @computed get dropdownListButton() {
-        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
         const script = ScriptCast(this.rootDoc.script);
 
         let noviceList: string[] = [];
         let text: string | undefined;
-        let dropdown = true;
         let getStyle: (val: string) => any = () => {};
         let icon: IconProp = 'caret-down';
-        let isViewDropdown: boolean = script?.script.originalScript.startsWith('setView');
-        try {
-            if (isViewDropdown) {
-                const selectedDocs: Doc[] = SelectionManager.Docs();
-                const selected = SelectionManager.Docs().lastElement();
-                if (selected) {
-                    if (StrCast(selected.type) === DocumentType.COL) {
-                        text = StrCast(selected._type_collection);
+        const isViewDropdown = script?.script.originalScript.startsWith('setView');
+        if (isViewDropdown) {
+            const selected = SelectionManager.Docs();
+            if (selected.lastElement()) {
+                if (StrCast(selected.lastElement().type) === DocumentType.COL) {
+                    text = StrCast(selected.lastElement()._type_collection);
+                } else {
+                    if (selected.length > 1) {
+                        text = selected.length + ' selected';
                     } else {
-                        dropdown = false;
-                        if (selectedDocs.length > 1) {
-                            text = selectedDocs.length + ' selected';
-                        } else {
-                            text = Utils.cleanDocumentType(StrCast(selected.type) as DocumentType);
-                            icon = Doc.toIcon(selected);
-                        }
-                        return (
-                            <Popup
-                                icon={<FontAwesomeIcon size={'1x'} icon={icon} />}
-                                text={text}
-                                type={Type.TERT}
-                                color={SettingsManager.userColor}
-                                background={SettingsManager.userVariantColor}
-                                popup={<SelectedDocView selectedDocs={selectedDocs} />}
-                                fillWidth
-                            />
-                        );
+                        text = Utils.cleanDocumentType(StrCast(selected.lastElement().type) as DocumentType);
+                        icon = Doc.toIcon(selected.lastElement());
                     }
-                } else {
-                    dropdown = false;
-                    return <Button text="None Selected" type={Type.TERT} color={SettingsManager.userColor} background={SettingsManager.userVariantColor} fillWidth inactive />;
+                    return (
+                        <Popup
+                            icon={<FontAwesomeIcon size={'1x'} icon={icon} />}
+                            text={text}
+                            type={Type.TERT}
+                            color={SettingsManager.userColor}
+                            background={SettingsManager.userVariantColor}
+                            popup={<SelectedDocView selectedDocs={selected} />}
+                            fillWidth
+                        />
+                    );
                 }
-                noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking, CollectionViewType.NoteTaking];
             } else {
-                text = StrCast((RichTextMenu.Instance?.TextView?.EditorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
-                getStyle = (val: string) => {
-                    return { fontFamily: val };
-                };
+                return <Button text="None Selected" type={Type.TERT} color={SettingsManager.userColor} background={SettingsManager.userVariantColor} fillWidth inactive />;
             }
-        } catch (e) {
-            console.log(e);
+            noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking, CollectionViewType.NoteTaking];
+        } else {
+            text = script?.script.run({ this: this.layoutDoc, self: this.rootDoc, value: '', _readOnly_: true }).result;
+            // text = StrCast((RichTextMenu.Instance?.TextView?.EditorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
+            getStyle = (val: string) => ({ fontFamily: val });
         }
 
         // Get items to place into the list
@@ -243,19 +233,17 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
             }));
 
         return (
-            <div style={{ color: SettingsManager.userColor, background: SettingsManager.userBackgroundColor }}>
-                <Dropdown
-                    selectedVal={text}
-                    setSelectedVal={undoable(val => script.script.run({ this: this.layoutDoc, self: this.rootDoc, val }), `dropdown select ${this.label}`)}
-                    color={SettingsManager.userColor}
-                    background={isViewDropdown ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor}
-                    type={Type.TERT}
-                    dropdownType={DropdownType.SELECT}
-                    items={list}
-                    tooltip={this.label}
-                    fillWidth
-                />
-            </div>
+            <Dropdown
+                selectedVal={text ?? 'Record Workspace'}
+                setSelectedVal={undoable(val => script.script.run({ this: this.layoutDoc, self: this.rootDoc, val }), `dropdown select ${this.label}`)}
+                color={SettingsManager.userColor}
+                background={SettingsManager.userVariantColor}
+                type={Type.TERT}
+                dropdownType={DropdownType.SELECT}
+                items={list}
+                tooltip={this.label}
+                fillWidth
+            />
         );
     }
 
diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
index 9dd673591..b1b044c25 100644
--- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
@@ -1,11 +1,10 @@
 import { action, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { formatTime } from '../../../../Utils';
 import { Doc, DocListCast } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
 import { listSpec } from '../../../../fields/Schema';
-import { BoolCast, Cast, DocCast } from '../../../../fields/Types';
+import { BoolCast, Cast, DocCast, StrCast } from '../../../../fields/Types';
 import { VideoField } from '../../../../fields/URLField';
 import { Upload } from '../../../../server/SharedMediaTypes';
 import { Docs } from '../../../documents/Documents';
@@ -22,6 +21,8 @@ import { FieldView, FieldViewProps } from '../FieldView';
 import { VideoBox } from '../VideoBox';
 import { RecordingView } from './RecordingView';
 import { DateField } from '../../../../fields/DateField';
+import { media_state } from '../AudioBox';
+import { List } from '../../../../fields/List';
 
 @observer
 export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -41,9 +42,7 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @observable videoDuration: number | undefined = undefined;
 
     @action
-    setVideoDuration = (duration: number) => {
-        this.videoDuration = duration;
-    };
+    setVideoDuration = (duration: number) => (this.videoDuration = duration);
 
     @action
     setResult = (info: Upload.AccessPathInfo, presentation?: Presentation) => {
@@ -61,76 +60,68 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this.dataDoc[this.fieldKey + '_presentation'] = JSON.stringify(presCopy);
         }
     };
-
-    /**
-     * This method toggles whether or not we are currently using the RecordingBox to record with the topbar button
-     * @param _readOnly_
-     * @returns
-     */
     @undoBatch
     @action
-    public static toggleWorkspaceRecording(_readOnly_: boolean) {
-        if (_readOnly_) return RecordingBox.screengrabber ? true : false;
-        if (RecordingBox.screengrabber) {
+    public static WorkspaceStopRecording() {
+        const remDoc = RecordingBox.screengrabber?.rootDoc;
+        if (remDoc) {
             //if recordingbox is true; when we press the stop button. changed vals temporarily to see if changes happening
-            console.log('grabbing screen!');
-            RecordingBox.screengrabber.Pause?.();
-            const remDoc = RecordingBox.screengrabber.rootDoc;
+            RecordingBox.screengrabber?.Pause?.();
             setTimeout(() => {
                 RecordingBox.screengrabber?.Finish?.();
-                RecordingBox.screengrabber!.rootDoc.overlayX = 70; //was 100
-                RecordingBox.screengrabber!.rootDoc.overlayY = 590;
-                console.log(RecordingBox.screengrabber);
+                remDoc.overlayX = 70; //was 100
+                remDoc.overlayY = 590;
                 RecordingBox.screengrabber = undefined;
             }, 100);
             //could break if recording takes too long to turn into videobox. If so, either increase time on setTimeout below or find diff place to do this
-            setTimeout(() => {
-                Doc.RemFromMyOverlay(remDoc);
-            }, 1000);
-            Doc.UserDoc().isRecording = false;
-            Doc.AddDocToList(Doc.UserDoc(), 'workspaceRecordings', RecordingBox.screengrabber.rootDoc);
-        } else {
-            //when we first press mic
-            const screengrabber = Docs.Create.WebCamDocument('', {
-                title: `${Doc.ActiveDashboard?.title ?? ''} ${new DateField()}`,
-                _width: 205,
-                _height: 115,
-            });
-            screengrabber.overlayX = 70; //was -400
-            screengrabber.overlayY = 590; //was 0
-            screengrabber[Doc.LayoutFieldKey(screengrabber) + '_trackScreen'] = true;
-            Doc.AddToMyOverlay(screengrabber); //just adds doc to overlay
-            DocumentManager.Instance.AddViewRenderedCb(screengrabber, docView => {
-                RecordingBox.screengrabber = docView.ComponentView as RecordingBox;
-                RecordingBox.screengrabber.Record?.();
-            });
-            Doc.UserDoc().isRecording = true;
+            setTimeout(() => Doc.RemFromMyOverlay(remDoc), 1000);
+            Doc.UserDoc().workspaceRecordingState = media_state.Paused;
+            Doc.AddDocToList(Doc.UserDoc(), 'workspaceRecordings', remDoc);
         }
     }
 
+    /**
+     * This method toggles whether or not we are currently using the RecordingBox to record with the topbar button
+     * @param _readOnly_
+     * @returns
+     */
+    @undoBatch
+    @action
+    public static WorkspaceStartRecording() {
+        const screengrabber = Docs.Create.ScreenshotDocument({
+            title: `${Doc.ActiveDashboard?.title ?? ''} ${new DateField()}`,
+            _width: 205,
+            _height: 115,
+        });
+        screengrabber.overlayX = 70; //was -400
+        screengrabber.overlayY = 590; //was 0
+        screengrabber[Doc.LayoutFieldKey(screengrabber) + '_trackScreen'] = true;
+        Doc.AddToMyOverlay(screengrabber); //just adds doc to overlay
+        DocumentManager.Instance.AddViewRenderedCb(screengrabber, docView => {
+            RecordingBox.screengrabber = docView.ComponentView as RecordingBox;
+            RecordingBox.screengrabber.Record?.();
+        });
+        Doc.UserDoc().workspaceRecordingState = media_state.Recording;
+    }
+
     /**
      * This method changes the menu depending on whether or not we are in playback mode
      * @param value  RecordingBox rootdoc
      */
     @undoBatch
     @action
-    public static toggleWorkspaceRecPlayback(value: Doc) {
+    public static replayWorkspace(value: Doc) {
         Doc.UserDoc().currentRecording = value;
-        console.log(value);
         value.overlayX = 70;
         value.overlayY = 590;
-        if (!Doc.UserDoc().isAddRecToDocMode) {
-            Doc.UserDoc().isRecPlayback = true;
-            Doc.UserDoc().isWorkspaceRecPlaying = true;
-            Doc.AddToMyOverlay(value);
-            DocumentManager.Instance.AddViewRenderedCb(value, docView => {
-                Doc.UserDoc().currentRecording = docView.rootDoc;
-                SelectionManager.SelectSchemaViewDoc(value);
-            });
-        } else {
-            let recordingIndex = DocListCast(Doc.UserDoc().workspaceRecordings).indexOf(value);
-            DragManager.StartDropdownDrag([document.createElement('div')], new DragManager.DocumentDragData([value]), 1, 1, recordingIndex);
-        }
+        Doc.AddToMyOverlay(value);
+        DocumentManager.Instance.AddViewRenderedCb(value, docView => {
+            Doc.UserDoc().currentRecording = docView.rootDoc;
+            SelectionManager.SelectSchemaViewDoc(value);
+            RecordingBox.resumeWorkspaceReplaying(value);
+        });
+        // let recordingIndex = DocListCast(Doc.UserDoc().workspaceRecordings).indexOf(value);
+        // DragManager.StartDropdownDrag([document.createElement('div')], new DragManager.DocumentDragData([value]), 1, 1, recordingIndex);
     }
 
     /**
@@ -140,62 +131,55 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @undoBatch
     @action
     public static addRecToWorkspace(value: RecordingBox) {
-        console.log('adding rec to doc');
-        console.log(value);
         let ffView = Array.from(DocumentManager.Instance.DocumentViews).find(view => view.ComponentView instanceof CollectionFreeFormView);
         (ffView?.ComponentView as CollectionFreeFormView).props.addDocument?.(value.rootDoc);
-        let recordingIndex = DocListCast(Doc.UserDoc().workspaceRecordings).indexOf(value.rootDoc);
-        console.log(recordingIndex);
-        Cast(Doc.UserDoc().workspaceRecordings, listSpec(Doc), null)?.splice(recordingIndex, 1);
-        Doc.UserDoc().isAddRecToDocMode = false;
+        Doc.RemoveDocFromList(Doc.UserDoc(), 'workspaceRecordings', value.rootDoc);
         Doc.RemFromMyOverlay(value.rootDoc);
         Doc.UserDoc().currentRecording = undefined;
-        Doc.UserDoc().isRecPlayback = false;
-        Doc.UserDoc().isAddRecToDocMode = false;
-        Doc.UserDoc().isWorkspaceRecPlaying = false;
-        Doc.UserDoc().isWorkspaceRecPaused = false;
+        Doc.UserDoc().workspaceReplayingState = undefined;
+        Doc.UserDoc().workspaceRecordingState = undefined;
     }
 
     @undoBatch
     @action
-    public static playWorkspaceRec(value: VideoBox) {
-        value.Play();
-        Doc.UserDoc().isWorkspaceRecPlaying = false;
-        Doc.UserDoc().isWorkspaceRecPaused = true;
+    public static resumeWorkspaceReplaying(doc: Doc) {
+        const docView = DocumentManager.Instance.getDocumentView(doc);
+        const videoBox = docView?.ComponentView as VideoBox;
+        if (videoBox) {
+            videoBox.Play();
+            Doc.UserDoc().workspaceReplayingState = media_state.Playing;
+        }
     }
 
     @undoBatch
     @action
-    public static pauseWorkspaceRec(value: VideoBox) {
-        value.Pause();
-        Doc.UserDoc().isWorkspaceRecPlaying = true;
-        Doc.UserDoc().isWorkspaceRecPaused = false;
+    public static pauseWorkspaceReplaying(doc: Doc) {
+        const docView = DocumentManager.Instance.getDocumentView(doc);
+        const videoBox = docView?.ComponentView as VideoBox;
+        if (videoBox) {
+            videoBox.Pause();
+        }
+        Doc.UserDoc().workspaceReplayingState = media_state.Paused;
     }
 
     @undoBatch
     @action
-    public static closeWorkspaceRec(value: VideoBox) {
-        value.Pause();
-        Doc.RemFromMyOverlay(value.rootDoc);
+    public static stopWorkspaceReplaying(value: Doc) {
+        Doc.RemFromMyOverlay(value);
         Doc.UserDoc().currentRecording = undefined;
-        Doc.UserDoc().isRecPlayback = false;
-        Doc.UserDoc().isWorkspaceRecPlaying = false;
-        Doc.UserDoc().isWorkspaceRecPaused = false;
-        Doc.RemFromMyOverlay(value.rootDoc);
+        Doc.UserDoc().workspaceReplayingState = undefined;
+        Doc.UserDoc().workspaceRecordingState = undefined;
+        Doc.RemFromMyOverlay(value);
     }
 
     @undoBatch
     @action
-    public static removeWorkspaceRec(value: VideoBox) {
-        let recordingIndex = DocListCast(Doc.UserDoc().workspaceRecordings).indexOf(value.rootDoc);
-        Cast(Doc.UserDoc().workspaceRecordings, listSpec(Doc), null)?.splice(recordingIndex, 1);
-        Doc.UserDoc().isAddRecToDocMode = false;
-        Doc.RemFromMyOverlay(value.rootDoc);
+    public static removeWorkspaceReplaying(value: Doc) {
+        Doc.RemoveDocFromList(Doc.UserDoc(), 'workspaceRecordings', value);
+        Doc.RemFromMyOverlay(value);
         Doc.UserDoc().currentRecording = undefined;
-        Doc.UserDoc().isRecPlayback = false;
-        Doc.UserDoc().isAddRecToDocMode = false;
-        Doc.UserDoc().isWorkspaceRecPlaying = false;
-        Doc.UserDoc().isWorkspaceRecPaused = false;
+        Doc.UserDoc().workspaceReplayingState = undefined;
+        Doc.UserDoc().workspaceRecordingState = undefined;
     }
 
     Record: undefined | (() => void);
@@ -225,47 +209,44 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
     static screengrabber: RecordingBox | undefined;
 }
 
-ScriptingGlobals.add(function toggleRecording(_readOnly_: boolean) {
-    RecordingBox.toggleWorkspaceRecording(_readOnly_);
-});
-ScriptingGlobals.add(function toggleRecPlayback(value: Doc) {
-    RecordingBox.toggleWorkspaceRecPlayback(value);
+ScriptingGlobals.add(function startWorkspaceRecording() {
+    RecordingBox.WorkspaceStartRecording();
 });
-ScriptingGlobals.add(function addRectoWorkspace(value: RecordingBox) {
-    RecordingBox.addRecToWorkspace(value);
+ScriptingGlobals.add(function stopWorkspaceRecording() {
+    RecordingBox.WorkspaceStopRecording();
 });
 
-ScriptingGlobals.add(function playWorkspaceRec(value: VideoBox) {
-    RecordingBox.playWorkspaceRec(value);
+ScriptingGlobals.add(function stopWorkspaceReplaying(value: Doc) {
+    RecordingBox.stopWorkspaceReplaying(value);
 });
-ScriptingGlobals.add(function pauseWorkspaceRec(value: VideoBox) {
-    RecordingBox.pauseWorkspaceRec(value);
-});
-ScriptingGlobals.add(function closeWorkspaceRec(value: VideoBox) {
-    RecordingBox.closeWorkspaceRec(value);
-});
-ScriptingGlobals.add(function removeWorkspaceRec(value: VideoBox) {
-    RecordingBox.removeWorkspaceRec(value);
+ScriptingGlobals.add(function removeWorkspaceReplaying(value: Doc) {
+    RecordingBox.removeWorkspaceReplaying(value);
 });
 
+ScriptingGlobals.add(function getCurrentRecording() {
+    return Doc.UserDoc().currentRecording;
+});
 ScriptingGlobals.add(function getWorkspaceRecordings() {
-    return Doc.UserDoc().workspaceRecordings;
+    return new List<any>(['Record Workspace', ...DocListCast(Doc.UserDoc().workspaceRecordings)]);
 });
-ScriptingGlobals.add(function getIsRecording() {
-    return Doc.UserDoc().isRecording;
+ScriptingGlobals.add(function isWorkspaceRecording() {
+    return Doc.UserDoc().workspaceRecordingState === media_state.Recording;
 });
-ScriptingGlobals.add(function getIsRecPlayback() {
-    return Doc.UserDoc().isRecPlayback;
+ScriptingGlobals.add(function isWorkspaceReplaying() {
+    return Doc.UserDoc().workspaceReplayingState;
 });
-ScriptingGlobals.add(function getCurrentRecording() {
-    return Doc.UserDoc().currentRecording;
+ScriptingGlobals.add(function replayWorkspace(value: Doc | string, _readOnly_: boolean) {
+    if (_readOnly_) return DocCast(Doc.UserDoc().currentRecording) ?? 'Record Workspace';
+    if (typeof value === 'string') RecordingBox.WorkspaceStartRecording();
+    else RecordingBox.replayWorkspace(value);
 });
-ScriptingGlobals.add(function getIsWorkspaceRecPlaying() {
-    return Doc.UserDoc().isWorkspaceRecPlaying;
+ScriptingGlobals.add(function pauseWorkspaceReplaying(value: Doc, _readOnly_: boolean) {
+    RecordingBox.pauseWorkspaceReplaying(value);
 });
-ScriptingGlobals.add(function getIsWorkspaceRecPaused() {
-    return Doc.UserDoc().isWorkspaceRecPaused;
+ScriptingGlobals.add(function resumeWorkspaceReplaying(value: Doc, _readOnly_: boolean) {
+    RecordingBox.resumeWorkspaceReplaying(value);
 });
+
 ScriptingGlobals.add(function renderDropdown() {
     if (!Doc.UserDoc().workspaceRecordings || DocListCast(Doc.UserDoc().workspaceRecordings).length === 0) {
         return true;
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index fa19caae1..4ebc93165 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -219,6 +219,9 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
         // }
         return null;
     }
+    Record = () => !this._screenCapture && this.toggleRecording();
+    Pause = () => this._screenCapture && this.toggleRecording();
+
     toggleRecording = async () => {
         if (!this._screenCapture) {
             this._audioRec = new MediaRecorder(await navigator.mediaDevices.getUserMedia({ audio: true }));
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index 5b097e639..55d94406a 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -1,13 +1,14 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Button, IconButton, isDark, Size, Toggle, Type } from 'browndash-components';
+import { Button, IconButton, isDark, Size, Type } from 'browndash-components';
 import { action, computed, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { FaBug, FaCamera } from 'react-icons/fa';
+import { FaBug } from 'react-icons/fa';
 import { Doc, DocListCast } from '../../../fields/Doc';
 import { AclAdmin, DashVersion } from '../../../fields/DocSymbols';
 import { StrCast } from '../../../fields/Types';
 import { GetEffectiveAcl } from '../../../fields/util';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../../Utils';
 import { CurrentUserUtils } from '../../util/CurrentUserUtils';
 import { DocumentManager } from '../../util/DocumentManager';
 import { PingManager } from '../../util/PingManager';
@@ -15,12 +16,14 @@ import { ReportManager } from '../../util/reportManager/ReportManager';
 import { ServerStats } from '../../util/ServerStats';
 import { SettingsManager } from '../../util/SettingsManager';
 import { SharingManager } from '../../util/SharingManager';
-import { UndoManager } from '../../util/UndoManager';
+import { Transform } from '../../util/Transform';
 import { CollectionDockingView } from '../collections/CollectionDockingView';
+import { CollectionLinearView } from '../collections/collectionLinear';
 import { ContextMenu } from '../ContextMenu';
 import { DashboardView } from '../DashboardView';
 import { Colors } from '../global/globalEnums';
-import { DocumentView } from '../nodes/DocumentView';
+import { DocumentView, DocumentViewInternal } from '../nodes/DocumentView';
+import { DefaultStyleProvider } from '../StyleProvider';
 import './TopBar.scss';
 
 /**
@@ -91,6 +94,43 @@ export class TopBar extends React.Component {
         );
     }
 
+    @computed get dashMenuButtons() {
+        const selDoc = Doc.MyTopBarBtns;
+        return !(selDoc instanceof Doc) ? null : (
+            <div className="collectionMenu-contMenuButtons" style={{ height: '100%' }}>
+                <CollectionLinearView
+                    Document={selDoc}
+                    DataDoc={undefined}
+                    fieldKey="data"
+                    dropAction="embed"
+                    setHeight={returnFalse}
+                    styleProvider={DefaultStyleProvider}
+                    rootSelected={returnTrue}
+                    bringToFront={emptyFunction}
+                    select={emptyFunction}
+                    isContentActive={returnTrue}
+                    isAnyChildContentActive={returnFalse}
+                    isSelected={returnFalse}
+                    docViewPath={returnEmptyDoclist}
+                    moveDocument={returnFalse}
+                    addDocument={returnFalse}
+                    addDocTab={DocumentViewInternal.addDocTabFunc}
+                    pinToPres={emptyFunction}
+                    removeDocument={returnFalse}
+                    ScreenToLocalTransform={Transform.Identity}
+                    PanelWidth={() => 200}
+                    PanelHeight={() => 30}
+                    renderDepth={0}
+                    focus={emptyFunction}
+                    whenChildContentsActiveChanged={emptyFunction}
+                    childFilters={returnEmptyFilter}
+                    childFiltersByRanges={returnEmptyFilter}
+                    searchFilterDocs={returnEmptyDoclist}
+                />
+            </div>
+        );
+    }
+
     /**
      * Returns the center of the topbar
      * This part of the topbar contains everything related to the current dashboard including:
@@ -118,23 +158,10 @@ export class TopBar extends React.Component {
                     style={{ fontWeight: 700, fontSize: '1rem' }}
                     onClick={(e: React.MouseEvent) => {
                         const dashView = Doc.ActiveDashboard && DocumentManager.Instance.getDocumentView(Doc.ActiveDashboard);
-                        ContextMenu.Instance.addItem({ description: 'Open Dashboard View', event: this.navigateToHome, icon: 'edit' });
                         dashView?.showContextMenu(e.clientX + 20, e.clientY + 30);
                     }}
                 />
-                {!Doc.noviceMode && (
-                    <IconButton
-                        tooltip="Work on a copy of the dashboard layout"
-                        size={Size.SMALL}
-                        color={this.color}
-                        onClick={async () => {
-                            const batch = UndoManager.StartBatch('snapshot');
-                            await DashboardView.snapshotDashboard();
-                            batch.end();
-                        }}
-                        icon={<FaCamera />}
-                    />
-                )}
+                {!Doc.noviceMode && this.dashMenuButtons}
             </div>
         ) : null;
     }
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index ad0e548ed..2a1dfbfc7 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -220,6 +220,9 @@ export class Doc extends RefField {
     public static get MyContextMenuBtns() {
         return DocCast(Doc.UserDoc().myContextMenuBtns);
     }
+    public static get MyTopBarBtns() {
+        return DocCast(Doc.UserDoc().myTopBarBtns);
+    }
     public static get MyRecentlyClosed() {
         return DocCast(Doc.UserDoc().myRecentlyClosed);
     }
-- 
cgit v1.2.3-70-g09d2


From b7295d4747d8eb01d8213c97442cc4916aec77c4 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 31 Aug 2023 12:16:09 -0400
Subject: from last again

---
 src/client/util/DragManager.ts                     |  4 +---
 .../collectionLinear/CollectionLinearView.tsx      |  4 ++--
 src/client/views/nodes/FontIconBox/FontIconBox.tsx |  2 +-
 .../views/nodes/RecordingBox/RecordingBox.tsx      |  3 ---
 src/client/views/nodes/trails/PresBox.tsx          |  2 --
 src/client/views/nodes/trails/PresElementBox.tsx   | 24 +++++++---------------
 6 files changed, 11 insertions(+), 28 deletions(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 4ab033555..6d6eaebec 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -4,17 +4,15 @@ import { Doc, Field, Opt, StrListCast } from '../../fields/Doc';
 import { List } from '../../fields/List';
 import { PrefetchProxy } from '../../fields/Proxy';
 import { ScriptField } from '../../fields/ScriptField';
-import { BoolCast, Cast, DocCast, ScriptCast, StrCast } from '../../fields/Types';
+import { ScriptCast, StrCast } from '../../fields/Types';
 import { emptyFunction, Utils } from '../../Utils';
 import { Docs, DocUtils } from '../documents/Documents';
 import * as globalCssVariables from '../views/global/globalCssVariables.scss';
-import { Colors } from '../views/global/globalEnums';
 import { DocumentView } from '../views/nodes/DocumentView';
 import { ScriptingGlobals } from './ScriptingGlobals';
 import { SelectionManager } from './SelectionManager';
 import { SnappingManager } from './SnappingManager';
 import { UndoManager } from './UndoManager';
-import { listSpec } from '../../fields/Schema';
 
 export type dropActionType = 'embed' | 'copy' | 'move' | 'add' | 'same' | 'proto' | 'none' | undefined; // undefined = move, "same" = move but don't call dropPropertiesToRemove
 
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index 0cf7d4411..47a98bdd1 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -229,8 +229,8 @@ export class CollectionLinearView extends CollectionSubView() {
                 <div className="collectionLinearView" ref={this.createDashEventsTarget} onContextMenu={this.myContextMenu} style={{ minHeight: this.dimension(), pointerEvents: 'all' }}>
                     {
                         <>
-                            {!this.layoutDoc.linearView_Expandable ? null :menuOpener}
-                            {!this.layoutDoc.linearView_IsOpen && !this.layoutDoc.linearView_alwaysOpen ? null : (
+                            {!this.layoutDoc.linearView_Expandable ? null : menuOpener}
+                            {!this.layoutDoc.linearView_IsOpen ? null : (
                                 <div
                                     className="collectionLinearView-content"
                                     style={{
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index 8ef9cd792..9bc9f2b52 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -234,7 +234,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
 
         return (
             <Dropdown
-                selectedVal={text ?? 'Record Workspace'}
+                selectedVal={text}
                 setSelectedVal={undoable(val => script.script.run({ this: this.layoutDoc, self: this.rootDoc, val }), `dropdown select ${this.label}`)}
                 color={SettingsManager.userColor}
                 background={SettingsManager.userVariantColor}
diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
index 1b2f63fa2..e9060a605 100644
--- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
@@ -138,7 +138,6 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
         Doc.UserDoc().workspaceRecordingState = undefined;
     }
 
-    @undoBatch
     @action
     public static resumeWorkspaceReplaying(doc: Doc) {
         const docView = DocumentManager.Instance.getDocumentView(doc);
@@ -149,7 +148,6 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
     }
 
-    @undoBatch
     @action
     public static pauseWorkspaceReplaying(doc: Doc) {
         const docView = DocumentManager.Instance.getDocumentView(doc);
@@ -160,7 +158,6 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
         Doc.UserDoc().workspaceReplayingState = media_state.Paused;
     }
 
-    @undoBatch
     @action
     public static stopWorkspaceReplaying(value: Doc) {
         Doc.RemFromMyOverlay(value);
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 02132b7b3..7bb1b80a3 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -303,7 +303,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     // Called when the user activates 'next' - to move to the next part of the pres. trail
     @action
     next = () => {
-        console.log('next');
         const progressiveReveal = (first: boolean) => {
             const presIndexed = Cast(this.activeItem?.presentation_indexed, 'number', null);
             if (presIndexed !== undefined) {
@@ -386,7 +385,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     //it'll also execute the necessary actions if presentation is playing.
     @undoBatch
     public gotoDocument = action((index: number, from?: Doc, group?: boolean, finished?: () => void) => {
-        console.log('going to document');
         Doc.UnBrushAllDocs();
         if (index >= 0 && index < this.childDocs.length) {
             this.rootDoc._itemIndex = index;
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 121eb87d5..82ed9e8d5 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -6,7 +6,7 @@ import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
 import { Height, Width } from '../../../../fields/DocSymbols';
 import { Id } from '../../../../fields/FieldSymbols';
 import { List } from '../../../../fields/List';
-import { Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
+import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
 import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from '../../../../Utils';
 import { Docs } from '../../../documents/Documents';
 import { CollectionViewType } from '../../../documents/DocumentTypes';
@@ -313,17 +313,8 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @undoBatch
     @action
     revertToPreviouslySaved = (presTargetDoc: Doc, activeItem: Doc) => {
-        console.log('reverting');
-        // console.log("reverting to previosly saved\n");
-        // console.log(this.prevTarget);
-        console.log('Content continuously updating');
         const target = DocCast(activeItem.annotationOn) ?? activeItem;
-        console.log(presTargetDoc.pinData);
         PresBox.reversePin(activeItem, target);
-        // console.log("new target\n");
-        // console.log(target);
-        // PresBox.pinDocView(activeItem, { pinData: PresBox.pinDataTypes(this.prevTarget) }, this.prevTarget ? this.prevTarget : target);
-        //figure out how to make it go back to the previously saved one
     };
 
     /**
@@ -426,7 +417,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     lfg = (e: React.MouseEvent) => {
         e.stopPropagation();
-        console.log('lfg called');
         // TODO: fix this bug
         const { toggleChildrenRun } = this.rootDoc;
         TreeView.ToggleChildrenRun.get(this.rootDoc)?.();
@@ -445,12 +435,12 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
 
     @computed get presButtons() {
-        const presBox = this.presBox; //presBox
-        const presBoxColor: string = StrCast(presBox?._backgroundColor);
-        const presColorBool: boolean = presBoxColor ? presBoxColor !== Colors.WHITE && presBoxColor !== 'transparent' : false;
-        const targetDoc: Doc = this.targetDoc;
-        const activeItem: Doc = this.rootDoc;
-        const hasChildren: boolean = Cast(this.rootDoc?.hasChildren, 'boolean') || false;
+        const presBox = this.presBox;
+        const presBoxColor = StrCast(presBox?._backgroundColor);
+        const presColorBool = presBoxColor ? presBoxColor !== Colors.WHITE && presBoxColor !== 'transparent' : false;
+        const targetDoc = this.targetDoc;
+        const activeItem = this.rootDoc;
+        const hasChildren = BoolCast(this.rootDoc?.hasChildren);
 
         const items: JSX.Element[] = [];
 
-- 
cgit v1.2.3-70-g09d2


From a60edcbd8fd724ef9c228262370a7ad98f7aadd1 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 31 Aug 2023 15:44:46 -0400
Subject: added dragging off of recordings and option for webcam vs screen

---
 package-lock.json                                  | 304 +++++++++------------
 package.json                                       |   2 +-
 src/client/util/CurrentUserUtils.ts                |   4 +-
 src/client/views/nodes/FontIconBox/FontIconBox.tsx |  15 +-
 .../views/nodes/RecordingBox/RecordingBox.tsx      |  44 +--
 src/client/views/nodes/ScreenshotBox.tsx           |  13 +-
 6 files changed, 185 insertions(+), 197 deletions(-)

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

diff --git a/package-lock.json b/package-lock.json
index c3190ddb4..aae7c022c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -164,11 +164,11 @@
             },
             "dependencies": {
                 "@babel/code-frame": {
-                    "version": "7.22.10",
-                    "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.10.tgz",
-                    "integrity": "sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==",
+                    "version": "7.22.13",
+                    "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
+                    "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==",
                     "requires": {
-                        "@babel/highlight": "^7.22.10",
+                        "@babel/highlight": "^7.22.13",
                         "chalk": "^2.4.2"
                     }
                 },
@@ -184,9 +184,9 @@
                     }
                 },
                 "@babel/highlight": {
-                    "version": "7.22.10",
-                    "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.10.tgz",
-                    "integrity": "sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==",
+                    "version": "7.22.13",
+                    "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.13.tgz",
+                    "integrity": "sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==",
                     "requires": {
                         "@babel/helper-validator-identifier": "^7.22.5",
                         "chalk": "^2.4.2",
@@ -194,9 +194,9 @@
                     }
                 },
                 "@babel/parser": {
-                    "version": "7.22.11",
-                    "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.11.tgz",
-                    "integrity": "sha512-R5zb8eJIBPJriQtbH/htEQy4k7E2dHWlD2Y2VT07JCzwYZHBxV5ZYtM0UhXSNMT74LyxuM+b1jdL7pSesXbC/g=="
+                    "version": "7.22.14",
+                    "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.14.tgz",
+                    "integrity": "sha512-1KucTHgOvaw/LzCVrEOAyXkr9rQlp0A1HiHRYnSUE9dmb8PvPW7o5sscg+5169r54n3vGlbx6GevTE/Iw/P3AQ=="
                 },
                 "@babel/traverse": {
                     "version": "7.22.11",
@@ -579,11 +579,11 @@
             },
             "dependencies": {
                 "@babel/code-frame": {
-                    "version": "7.22.10",
-                    "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.10.tgz",
-                    "integrity": "sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==",
+                    "version": "7.22.13",
+                    "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
+                    "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==",
                     "requires": {
-                        "@babel/highlight": "^7.22.10",
+                        "@babel/highlight": "^7.22.13",
                         "chalk": "^2.4.2"
                     }
                 },
@@ -599,9 +599,9 @@
                     }
                 },
                 "@babel/highlight": {
-                    "version": "7.22.10",
-                    "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.10.tgz",
-                    "integrity": "sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==",
+                    "version": "7.22.13",
+                    "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.13.tgz",
+                    "integrity": "sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==",
                     "requires": {
                         "@babel/helper-validator-identifier": "^7.22.5",
                         "chalk": "^2.4.2",
@@ -609,9 +609,9 @@
                     }
                 },
                 "@babel/parser": {
-                    "version": "7.22.11",
-                    "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.11.tgz",
-                    "integrity": "sha512-R5zb8eJIBPJriQtbH/htEQy4k7E2dHWlD2Y2VT07JCzwYZHBxV5ZYtM0UhXSNMT74LyxuM+b1jdL7pSesXbC/g=="
+                    "version": "7.22.14",
+                    "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.14.tgz",
+                    "integrity": "sha512-1KucTHgOvaw/LzCVrEOAyXkr9rQlp0A1HiHRYnSUE9dmb8PvPW7o5sscg+5169r54n3vGlbx6GevTE/Iw/P3AQ=="
                 },
                 "@babel/traverse": {
                     "version": "7.22.11",
@@ -1822,9 +1822,9 @@
             }
         },
         "@babel/preset-env": {
-            "version": "7.22.10",
-            "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.10.tgz",
-            "integrity": "sha512-riHpLb1drNkpLlocmSyEg4oYJIQFeXAK/d7rI6mbD0XsvoTOOweXDmQPG/ErxsEhWk3rl3Q/3F6RFQlVFS8m0A==",
+            "version": "7.22.14",
+            "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.14.tgz",
+            "integrity": "sha512-daodMIoVo+ol/g+//c/AH+szBkFj4STQUikvBijRGL72Ph+w+AMTSh55DUETe8KJlPlDT1k/mp7NBfOuiWmoig==",
             "requires": {
                 "@babel/compat-data": "^7.22.9",
                 "@babel/helper-compilation-targets": "^7.22.10",
@@ -1852,41 +1852,41 @@
                 "@babel/plugin-syntax-top-level-await": "^7.14.5",
                 "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6",
                 "@babel/plugin-transform-arrow-functions": "^7.22.5",
-                "@babel/plugin-transform-async-generator-functions": "^7.22.10",
+                "@babel/plugin-transform-async-generator-functions": "^7.22.11",
                 "@babel/plugin-transform-async-to-generator": "^7.22.5",
                 "@babel/plugin-transform-block-scoped-functions": "^7.22.5",
                 "@babel/plugin-transform-block-scoping": "^7.22.10",
                 "@babel/plugin-transform-class-properties": "^7.22.5",
-                "@babel/plugin-transform-class-static-block": "^7.22.5",
+                "@babel/plugin-transform-class-static-block": "^7.22.11",
                 "@babel/plugin-transform-classes": "^7.22.6",
                 "@babel/plugin-transform-computed-properties": "^7.22.5",
                 "@babel/plugin-transform-destructuring": "^7.22.10",
                 "@babel/plugin-transform-dotall-regex": "^7.22.5",
                 "@babel/plugin-transform-duplicate-keys": "^7.22.5",
-                "@babel/plugin-transform-dynamic-import": "^7.22.5",
+                "@babel/plugin-transform-dynamic-import": "^7.22.11",
                 "@babel/plugin-transform-exponentiation-operator": "^7.22.5",
-                "@babel/plugin-transform-export-namespace-from": "^7.22.5",
+                "@babel/plugin-transform-export-namespace-from": "^7.22.11",
                 "@babel/plugin-transform-for-of": "^7.22.5",
                 "@babel/plugin-transform-function-name": "^7.22.5",
-                "@babel/plugin-transform-json-strings": "^7.22.5",
+                "@babel/plugin-transform-json-strings": "^7.22.11",
                 "@babel/plugin-transform-literals": "^7.22.5",
-                "@babel/plugin-transform-logical-assignment-operators": "^7.22.5",
+                "@babel/plugin-transform-logical-assignment-operators": "^7.22.11",
                 "@babel/plugin-transform-member-expression-literals": "^7.22.5",
                 "@babel/plugin-transform-modules-amd": "^7.22.5",
-                "@babel/plugin-transform-modules-commonjs": "^7.22.5",
-                "@babel/plugin-transform-modules-systemjs": "^7.22.5",
+                "@babel/plugin-transform-modules-commonjs": "^7.22.11",
+                "@babel/plugin-transform-modules-systemjs": "^7.22.11",
                 "@babel/plugin-transform-modules-umd": "^7.22.5",
                 "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5",
                 "@babel/plugin-transform-new-target": "^7.22.5",
-                "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.5",
-                "@babel/plugin-transform-numeric-separator": "^7.22.5",
-                "@babel/plugin-transform-object-rest-spread": "^7.22.5",
+                "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.11",
+                "@babel/plugin-transform-numeric-separator": "^7.22.11",
+                "@babel/plugin-transform-object-rest-spread": "^7.22.11",
                 "@babel/plugin-transform-object-super": "^7.22.5",
-                "@babel/plugin-transform-optional-catch-binding": "^7.22.5",
-                "@babel/plugin-transform-optional-chaining": "^7.22.10",
+                "@babel/plugin-transform-optional-catch-binding": "^7.22.11",
+                "@babel/plugin-transform-optional-chaining": "^7.22.12",
                 "@babel/plugin-transform-parameters": "^7.22.5",
                 "@babel/plugin-transform-private-methods": "^7.22.5",
-                "@babel/plugin-transform-private-property-in-object": "^7.22.5",
+                "@babel/plugin-transform-private-property-in-object": "^7.22.11",
                 "@babel/plugin-transform-property-literals": "^7.22.5",
                 "@babel/plugin-transform-regenerator": "^7.22.10",
                 "@babel/plugin-transform-reserved-words": "^7.22.5",
@@ -1900,7 +1900,7 @@
                 "@babel/plugin-transform-unicode-regex": "^7.22.5",
                 "@babel/plugin-transform-unicode-sets-regex": "^7.22.5",
                 "@babel/preset-modules": "0.1.6-no-external-plugins",
-                "@babel/types": "^7.22.10",
+                "@babel/types": "^7.22.11",
                 "babel-plugin-polyfill-corejs2": "^0.4.5",
                 "babel-plugin-polyfill-corejs3": "^0.8.3",
                 "babel-plugin-polyfill-regenerator": "^0.5.2",
@@ -2711,11 +2711,11 @@
             }
         },
         "@floating-ui/react-dom": {
-            "version": "2.0.1",
-            "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.1.tgz",
-            "integrity": "sha512-rZtAmSht4Lry6gdhAJDrCp/6rKN7++JnL1/Anbr/DdeyYXQPxvg/ivrbYvJulbRf4vL8b212suwMM2lxbv+RQA==",
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.2.tgz",
+            "integrity": "sha512-5qhlDvjaLmAst/rKb3VdlCinwTF4EYMiVxuuc/HVUjs46W0zgtbMmAZ1UTsDrRTxRmUEzl92mOtWbeeXL26lSQ==",
             "requires": {
-                "@floating-ui/dom": "^1.3.0"
+                "@floating-ui/dom": "^1.5.1"
             }
         },
         "@floating-ui/utils": {
@@ -3232,9 +3232,9 @@
             }
         },
         "@mui/styled-engine-sc": {
-            "version": "5.14.6",
-            "resolved": "https://registry.npmjs.org/@mui/styled-engine-sc/-/styled-engine-sc-5.14.6.tgz",
-            "integrity": "sha512-7/KXXdDLjpQAmbmIhUs1x7nzqooEiHkidQOXCIH04NiVa4KRxP4v/bOWV/5GpgZi1Aky5ruf9IVyH3jxYIW3JA==",
+            "version": "5.14.7",
+            "resolved": "https://registry.npmjs.org/@mui/styled-engine-sc/-/styled-engine-sc-5.14.7.tgz",
+            "integrity": "sha512-so3X2N0SxpXjc61+d6s0aG11OVF9XTDrKusaXd8YE1d9fzEuIGEpvOXizaMJZThqkTqGzNKQuXFEpC55Vb0gdw==",
             "requires": {
                 "@babel/runtime": "^7.22.10",
                 "prop-types": "^15.8.1"
@@ -6537,9 +6537,9 @@
             }
         },
         "browndash-components": {
-            "version": "0.1.2",
-            "resolved": "https://registry.npmjs.org/browndash-components/-/browndash-components-0.1.2.tgz",
-            "integrity": "sha512-5ONrqd6qYYMjWuayhhdwU4tlrtr+7c0n9MAMXg4jccdd9OxnmdlWpjPzOBYl3slE+mSpKcycwKarwYND+eM0NA==",
+            "version": "0.1.21",
+            "resolved": "https://registry.npmjs.org/browndash-components/-/browndash-components-0.1.21.tgz",
+            "integrity": "sha512-xPHuliete1jNXCGYZrVxuKUDmlanleFLWjZm8dC3vII0BT7jNPxMRZyYTxgc/PjYMRgM/BY0FCUPFTrsiYRawQ==",
             "requires": {
                 "@emotion/react": "^11.11.1",
                 "@emotion/styled": "^11.11.0",
@@ -6657,15 +6657,15 @@
                     "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww=="
                 },
                 "@mui/base": {
-                    "version": "5.0.0-beta.12",
-                    "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.12.tgz",
-                    "integrity": "sha512-tZjjXNAyUpwSDT1uRliZMhRQkWYzELJ8Qi61EuOMRpi36HIwnK2T7Nr4RI423Sv8G2EEikDAZj7je33eNd73NQ==",
+                    "version": "5.0.0-beta.13",
+                    "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.13.tgz",
+                    "integrity": "sha512-uC0l97pBspfDAp+iz2cJq8YZ8Sd9i73V77+WzUiOAckIVEyCm5dyVDZCCO2/phmzckVEeZCGcytybkjMQuhPQw==",
                     "requires": {
                         "@babel/runtime": "^7.22.10",
                         "@emotion/is-prop-valid": "^1.2.1",
                         "@floating-ui/react-dom": "^2.0.1",
                         "@mui/types": "^7.2.4",
-                        "@mui/utils": "^5.14.6",
+                        "@mui/utils": "^5.14.7",
                         "@popperjs/core": "^2.11.8",
                         "clsx": "^2.0.0",
                         "prop-types": "^15.8.1",
@@ -6683,21 +6683,21 @@
                     }
                 },
                 "@mui/core-downloads-tracker": {
-                    "version": "5.14.6",
-                    "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.6.tgz",
-                    "integrity": "sha512-QZEU3pyGWLuaHbxvOlShol7U1FVgzWBR0OH9H8D7L8w4/vto5N5jJVvlqFQS3T0zbR6YGHxFaiL6Ky87jQg7aw=="
+                    "version": "5.14.7",
+                    "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.7.tgz",
+                    "integrity": "sha512-sCWTUNElBPgB30iLvWe3PU7SIlTKZNf6/E/sko85iHVeHCM6WPkDw+y89CrZYjhFNmPqt2fIQM/pZu+rP2lFLA=="
                 },
                 "@mui/material": {
-                    "version": "5.14.6",
-                    "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.14.6.tgz",
-                    "integrity": "sha512-C3UgGrmtvcGkQkm0ONBU7bTdapTjQc2Se3b2354xMmU7lgSgW7VM6EP9wIH5XqqoJ60m9l/s9kbTWX0Y+EaWvA==",
+                    "version": "5.14.7",
+                    "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.14.7.tgz",
+                    "integrity": "sha512-jIZj9F7zMv6IlyaYDVv5M2Kp20jIX8c0kzuwteySHS/A0IvPVyomQEPtWc51MCbpDNCqzwoZUp3rQtA2lI8k7A==",
                     "requires": {
                         "@babel/runtime": "^7.22.10",
-                        "@mui/base": "5.0.0-beta.12",
-                        "@mui/core-downloads-tracker": "^5.14.6",
-                        "@mui/system": "^5.14.6",
+                        "@mui/base": "5.0.0-beta.13",
+                        "@mui/core-downloads-tracker": "^5.14.7",
+                        "@mui/system": "^5.14.7",
                         "@mui/types": "^7.2.4",
-                        "@mui/utils": "^5.14.6",
+                        "@mui/utils": "^5.14.7",
                         "@types/react-transition-group": "^4.4.6",
                         "clsx": "^2.0.0",
                         "csstype": "^3.1.2",
@@ -6717,12 +6717,12 @@
                     }
                 },
                 "@mui/private-theming": {
-                    "version": "5.14.6",
-                    "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.14.6.tgz",
-                    "integrity": "sha512-3VBLFGizBXfofyk33bwRg6t9L648aKnLmOKPfY1wFuiXq3AEYwobK65iDci/tHKxm/VKbZ6A7PFjLejvB3EvRQ==",
+                    "version": "5.14.7",
+                    "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.14.7.tgz",
+                    "integrity": "sha512-Y86+hmDnJab2Ka42PgxKpK3oL7EiacbeeX3X/lG9LGO0wSc45wZjHeTfIlVSkkUCkexiMKEJp5NlSjZhr27NRQ==",
                     "requires": {
                         "@babel/runtime": "^7.22.10",
-                        "@mui/utils": "^5.14.6",
+                        "@mui/utils": "^5.14.7",
                         "prop-types": "^15.8.1"
                     },
                     "dependencies": {
@@ -6737,9 +6737,9 @@
                     }
                 },
                 "@mui/styled-engine": {
-                    "version": "5.14.6",
-                    "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.14.6.tgz",
-                    "integrity": "sha512-I6zeu/OP1Hk4NsX1Oj85TiYl1dER0JMsLJVn76J1Ihl24A5EbiZQKJp3Mn+ufA79ypkdAvM9aQCAQyiVBFcUHg==",
+                    "version": "5.14.7",
+                    "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.14.7.tgz",
+                    "integrity": "sha512-hKBETEDsIAkL8/mBwPiQj/vw28OeIhMXC3Tvj4J2bb9snxAKpiZioR1PwqP+6P41twsC/GKBd0Vr9oaWYaHuMg==",
                     "requires": {
                         "@babel/runtime": "^7.22.10",
                         "@emotion/cache": "^11.11.0",
@@ -6758,15 +6758,15 @@
                     }
                 },
                 "@mui/system": {
-                    "version": "5.14.6",
-                    "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.14.6.tgz",
-                    "integrity": "sha512-/n0ae1MegWjiV1BpRU8jgg4E0zBjeB2VYsT/68ag/xaDuq3/TaDKJeT9REIvyBvwlG3CI3S2O+tRELktxCD1kg==",
+                    "version": "5.14.7",
+                    "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.14.7.tgz",
+                    "integrity": "sha512-jeZtHglc+Pi6qjGoopT6O4RqYXVBMqHVOsjMGP0hxGSSPm1T4gsAu7jU8eqGx9YwwjvvJ0eotTjFqw7iJ6qE2Q==",
                     "requires": {
                         "@babel/runtime": "^7.22.10",
-                        "@mui/private-theming": "^5.14.6",
-                        "@mui/styled-engine": "^5.14.6",
+                        "@mui/private-theming": "^5.14.7",
+                        "@mui/styled-engine": "^5.14.7",
                         "@mui/types": "^7.2.4",
-                        "@mui/utils": "^5.14.6",
+                        "@mui/utils": "^5.14.7",
                         "clsx": "^2.0.0",
                         "csstype": "^3.1.2",
                         "prop-types": "^15.8.1"
@@ -6783,9 +6783,9 @@
                     }
                 },
                 "@mui/utils": {
-                    "version": "5.14.6",
-                    "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.14.6.tgz",
-                    "integrity": "sha512-AznpqLu6hrFnpHgcvsSSMCG+cDbkcCYfo+daUwBVReNYv4l+NQ8+wvBAF4aUMi155N7xWbbgh0cyKs6Wdsm3aA==",
+                    "version": "5.14.7",
+                    "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.14.7.tgz",
+                    "integrity": "sha512-RtheP/aBoPogVdi8vj8Vo2IFnRa4mZVmnD0RGlVZ49yF60rZs+xP4/KbpIrTr83xVs34QmHQ2aQ+IX7I0a0dDw==",
                     "requires": {
                         "@babel/runtime": "^7.22.10",
                         "@types/prop-types": "^15.7.5",
@@ -7094,97 +7094,12 @@
                                         "strip-ansi": "^7.0.1"
                                     }
                                 },
-                                "string-width-cjs": {
-                                    "version": "npm:string-width@4.2.3",
-                                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
-                                    "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-                                    "requires": {
-                                        "emoji-regex": "^8.0.0",
-                                        "is-fullwidth-code-point": "^3.0.0",
-                                        "strip-ansi": "^6.0.1"
-                                    },
-                                    "dependencies": {
-                                        "ansi-regex": {
-                                            "version": "5.0.1",
-                                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-                                            "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
-                                        },
-                                        "emoji-regex": {
-                                            "version": "8.0.0",
-                                            "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-                                            "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
-                                        },
-                                        "strip-ansi": {
-                                            "version": "6.0.1",
-                                            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-                                            "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-                                            "requires": {
-                                                "ansi-regex": "^5.0.1"
-                                            }
-                                        }
-                                    }
-                                },
                                 "strip-ansi": {
                                     "version": "7.1.0",
                                     "bundled": true,
                                     "requires": {
                                         "ansi-regex": "^6.0.1"
                                     }
-                                },
-                                "strip-ansi-cjs": {
-                                    "version": "npm:strip-ansi@6.0.1",
-                                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-                                    "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-                                    "requires": {
-                                        "ansi-regex": "^5.0.1"
-                                    },
-                                    "dependencies": {
-                                        "ansi-regex": {
-                                            "version": "5.0.1",
-                                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-                                            "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
-                                        }
-                                    }
-                                },
-                                "wrap-ansi-cjs": {
-                                    "version": "npm:wrap-ansi@7.0.0",
-                                    "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
-                                    "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
-                                    "requires": {
-                                        "ansi-styles": "^4.0.0",
-                                        "string-width": "^4.1.0",
-                                        "strip-ansi": "^6.0.0"
-                                    },
-                                    "dependencies": {
-                                        "ansi-regex": {
-                                            "version": "5.0.1",
-                                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-                                            "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
-                                        },
-                                        "emoji-regex": {
-                                            "version": "8.0.0",
-                                            "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-                                            "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
-                                        },
-                                        "string-width": {
-                                            "version": "4.2.3",
-                                            "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
-                                            "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-                                            "requires": {
-                                                "emoji-regex": "^8.0.0",
-                                                "is-fullwidth-code-point": "^3.0.0",
-                                                "strip-ansi": "^6.0.1"
-                                            }
-                                        },
-                                        "strip-ansi": {
-                                            "version": "6.0.1",
-                                            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-                                            "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-                                            "requires": {
-                                                "ansi-regex": "^5.0.1"
-                                            }
-                                        }
-                                    }
                                 }
                             }
                         },
@@ -8689,7 +8604,7 @@
                             }
                         },
                         "string-width-cjs": {
-                            "version": "npm:string-width-cjs@4.2.3",
+                            "version": "npm:string-width@4.2.3",
                             "bundled": true,
                             "requires": {
                                 "emoji-regex": "^8.0.0",
@@ -8712,7 +8627,7 @@
                             }
                         },
                         "strip-ansi-cjs": {
-                            "version": "npm:strip-ansi-cjs@6.0.1",
+                            "version": "npm:strip-ansi@6.0.1",
                             "bundled": true,
                             "requires": {
                                 "ansi-regex": "^5.0.1"
@@ -8871,7 +8786,7 @@
                             }
                         },
                         "wrap-ansi-cjs": {
-                            "version": "npm:wrap-ansi-cjs@7.0.0",
+                            "version": "npm:wrap-ansi@7.0.0",
                             "bundled": true,
                             "requires": {
                                 "ansi-styles": "^4.0.0",
@@ -8903,9 +8818,9 @@
                     "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g=="
                 },
                 "postcss": {
-                    "version": "8.4.28",
-                    "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.28.tgz",
-                    "integrity": "sha512-Z7V5j0cq8oEKyejIKfpD8b4eBy9cwW2JWPk0+fB1HOAMsfHbnAXLLS+PfVWlzMSLQaWttKDt607I0XHmpE67Vw==",
+                    "version": "8.4.29",
+                    "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz",
+                    "integrity": "sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==",
                     "requires": {
                         "nanoid": "^3.3.6",
                         "picocolors": "^1.0.0",
@@ -10081,14 +9996,14 @@
                     }
                 },
                 "caniuse-lite": {
-                    "version": "1.0.30001524",
-                    "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001524.tgz",
-                    "integrity": "sha512-Jj917pJtYg9HSJBF95HVX3Cdr89JUyLT4IZ8SvM5aDRni95swKgYi3TgYLH5hnGfPE/U1dg6IfZ50UsIlLkwSA=="
+                    "version": "1.0.30001525",
+                    "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001525.tgz",
+                    "integrity": "sha512-/3z+wB4icFt3r0USMwxujAqRvaD/B7rvGTsKhbhSQErVrJvkZCLhgNLJxU8MevahQVH6hCU9FsHdNUFbiwmE7Q=="
                 },
                 "electron-to-chromium": {
-                    "version": "1.4.503",
-                    "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.503.tgz",
-                    "integrity": "sha512-LF2IQit4B0VrUHFeQkWhZm97KuJSGF2WJqq1InpY+ECpFRkXd8yTIaTtJxsO0OKDmiBYwWqcrNaXOurn2T2wiA=="
+                    "version": "1.4.506",
+                    "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.506.tgz",
+                    "integrity": "sha512-xxGct4GPAKSRlrLBtJxJFYy74W11zX6PO9GyHgl/U+2s3Dp0ZEwAklDfNHXOWcvH7zWMpsmgbR0ggEuaYAVvHA=="
                 }
             }
         },
@@ -10445,6 +10360,16 @@
             "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
             "dev": true
         },
+        "d": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+            "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+            "dev": true,
+            "requires": {
+                "es5-ext": "^0.10.50",
+                "type": "^1.0.1"
+            }
+        },
         "d3": {
             "version": "7.8.4",
             "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.4.tgz",
@@ -11825,6 +11750,28 @@
                 "is-symbol": "^1.0.2"
             }
         },
+        "es5-ext": {
+            "version": "0.10.62",
+            "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz",
+            "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==",
+            "dev": true,
+            "requires": {
+                "es6-iterator": "^2.0.3",
+                "es6-symbol": "^3.1.3",
+                "next-tick": "^1.1.0"
+            }
+        },
+        "es6-iterator": {
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
+            "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
+            "dev": true,
+            "requires": {
+                "d": "1",
+                "es5-ext": "^0.10.35",
+                "es6-symbol": "^3.1.1"
+            }
+        },
         "es6-promise": {
             "version": "3.2.1",
             "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz",
@@ -11836,6 +11783,7 @@
             "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
             "dev": true,
             "requires": {
+                "d": "^1.0.1",
                 "ext": "^1.1.2"
             }
         },
@@ -27648,6 +27596,12 @@
             "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
             "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
         },
+        "type": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
+            "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
+            "dev": true
+        },
         "type-check": {
             "version": "0.4.0",
             "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
diff --git a/package.json b/package.json
index 5f7289719..de44b31f3 100644
--- a/package.json
+++ b/package.json
@@ -178,7 +178,7 @@
         "body-parser": "^1.19.2",
         "bootstrap": "^4.6.1",
         "brotli": "^1.3.3",
-        "browndash-components": "^0.1.02",
+        "browndash-components": "^0.1.21",
         "browser-assert": "^1.2.1",
         "bson": "^4.6.1",
         "canvas": "^2.9.3",
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 539c62833..9f83f105e 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -773,8 +773,8 @@ export class CurrentUserUtils {
                             CurrentUserUtils.createToolButton(opts), scripts, funcs);
         
         const btnDescs = [// setup reactions to change the highlights on the undo/redo buttons -- would be better to encode this in the undo/redo buttons, but the undo/redo stacks are not wired up that way yet
-            { opts: { title: "Replicate",icon:"camera",toolTip: "Copy dashboard layout",btnType: ButtonType.ClickButton, expertMode: true},                                                                                               scripts: { onClick:  `snapshotDashboard()`}},
-            { opts: { title: "Recordings",            toolTip: "Workspace Recordings", btnType: ButtonType.DropdownList,expertMode: false, ignoreClick: true, width: 100}, funcs: {hidden: `false`, btnList:`getWorkspaceRecordings()`}, scripts: { script:  `{ return replayWorkspace(value, _readOnly_); }`}},
+            { opts: { title: "Replicate",icon:"camera",toolTip: "Copy dashboard layout",btnType: ButtonType.ClickButton, expertMode: true},                                                                                              scripts: { onClick:  `snapshotDashboard()`}},
+            { opts: { title: "Recordings",            toolTip: "Workspace Recordings", btnType: ButtonType.DropdownList,expertMode: false, ignoreClick: true, width: 100}, funcs: {hidden: `false`, btnList:`getWorkspaceRecordings()`}, scripts: { script:  `{ return replayWorkspace(value, _readOnly_); }`, onDragScript: `{ return startRecordingDrag(value); }`}},
             { opts: { title: "Stop Rec",icon: "stop", toolTip: "Stop recording",       btnType: ButtonType.ClickButton, expertMode: false}, funcs: {hidden: `!isWorkspaceRecording()`},                                                  scripts: { onClick: `stopWorkspaceRecording()`}},
             { opts: { title: "Play",    icon: "play", toolTip: "Play recording",       btnType: ButtonType.ClickButton, expertMode: false}, funcs: {hidden: `isWorkspaceReplaying() !== "${media_state.Paused}"`},                       scripts: { onClick: `resumeWorkspaceReplaying(getCurrentRecording())`}},
             { opts: { title: "Pause",   icon: "pause",toolTip: "Pause playback",       btnType: ButtonType.ClickButton, expertMode: false}, funcs: {hidden: `isWorkspaceReplaying() !== "${media_state.Playing}"`},                      scripts: { onClick: `pauseWorkspaceReplaying(getCurrentRecording())`}},
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index 9bc9f2b52..ea7c2de82 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -7,7 +7,7 @@ import * as React from 'react';
 import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc';
 import { ScriptField } from '../../../../fields/ScriptField';
 import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { Utils } from '../../../../Utils';
+import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../Utils';
 import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
 import { SelectionManager } from '../../../util/SelectionManager';
 import { SettingsManager } from '../../../util/SettingsManager';
@@ -176,6 +176,18 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         );
     }
 
+    dropdownItemDown = (e: React.PointerEvent, value: string | number) => {
+        setupMoveUpEvents(
+            this,
+            e,
+            (e: PointerEvent) => {
+                return ScriptCast(this.rootDoc.onDragScript)?.script.run({ this: this.layoutDoc, self: this.rootDoc, value: { doc: value, e } }).result;
+            },
+            emptyFunction,
+            emptyFunction
+        );
+    };
+
     /**
      * Dropdown list
      */
@@ -240,6 +252,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 background={SettingsManager.userVariantColor}
                 type={Type.TERT}
                 dropdownType={DropdownType.SELECT}
+                onItemDown={this.dropdownItemDown}
                 items={list}
                 tooltip={this.label}
                 fillWidth
diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
index 3412b2dd2..1f113110b 100644
--- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
@@ -1,10 +1,11 @@
 import { action, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
+import { DateField } from '../../../../fields/DateField';
 import { Doc, DocListCast } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
-import { listSpec } from '../../../../fields/Schema';
-import { BoolCast, Cast, DocCast, StrCast } from '../../../../fields/Types';
+import { List } from '../../../../fields/List';
+import { BoolCast, DocCast } from '../../../../fields/Types';
 import { VideoField } from '../../../../fields/URLField';
 import { Upload } from '../../../../server/SharedMediaTypes';
 import { Docs } from '../../../documents/Documents';
@@ -17,12 +18,10 @@ import { Presentation } from '../../../util/TrackMovements';
 import { undoBatch } from '../../../util/UndoManager';
 import { CollectionFreeFormView } from '../../collections/collectionFreeForm/CollectionFreeFormView';
 import { ViewBoxBaseComponent } from '../../DocComponent';
+import { media_state } from '../AudioBox';
 import { FieldView, FieldViewProps } from '../FieldView';
 import { VideoBox } from '../VideoBox';
 import { RecordingView } from './RecordingView';
-import { DateField } from '../../../../fields/DateField';
-import { media_state } from '../AudioBox';
-import { List } from '../../../../fields/List';
 
 @observer
 export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -87,15 +86,22 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
      */
     @undoBatch
     @action
-    public static WorkspaceStartRecording() {
-        const screengrabber = Docs.Create.ScreenshotDocument({
-            title: `${new DateField()}-${Doc.ActiveDashboard?.title ?? ''}`,
-            _width: 205,
-            _height: 115,
-        });
+    public static WorkspaceStartRecording(value: string) {
+        const screengrabber =
+            value === 'Record Workspace'
+                ? Docs.Create.ScreenshotDocument({
+                      title: `${new DateField()}-${Doc.ActiveDashboard?.title ?? ''}`,
+                      _width: 205,
+                      _height: 115,
+                  })
+                : Docs.Create.WebCamDocument(`${new DateField()}-${Doc.ActiveDashboard?.title ?? ''}`, {
+                      title: `${new DateField()}-${Doc.ActiveDashboard?.title ?? ''}`,
+                      _width: 205,
+                      _height: 115,
+                  });
         screengrabber.overlayX = 70; //was -400
         screengrabber.overlayY = 590; //was 0
-        screengrabber[Doc.LayoutFieldKey(screengrabber) + '_trackScreen'] = true;
+        Doc.GetProto(screengrabber)[Doc.LayoutFieldKey(screengrabber) + '_trackScreen'] = true;
         Doc.AddToMyOverlay(screengrabber); //just adds doc to overlay
         DocumentManager.Instance.AddViewRenderedCb(screengrabber, docView => {
             RecordingBox.screengrabber = docView.ComponentView as RecordingBox;
@@ -204,9 +210,6 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
     static screengrabber: RecordingBox | undefined;
 }
 
-ScriptingGlobals.add(function startWorkspaceRecording() {
-    RecordingBox.WorkspaceStartRecording();
-});
 ScriptingGlobals.add(function stopWorkspaceRecording() {
     RecordingBox.WorkspaceStopRecording();
 });
@@ -222,7 +225,7 @@ ScriptingGlobals.add(function getCurrentRecording() {
     return Doc.UserDoc().currentRecording;
 });
 ScriptingGlobals.add(function getWorkspaceRecordings() {
-    return new List<any>(['Record Workspace', ...DocListCast(Doc.UserDoc().workspaceRecordings)]);
+    return new List<any>(['Record Workspace', `Record Webcam`, ...DocListCast(Doc.UserDoc().workspaceRecordings)]);
 });
 ScriptingGlobals.add(function isWorkspaceRecording() {
     return Doc.UserDoc().workspaceRecordingState === media_state.Recording;
@@ -232,7 +235,7 @@ ScriptingGlobals.add(function isWorkspaceReplaying() {
 });
 ScriptingGlobals.add(function replayWorkspace(value: Doc | string, _readOnly_: boolean) {
     if (_readOnly_) return DocCast(Doc.UserDoc().currentRecording) ?? 'Record Workspace';
-    if (typeof value === 'string') RecordingBox.WorkspaceStartRecording();
+    if (typeof value === 'string') RecordingBox.WorkspaceStartRecording(value);
     else RecordingBox.replayWorkspace(value);
 });
 ScriptingGlobals.add(function pauseWorkspaceReplaying(value: Doc, _readOnly_: boolean) {
@@ -242,6 +245,13 @@ ScriptingGlobals.add(function resumeWorkspaceReplaying(value: Doc, _readOnly_: b
     RecordingBox.resumeWorkspaceReplaying(value);
 });
 
+ScriptingGlobals.add(function startRecordingDrag(value: { doc: Doc | string; e: React.PointerEvent }) {
+    if (DocCast(value.doc)) {
+        DragManager.StartDocumentDrag([value.e.target as HTMLElement], new DragManager.DocumentDragData([DocCast(value.doc)], 'embed'), value.e.clientX, value.e.clientY);
+        value.e.preventDefault();
+        return true;
+    }
+});
 ScriptingGlobals.add(function renderDropdown() {
     if (!Doc.UserDoc().workspaceRecordings || DocListCast(Doc.UserDoc().workspaceRecordings).length === 0) {
         return true;
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 4ebc93165..26ad8b7bb 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -26,6 +26,7 @@ import { FieldView, FieldViewProps } from './FieldView';
 import { FormattedTextBox } from './formattedText/FormattedTextBox';
 import './ScreenshotBox.scss';
 import { VideoBox } from './VideoBox';
+import { TrackMovements } from '../../util/TrackMovements';
 
 declare class MediaRecorder {
     constructor(e: any, options?: any); // whatever MediaRecorder has
@@ -236,9 +237,19 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
             this._videoRef!.srcObject = await (navigator.mediaDevices as any).getDisplayMedia({ video: true });
             this._videoRec = new MediaRecorder(this._videoRef!.srcObject);
             const vid_chunks: any = [];
-            this._videoRec.onstart = () => (this.dataDoc[this.props.fieldKey + '_recordingStart'] = new DateField(new Date()));
+            this._videoRec.onstart = () => {
+                if (this.dataDoc[this.props.fieldKey + '_trackScreen']) TrackMovements.Instance.start();
+                this.dataDoc[this.props.fieldKey + '_recordingStart'] = new DateField(new Date());
+            };
             this._videoRec.ondataavailable = (e: any) => vid_chunks.push(e.data);
             this._videoRec.onstop = async (e: any) => {
+                const presentation = TrackMovements.Instance.yieldPresentation();
+                if (presentation?.movements) {
+                    const presCopy = { ...presentation };
+                    presCopy.movements = presentation.movements.map(movement => ({ ...movement, doc: movement.doc[Id] })) as any;
+                    this.dataDoc[this.fieldKey + '_presentation'] = JSON.stringify(presCopy);
+                }
+                TrackMovements.Instance.finish();
                 const file = new File(vid_chunks, `${this.rootDoc[Id]}.mkv`, { type: vid_chunks[0].type, lastModified: Date.now() });
                 const [{ result }] = await Networking.UploadFilesToServer({ file });
                 this.dataDoc[this.fieldKey + '_duration'] = (new Date().getTime() - this.recordingStart!) / 1000;
-- 
cgit v1.2.3-70-g09d2


From a63ab5802551887f62265d420b7c5e925b5cd7d6 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 5 Sep 2023 01:26:54 -0400
Subject: added ui for specifying wehther to play a/v when following links, and
 cleaned up ui/etc for pres trails.  fixed a bunch of colors in pres
 properties.  fixed a/v anchors to be configs when not adding them to the doc,
 otherwise labels.

---
 src/client/util/DocumentManager.ts                 |   5 +-
 src/client/util/LinkFollower.ts                    |   1 +
 src/client/views/MainView.tsx                      |   2 +-
 src/client/views/PropertiesView.tsx                |  14 +-
 src/client/views/UndoStack.tsx                     |  69 ++++----
 .../collections/CollectionStackedTimeline.tsx      |   6 +-
 src/client/views/collections/TabDocView.tsx        |   6 +-
 src/client/views/nodes/AudioBox.tsx                |  28 +--
 src/client/views/nodes/DocumentView.tsx            |   1 +
 src/client/views/nodes/FontIconBox/FontIconBox.tsx |   1 -
 src/client/views/nodes/VideoBox.tsx                |  13 +-
 src/client/views/nodes/trails/PresBox.scss         |   6 -
 src/client/views/nodes/trails/PresBox.tsx          | 192 ++++++++++++++-------
 src/client/views/selectedDoc/SelectedDocView.tsx   |  61 +++----
 14 files changed, 244 insertions(+), 161 deletions(-)

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

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 28ca37611..bfe0e1b48 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -1,9 +1,9 @@
-import { action, computed, observable, ObservableSet, observe, reaction } from 'mobx';
+import { action, computed, observable, ObservableSet, observe } from 'mobx';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
 import { AclAdmin, AclEdit, Animation } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { listSpec } from '../../fields/Schema';
-import { Cast, DocCast, StrCast } from '../../fields/Types';
+import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
 import { AudioField } from '../../fields/URLField';
 import { GetEffectiveAcl } from '../../fields/util';
 import { CollectionViewType } from '../documents/DocumentTypes';
@@ -323,6 +323,7 @@ export class DocumentManager {
             if (docView.ComponentView instanceof FormattedTextBox) docView.ComponentView?.focus(viewSpec, options);
             PresBox.restoreTargetDocView(docView, viewSpec, options.zoomTime ?? 500);
             Doc.linkFollowHighlight(viewSpec ? [docView.rootDoc, viewSpec] : docView.rootDoc, undefined, options.effect);
+            if (options.playMedia) docView.ComponentView?.playFrom?.(NumCast(docView.rootDoc._layout_currentTimecode));
             if (options.playAudio) DocumentManager.playAudioAnno(docView.rootDoc);
             if (options.toggleTarget && (!options.didMove || docView.rootDoc.hidden)) docView.rootDoc.hidden = !docView.rootDoc.hidden;
             if (options.effect) docView.rootDoc[Animation] = options.effect;
diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
index b8fea340f..2fc811b09 100644
--- a/src/client/util/LinkFollower.ts
+++ b/src/client/util/LinkFollower.ts
@@ -73,6 +73,7 @@ export class LinkFollower {
                     const toggleTarget = canToggle && BoolCast(sourceDoc.followLinkToggle);
                     const options: DocFocusOptions = {
                         playAudio: BoolCast(sourceDoc.followLinkAudio),
+                        playMedia: BoolCast(sourceDoc.followLinkVideo),
                         toggleTarget,
                         noSelect: true,
                         willPan: true,
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 7ae9a374d..c8b89c1d5 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -805,7 +805,7 @@ export class MainView extends React.Component {
                         {this.dockingContent}
 
                         {this._hideUI ? null : (
-                            <div className="mainView-propertiesDragger" key="props" onPointerDown={this.onPropertiesPointerDown} style={{ background: SettingsManager.userBackgroundColor, right: this.propertiesWidth() - 1 }}>
+                            <div className="mainView-propertiesDragger" key="props" onPointerDown={this.onPropertiesPointerDown} style={{ background: SettingsManager.userVariantColor, right: this.propertiesWidth() - 1 }}>
                                 <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? 'chevron-left' : 'chevron-right'} color={SettingsManager.userColor} size="sm" />
                             </div>
                         )}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 5f9439cbc..01329b482 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -1538,6 +1538,16 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                                 <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
                             </button>
                         </div>
+                        <div className="propertiesView-input inline">
+                            <p>Play Target Video</p>
+                            <button
+                                style={{ background: !this.sourceAnchor?.followLinkVideo ? '' : '#4476f7', borderRadius: 3 }}
+                                onPointerDown={e => this.toggleAnchorProp(e, 'followLinkVideo', this.sourceAnchor)}
+                                onClick={e => e.stopPropagation()}
+                                className="propertiesButton">
+                                <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+                            </button>
+                        </div>
                         <div className="propertiesView-input inline">
                             <p>Zoom Text Selections</p>
                             <button
@@ -1742,12 +1752,12 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                                         color: SettingsManager.userColor,
                                         backgroundColor: this.openPresVisibilityAndDuration ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor,
                                     }}>
-                                    &nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={'rocket'} /> &nbsp; Visibilty
+                                    &nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={'rocket'} /> &nbsp; Visibility
                                     <div className="propertiesView-presentationTrails-title-icon">
                                         <FontAwesomeIcon icon={this.openPresVisibilityAndDuration ? 'caret-down' : 'caret-right'} size="lg" />
                                     </div>
                                 </div>
-                                {this.openPresVisibilityAndDuration ? <div className="propertiesView-presentationTrails-content">{PresBox.Instance.visibiltyDurationDropdown}</div> : null}
+                                {this.openPresVisibilityAndDuration ? <div className="propertiesView-presentationTrails-content">{PresBox.Instance.visibilityDurationDropdown}</div> : null}
                             </div>
                         )}
                         {!selectedItem ? null : (
diff --git a/src/client/views/UndoStack.tsx b/src/client/views/UndoStack.tsx
index 47853b5e4..1afd5ad22 100644
--- a/src/client/views/UndoStack.tsx
+++ b/src/client/views/UndoStack.tsx
@@ -8,6 +8,7 @@ import { Doc } from '../../fields/Doc';
 import { Popup, Type, isDark } from 'browndash-components';
 import { Colors } from './global/globalEnums';
 import { SettingsManager } from '../util/SettingsManager';
+import { Tooltip } from '@mui/material';
 
 interface UndoStackProps {
     width?: number;
@@ -22,39 +23,43 @@ export class UndoStack extends React.Component<UndoStackProps> {
         const background = UndoManager.batchCounter.get() ? 'yellow' : SettingsManager.userVariantColor;
         const color = UndoManager.batchCounter.get() ? 'black' : SettingsManager.userColor;
         return this.props.inline && UndoStack.HideInline ? null : (
-            <div className="undoStack-outerContainer">
-                <Popup
-                    text="stack"
-                    color={color}
-                    background={background}
-                    placement={`top-start`}
-                    type={Type.TERT}
-                    popup={
-                        <div
-                            className="undoStack-commandsContainer"
-                            ref={r => r?.scroll({ behavior: 'auto', top: r?.scrollHeight + 20 })}
-                            style={{
-                                background,
-                                color,
-                            }}>
-                            {UndoManager.undoStackNames.map((name, i) => (
-                                <div className="undoStack-resultContainer" key={i}>
-                                    <div className="undoStack-commandString">{StrCast(name).replace(/[^\.]*\./, '')}</div>
-                                </div>
-                            ))}
-                            {Array.from(UndoManager.redoStackNames)
-                                .reverse()
-                                .map((name, i) => (
-                                    <div className="undoStack-resultContainer" key={i}>
-                                        <div className="undoStack-commandString" style={{ fontWeight: 'bold', color: SettingsManager.userBackgroundColor }}>
-                                            {StrCast(name).replace(/[^\.]*\./, '')}
+            <Tooltip title={'undo stack (if it stays yellow, undo is broken - you should reload Dash)'}>
+                <div>
+                    <div className="undoStack-outerContainer">
+                        <Popup
+                            text="stack"
+                            color={color}
+                            background={background}
+                            placement={`top-start`}
+                            type={Type.TERT}
+                            popup={
+                                <div
+                                    className="undoStack-commandsContainer"
+                                    ref={r => r?.scroll({ behavior: 'auto', top: r?.scrollHeight + 20 })}
+                                    style={{
+                                        background,
+                                        color,
+                                    }}>
+                                    {UndoManager.undoStackNames.map((name, i) => (
+                                        <div className="undoStack-resultContainer" key={i}>
+                                            <div className="undoStack-commandString">{StrCast(name).replace(/[^\.]*\./, '')}</div>
                                         </div>
-                                    </div>
-                                ))}
-                        </div>
-                    }
-                />
-            </div>
+                                    ))}
+                                    {Array.from(UndoManager.redoStackNames)
+                                        .reverse()
+                                        .map((name, i) => (
+                                            <div className="undoStack-resultContainer" key={i}>
+                                                <div className="undoStack-commandString" style={{ fontWeight: 'bold', color: SettingsManager.userBackgroundColor }}>
+                                                    {StrCast(name).replace(/[^\.]*\./, '')}
+                                                </div>
+                                            </div>
+                                        ))}
+                                </div>
+                            }
+                        />
+                    </div>
+                </div>
+            </Tooltip>
         );
     }
 }
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index d2be70577..0a5a80936 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -32,7 +32,7 @@ import './CollectionStackedTimeline.scss';
 export type CollectionStackedTimelineProps = {
     Play: () => void;
     Pause: () => void;
-    playLink: (linkDoc: Doc) => void;
+    playLink: (linkDoc: Doc, options: DocFocusOptions) => void;
     playFrom: (seekTimeInSeconds: number, endTime?: number) => void;
     playing: () => boolean;
     setTime: (time: number) => void;
@@ -677,7 +677,7 @@ interface StackedTimelineAnchorProps {
     height: number;
     toTimeline: (screen_delta: number, width: number) => number;
     styleProvider?: (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => any;
-    playLink: (linkDoc: Doc) => void;
+    playLink: (linkDoc: Doc, options: DocFocusOptions) => void;
     setTime: (time: number) => void;
     startTag: string;
     endTag: string;
@@ -793,7 +793,7 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
     renderInner = computedFn(function (this: StackedTimelineAnchor, mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), screenXf: () => Transform, width: () => number, height: () => number) {
         const anchor = observable({ view: undefined as any });
         const focusFunc = (doc: Doc, options: DocFocusOptions): number | undefined => {
-            this.props.playLink(mark);
+            this.props.playLink(mark, options);
             return undefined;
         };
         return {
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index f6acafa95..6cdb84dea 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -278,10 +278,8 @@ export class TabDocView extends React.Component<TabDocViewProps> {
 
             if (pinProps.pinViewport) PresBox.pinDocView(pinDoc, pinProps, anchorDoc ?? doc);
             if (!pinProps?.audioRange && duration !== undefined) {
-                pinDoc.mediaStart = 'manual';
-                pinDoc.mediaStop = 'manual';
-                pinDoc.config_clipStart = NumCast(doc.clipStart);
-                pinDoc.config_clipEnd = NumCast(doc.clipEnd, duration);
+                pinDoc.presentation_mediaStart = 'manual';
+                pinDoc.presentation_mediaStop = 'manual';
             }
             if (pinProps?.activeFrame !== undefined) {
                 pinDoc.config_activeFrame = pinProps?.activeFrame;
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 7c409c38c..50b2432d2 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -8,7 +8,7 @@ import { ComputedField } from '../../../fields/ScriptField';
 import { Cast, DateCast, NumCast } from '../../../fields/Types';
 import { AudioField, nullAudio } from '../../../fields/URLField';
 import { emptyFunction, formatTime, returnFalse, setupMoveUpEvents } from '../../../Utils';
-import { DocUtils } from '../../documents/Documents';
+import { Docs, DocUtils } from '../../documents/Documents';
 import { Networking } from '../../Network';
 import { DragManager } from '../../util/DragManager';
 import { LinkManager } from '../../util/LinkManager';
@@ -20,6 +20,7 @@ import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComp
 import './AudioBox.scss';
 import { FieldView, FieldViewProps } from './FieldView';
 import { PinProps, PresBox } from './trails';
+import { DocFocusOptions } from './DocumentView';
 
 /**
  * AudioBox
@@ -134,16 +135,19 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     }
 
     getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
-        const anchor =
-            CollectionStackedTimeline.createAnchor(
-                this.rootDoc,
-                this.dataDoc,
-                this.annotationKey,
-                this._ele?.currentTime || Cast(this.props.Document._layout_currentTimecode, 'number', null) || (this.mediaState === media_state.Recording ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined),
-                undefined,
-                undefined,
-                addAsAnnotation
-            ) || this.rootDoc;
+        const timecode = Cast(this.layoutDoc._layout_currentTimecode, 'number', null);
+        const anchor = addAsAnnotation
+            ? CollectionStackedTimeline.createAnchor(
+                  this.rootDoc,
+                  this.dataDoc,
+                  this.annotationKey,
+                  this._ele?.currentTime || Cast(this.props.Document._layout_currentTimecode, 'number', null) || (this.mediaState === media_state.Recording ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined),
+                  undefined,
+                  undefined,
+                  addAsAnnotation
+              ) || this.rootDoc
+            : Docs.Create.ConfigDocument({ title: '#' + timecode, _timecodeToShow: timecode, annotationOn: this.rootDoc });
+
         PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), temporal: true } }, this.rootDoc);
         return anchor;
     };
@@ -418,7 +422,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     };
 
     // plays link
-    playLink = (link: Doc) => {
+    playLink = (link: Doc, options: DocFocusOptions) => {
         if (link.annotationOn === this.rootDoc) {
             if (!this.layoutDoc.dontAutoPlayFollowedLinks) {
                 this.playFrom(this.timeline?.anchorStart(link) || 0, this.timeline?.anchorEnd(link));
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 998024cea..f7773ff18 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -100,6 +100,7 @@ export interface DocFocusOptions {
     effect?: Doc; // animation effect for focus
     noSelect?: boolean; // whether target should be selected after focusing
     playAudio?: boolean; // whether to play audio annotation on focus
+    playMedia?: boolean; // whether to play start target videos
     openLocation?: OpenWhere; // where to open a missing document
     zoomTextSelections?: boolean; // whether to display a zoomed overlay of anchor text selections
     toggleTarget?: boolean; // whether to toggle target on and off
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index ea7c2de82..1eb6fd51c 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -19,7 +19,6 @@ import { SelectedDocView } from '../../selectedDoc';
 import { StyleProp } from '../../StyleProvider';
 import { OpenWhere } from '../DocumentView';
 import { FieldView, FieldViewProps } from '../FieldView';
-import { RichTextMenu } from '../formattedText/RichTextMenu';
 import './FontIconBox.scss';
 import TrailsIcon from './TrailsIcon';
 
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 56508abf6..9d9aa8a4b 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -29,7 +29,7 @@ import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComp
 import { MarqueeAnnotator } from '../MarqueeAnnotator';
 import { AnchorMenu } from '../pdf/AnchorMenu';
 import { StyleProp } from '../StyleProvider';
-import { DocumentView, OpenWhere } from './DocumentView';
+import { DocFocusOptions, DocumentView, OpenWhere } from './DocumentView';
 import { FieldView, FieldViewProps } from './FieldView';
 import { RecordingBox } from './RecordingBox';
 import { PinProps, PresBox } from './trails';
@@ -385,7 +385,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         const timecode = Cast(this.layoutDoc._layout_currentTimecode, 'number', null);
         const marquee = AnchorMenu.Instance.GetAnchor?.(undefined, addAsAnnotation);
         if (!addAsAnnotation && marquee) marquee.backgroundColor = 'transparent';
-        const anchor = CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, timecode ? timecode : undefined, undefined, marquee, addAsAnnotation) || this.rootDoc;
+        const anchor = addAsAnnotation
+            ? CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, timecode ? timecode : undefined, undefined, marquee, addAsAnnotation) || this.rootDoc
+            : Docs.Create.ConfigDocument({ title: '#' + timecode, _timecodeToShow: timecode, annotationOn: this.rootDoc });
         PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), temporal: true } }, this.rootDoc);
         return anchor;
     };
@@ -846,11 +848,11 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     zoom = (zoom: number) => this.timeline?.setZoom(zoom);
 
     // plays link
-    playLink = (doc: Doc) => {
-        const startTime = Math.max(0, this._stackedTimeline?.anchorStart(doc) || 0);
+    playLink = (doc: Doc, options: DocFocusOptions) => {
+        const startTime = Math.max(0, NumCast(doc.config_clipStart, this._stackedTimeline?.anchorStart(doc) || 0));
         const endTime = this.timeline?.anchorEnd(doc);
         if (startTime !== undefined) {
-            if (!this.layoutDoc.dontAutoPlayFollowedLinks) endTime ? this.playFrom(startTime, endTime) : this.playFrom(startTime);
+            if (options.playMedia) endTime ? this.playFrom(startTime, endTime) : this.playFrom(startTime);
             else this.Seek(startTime);
         }
     };
@@ -1038,7 +1040,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         this.props.bringToFront(cropping);
         return cropping;
     };
-
     savedAnnotations = () => this._savedAnnotations;
     render() {
         const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding);
diff --git a/src/client/views/nodes/trails/PresBox.scss b/src/client/views/nodes/trails/PresBox.scss
index bf56b4d9e..31a003144 100644
--- a/src/client/views/nodes/trails/PresBox.scss
+++ b/src/client/views/nodes/trails/PresBox.scss
@@ -187,9 +187,6 @@
         font-size: 11;
         font-weight: 200;
         height: 20;
-        background-color: $white;
-        color: $black;
-        border: solid 1px $black;
         display: flex;
         margin-left: 5px;
         margin-top: 5px;
@@ -210,13 +207,11 @@
 
         .ribbon-propertyUpDownItem {
             cursor: pointer;
-            color: white;
             display: flex;
             justify-content: center;
             align-items: center;
             height: 100%;
             width: 100%;
-            background: $black;
         }
 
         .ribbon-propertyUpDownItem:hover {
@@ -609,7 +604,6 @@
         font-weight: 200;
         height: 20;
         background-color: $white;
-        border: solid 1px rgba(0, 0, 0, 0.5);
         display: flex;
         color: $black;
         margin-top: 5px;
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 92c130ea1..48f376075 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -391,11 +391,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             if (from?.mediaStopTriggerList && this.layoutDoc.presentation_status !== PresStatus.Edit) {
                 DocListCast(from.mediaStopTriggerList).forEach(this.stopTempMedia);
             }
-            if (from?.mediaStop === 'auto' && this.layoutDoc.presentation_status !== PresStatus.Edit) {
+            if (from?.presentation_mediaStop === 'auto' && this.layoutDoc.presentation_status !== PresStatus.Edit) {
                 this.stopTempMedia(from.presentation_targetDoc);
             }
             // If next slide is audio / video 'Play automatically' then the next slide should be played
-            if (this.layoutDoc.presentation_status !== PresStatus.Edit && (this.targetDoc.type === DocumentType.AUDIO || this.targetDoc.type === DocumentType.VID) && this.activeItem.mediaStart === 'auto') {
+            if (this.layoutDoc.presentation_status !== PresStatus.Edit && (this.targetDoc.type === DocumentType.AUDIO || this.targetDoc.type === DocumentType.VID) && this.activeItem.presentation_mediaStart === 'auto') {
                 this.startTempMedia(this.targetDoc, this.activeItem);
             }
             if (!group) this.clearSelectedArray();
@@ -798,8 +798,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             easeFunc: StrCast(activeItem.presEaseFunc, 'ease') as any,
             zoomTextSelections: BoolCast(activeItem.presentation_zoomText),
             playAudio: BoolCast(activeItem.presPlayAudio),
+            playMedia: activeItem.presentation_mediaStart === 'auto',
         };
-        if (activeItem.presOpenInLightbox) {
+        if (activeItem.presentation_openInLightbox) {
             const context = DocCast(targetDoc.annotationOn) ?? targetDoc;
             if (!DocumentManager.Instance.getLightboxDocumentView(context)) {
                 LightboxView.SetLightboxDoc(context);
@@ -1075,8 +1076,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             if (doc.type === DocumentType.LABEL) {
                 const audio = Cast(doc.annotationOn, Doc, null);
                 if (audio) {
-                    audio.mediaStart = 'manual';
-                    audio.mediaStop = 'manual';
                     audio.config_clipStart = NumCast(doc._timecodeToShow /* audioStart */, NumCast(doc._timecodeToShow /* videoStart */));
                     audio.config_clipEnd = NumCast(doc._timecodeToHide /* audioEnd */, NumCast(doc._timecodeToHide /* videoEnd */));
                     audio.presentation_duration = audio.config_clipStart - audio.config_clipEnd;
@@ -1472,8 +1471,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @undoBatch
     @action
     updateOpenDoc = (activeItem: Doc) => {
-        activeItem.presOpenInLightbox = !activeItem.presOpenInLightbox;
-        this.selectedArray.forEach(doc => (doc.presOpenInLightbox = activeItem.presOpenInLightbox));
+        activeItem.presentation_openInLightbox = !activeItem.presentation_openInLightbox;
+        this.selectedArray.forEach(doc => (doc.presentation_openInLightbox = activeItem.presentation_openInLightbox));
     };
 
     @undoBatch
@@ -1501,7 +1500,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 max={max}
                 value={value}
                 readOnly={true}
-                style={{ marginLeft: hmargin, marginRight: hmargin, width: `calc(100% - ${2 * (hmargin ?? 0)}px)` }}
+                style={{ marginLeft: hmargin, marginRight: hmargin, width: `calc(100% - ${2 * (hmargin ?? 0)}px)`, background: SettingsManager.userColor, color: SettingsManager.userVariantColor }}
                 className={`toolbar-slider ${active ? '' : 'none'}`}
                 onPointerDown={e => {
                     PresBox._sliderBatch = UndoManager.StartBatch('pres slider');
@@ -1532,7 +1531,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         });
     };
 
-    @computed get visibiltyDurationDropdown() {
+    @computed get visibilityDurationDropdown() {
         const activeItem = this.activeItem;
         if (activeItem && this.targetDoc) {
             const targetType = this.targetDoc.type;
@@ -1541,30 +1540,49 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             return (
                 <div className="presBox-ribbon">
                     <div className="ribbon-doubleButton">
-                        <Tooltip title={<div className="dash-tooltip">{'Hide before presented'}</div>}>
-                            <div className={`ribbon-toggle ${activeItem.presentation_hideBefore ? 'active' : ''}`} onClick={() => this.updateHideBefore(activeItem)}>
+                        <Tooltip title={<div className="dash-tooltip">Hide before presented</div>}>
+                            <div
+                                className={`ribbon-toggle ${activeItem.presentation_hideBefore ? 'active' : ''}`}
+                                style={{ border: `solid 1px ${SettingsManager.userColor}`, color: SettingsManager.userColor, background: activeItem.presentation_hideBefore ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor }}
+                                onClick={() => this.updateHideBefore(activeItem)}>
                                 Hide before
                             </div>
                         </Tooltip>
                         <Tooltip title={<div className="dash-tooltip">{'Hide while presented'}</div>}>
-                            <div className={`ribbon-toggle ${activeItem.presentation_hide ? 'active' : ''}`} onClick={() => this.updateHide(activeItem)}>
+                            <div
+                                className={`ribbon-toggle ${activeItem.presentation_hide ? 'active' : ''}`}
+                                style={{ border: `solid 1px ${SettingsManager.userColor}`, color: SettingsManager.userColor, background: activeItem.presentation_hide ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor }}
+                                onClick={() => this.updateHide(activeItem)}>
                                 Hide
                             </div>
                         </Tooltip>
 
                         <Tooltip title={<div className="dash-tooltip">{'Hide after presented'}</div>}>
-                            <div className={`ribbon-toggle ${activeItem.presentation_hideAfter ? 'active' : ''}`} onClick={() => this.updateHideAfter(activeItem)}>
+                            <div
+                                className={`ribbon-toggle ${activeItem.presentation_hideAfter ? 'active' : ''}`}
+                                style={{ border: `solid 1px ${SettingsManager.userColor}`, color: SettingsManager.userColor, background: activeItem.presentation_hideAfter ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor }}
+                                onClick={() => this.updateHideAfter(activeItem)}>
                                 Hide after
                             </div>
                         </Tooltip>
 
                         <Tooltip title={<div className="dash-tooltip">{'Open in lightbox view'}</div>}>
-                            <div className="ribbon-toggle" style={{ backgroundColor: activeItem.presOpenInLightbox ? Colors.LIGHT_BLUE : '' }} onClick={() => this.updateOpenDoc(activeItem)}>
+                            <div
+                                className="ribbon-toggle"
+                                style={{
+                                    border: `solid 1px ${SettingsManager.userColor}`,
+                                    color: SettingsManager.userColor,
+                                    background: activeItem.presentation_openInLightbox ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor,
+                                }}
+                                onClick={() => this.updateOpenDoc(activeItem)}>
                                 Lightbox
                             </div>
                         </Tooltip>
-                        <Tooltip title={<div className="dash-tooltip">{'Transition movement style'}</div>}>
-                            <div className="ribbon-toggle" onClick={() => this.updateEaseFunc(activeItem)}>
+                        <Tooltip title={<div className="dash-tooltip">Transition movement style</div>}>
+                            <div
+                                className="ribbon-toggle"
+                                style={{ border: `solid 1px ${SettingsManager.userColor}`, color: SettingsManager.userColor, background: activeItem.presEaseFunc === 'ease' ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor }}
+                                onClick={() => this.updateEaseFunc(activeItem)}>
                                 {`${StrCast(activeItem.presEaseFunc, 'ease')}`}
                             </div>
                         </Tooltip>
@@ -1573,10 +1591,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         <>
                             <div className="ribbon-doubleButton">
                                 <div className="presBox-subheading">Slide Duration</div>
-                                <div className="ribbon-property">
+                                <div className="ribbon-property" style={{ border: `solid 1px ${SettingsManager.userColor}` }}>
                                     <input className="presBox-input" type="number" readOnly={true} value={duration} onKeyDown={e => e.stopPropagation()} onChange={e => this.updateDurationTime(e.target.value)} /> s
                                 </div>
-                                <div className="ribbon-propertyUpDown">
+                                <div className="ribbon-propertyUpDown" style={{ color: SettingsManager.userBackgroundColor, background: SettingsManager.userColor }}>
                                     <div className="ribbon-propertyUpDownItem" onClick={() => this.updateDurationTime(String(duration), 1000)}>
                                         <FontAwesomeIcon icon={'caret-up'} />
                                     </div>
@@ -1615,7 +1633,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         <div className="presBox-subheading">Progressivize Collection</div>
                         <input
                             className="presBox-checkbox"
-                            style={{ margin: 10 }}
+                            style={{ margin: 10, border: `solid 1px ${SettingsManager.userColor}` }}
                             type="checkbox"
                             onChange={() => {
                                 activeItem.presentation_indexed = activeItem.presentation_indexed === undefined ? 0 : undefined;
@@ -1638,7 +1656,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         <div className="presBox-subheading">Progressivize First Bullet</div>
                         <input
                             className="presBox-checkbox"
-                            style={{ margin: 10 }}
+                            style={{ margin: 10, border: `solid 1px ${SettingsManager.userColor}` }}
                             type="checkbox"
                             onChange={() => (activeItem.presentation_indexedStart = activeItem.presentation_indexedStart ? 0 : 1)}
                             checked={!NumCast(activeItem.presentation_indexedStart)}
@@ -1646,7 +1664,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     </div>
                     <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
                         <div className="presBox-subheading">Expand Current Bullet</div>
-                        <input className="presBox-checkbox" style={{ margin: 10 }} type="checkbox" onChange={() => (activeItem.presBulletExpand = !activeItem.presBulletExpand)} checked={BoolCast(activeItem.presBulletExpand)} />
+                        <input
+                            className="presBox-checkbox"
+                            style={{ margin: 10, border: `solid 1px ${SettingsManager.userColor}` }}
+                            type="checkbox"
+                            onChange={() => (activeItem.presBulletExpand = !activeItem.presBulletExpand)}
+                            checked={BoolCast(activeItem.presBulletExpand)}
+                        />
                     </div>
 
                     <div className="ribbon-box">
@@ -1657,10 +1681,18 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 e.stopPropagation();
                                 this._openBulletEffectDropdown = !this._openBulletEffectDropdown;
                             })}
-                            style={{ borderBottomLeftRadius: this._openBulletEffectDropdown ? 0 : 5, border: this._openBulletEffectDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}>
+                            style={{
+                                color: SettingsManager.userColor,
+                                background: SettingsManager.userVariantColor,
+                                borderBottomLeftRadius: this._openBulletEffectDropdown ? 0 : 5,
+                                border: this._openBulletEffectDropdown ? `solid 2px ${SettingsManager.userVariantColor}` : `solid 1px ${SettingsManager.userColor}`,
+                            }}>
                             {effect?.toString()}
                             <FontAwesomeIcon className="presBox-dropdownIcon" style={{ gridColumn: 2, color: this._openBulletEffectDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={'angle-down'} />
-                            <div className={'presBox-dropdownOptions'} style={{ display: this._openBulletEffectDropdown ? 'grid' : 'none' }} onPointerDown={e => e.stopPropagation()}>
+                            <div
+                                className={'presBox-dropdownOptions'}
+                                style={{ display: this._openBulletEffectDropdown ? 'grid' : 'none', color: SettingsManager.userColor, background: SettingsManager.userBackgroundColor }}
+                                onPointerDown={e => e.stopPropagation()}>
                                 {bulletEffect(PresEffect.None)}
                                 {bulletEffect(PresEffect.Fade)}
                                 {bulletEffect(PresEffect.Flip)}
@@ -1725,7 +1757,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 e.stopPropagation();
                                 this._openMovementDropdown = !this._openMovementDropdown;
                             })}
-                            style={{ borderBottomLeftRadius: this._openMovementDropdown ? 0 : 5, border: this._openMovementDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}>
+                            style={{
+                                color: SettingsManager.userColor,
+                                background: SettingsManager.userVariantColor,
+                                borderBottomLeftRadius: this._openMovementDropdown ? 0 : 5,
+                                border: this._openMovementDropdown ? `solid 2px ${SettingsManager.userVariantColor}` : `solid 1px ${SettingsManager.userColor}`,
+                            }}>
                             {this.movementName(activeItem)}
                             <FontAwesomeIcon className="presBox-dropdownIcon" style={{ gridColumn: 2, color: this._openMovementDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={'angle-down'} />
                             <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} onPointerDown={StopEvent} style={{ display: this._openMovementDropdown ? 'grid' : 'none' }}>
@@ -1738,10 +1775,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         </div>
                         <div className="ribbon-doubleButton" style={{ display: activeItem.presentation_movement === PresMovement.Zoom ? 'inline-flex' : 'none' }}>
                             <div className="presBox-subheading">Zoom (% screen filled)</div>
-                            <div className="ribbon-property">
+                            <div className="ribbon-property" style={{ border: `solid 1px ${SettingsManager.userColor}` }}>
                                 <input className="presBox-input" type="number" readOnly={true} value={zoom} onChange={e => this.updateZoom(e.target.value)} />%
                             </div>
-                            <div className="ribbon-propertyUpDown">
+                            <div className="ribbon-propertyUpDown" style={{ color: SettingsManager.userBackgroundColor, background: SettingsManager.userColor }}>
                                 <div className="ribbon-propertyUpDownItem" onClick={() => this.updateZoom(String(zoom), 0.1)}>
                                     <FontAwesomeIcon icon={'caret-up'} />
                                 </div>
@@ -1753,10 +1790,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         {PresBox.inputter('0', '1', '100', zoom, activeItem.presentation_movement === PresMovement.Zoom, this.updateZoom)}
                         <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
                             <div className="presBox-subheading">Transition Time</div>
-                            <div className="ribbon-property">
+                            <div className="ribbon-property" style={{ border: `solid 1px ${SettingsManager.userColor}` }}>
                                 <input className="presBox-input" type="number" readOnly={true} value={transitionSpeed} onKeyDown={e => e.stopPropagation()} onChange={action(e => this.updateTransitionTime(e.target.value))} /> s
                             </div>
-                            <div className="ribbon-propertyUpDown">
+                            <div className="ribbon-propertyUpDown" style={{ color: SettingsManager.userBackgroundColor, background: SettingsManager.userColor }}>
                                 <div className="ribbon-propertyUpDownItem" onClick={() => this.updateTransitionTime(String(transitionSpeed), 1000)}>
                                     <FontAwesomeIcon icon={'caret-up'} />
                                 </div>
@@ -1776,13 +1813,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         Effects
                         <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
                             <div className="presBox-subheading">Play Audio Annotation</div>
-                            <input className="presBox-checkbox" style={{ margin: 10 }} type="checkbox" onChange={() => (activeItem.presPlayAudio = !BoolCast(activeItem.presPlayAudio))} checked={BoolCast(activeItem.presPlayAudio)} />
+                            <input
+                                className="presBox-checkbox"
+                                style={{ margin: 10, border: `solid 1px ${SettingsManager.userColor}` }}
+                                type="checkbox"
+                                onChange={() => (activeItem.presPlayAudio = !BoolCast(activeItem.presPlayAudio))}
+                                checked={BoolCast(activeItem.presPlayAudio)}
+                            />
                         </div>
                         <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
                             <div className="presBox-subheading">Zoom Text Selections</div>
                             <input
                                 className="presBox-checkbox"
-                                style={{ margin: 10 }}
+                                style={{ margin: 10, border: `solid 1px ${SettingsManager.userColor}` }}
                                 type="checkbox"
                                 onChange={() => (activeItem.presentation_zoomText = !BoolCast(activeItem.presentation_zoomText))}
                                 checked={BoolCast(activeItem.presentation_zoomText)}
@@ -1794,7 +1837,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 e.stopPropagation();
                                 this._openEffectDropdown = !this._openEffectDropdown;
                             })}
-                            style={{ borderBottomLeftRadius: this._openEffectDropdown ? 0 : 5, border: this._openEffectDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}>
+                            style={{
+                                color: SettingsManager.userColor,
+                                background: SettingsManager.userVariantColor,
+                                borderBottomLeftRadius: this._openEffectDropdown ? 0 : 5,
+                                border: this._openEffectDropdown ? `solid 2px ${SettingsManager.userVariantColor}` : `solid 1px ${SettingsManager.userColor}`,
+                            }}>
                             {effect?.toString()}
                             <FontAwesomeIcon className="presBox-dropdownIcon" style={{ gridColumn: 2, color: this._openEffectDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={'angle-down'} />
                             <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} style={{ display: this._openEffectDropdown ? 'grid' : 'none' }} onPointerDown={e => e.stopPropagation()}>
@@ -1808,7 +1856,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         </div>
                         <div className="ribbon-doubleButton" style={{ display: effect === PresEffectDirection.None ? 'none' : 'inline-flex' }}>
                             <div className="presBox-subheading">Effect direction</div>
-                            <div className="ribbon-property">{StrCast(this.activeItem.presentation_effectDirection)}</div>
+                            <div className="ribbon-property" style={{ border: `solid 1px ${SettingsManager.userColor}` }}>
+                                {StrCast(this.activeItem.presentation_effectDirection)}
+                            </div>
                         </div>
                         <div className="effectDirection" style={{ display: effect === PresEffectDirection.None ? 'none' : 'grid', width: 40 }}>
                             {presDirection(PresEffectDirection.Left, 'angle-right', 1, 2, {})}
@@ -1830,8 +1880,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @computed get mediaOptionsDropdown() {
         const activeItem = this.activeItem;
         if (activeItem && this.targetDoc) {
-            const clipStart = NumCast(activeItem.clipStart);
-            const clipEnd = NumCast(activeItem.clipEnd, NumCast(activeItem[Doc.LayoutFieldKey(activeItem) + '_duration']));
+            const renderTarget = PresBox.targetRenderedDoc(this.activeItem);
+            const clipStart = NumCast(renderTarget.clipStart);
+            const clipEnd = NumCast(renderTarget.clipEnd, clipStart + NumCast(renderTarget[Doc.LayoutFieldKey(renderTarget) + '_duration']));
+            const config_clipEnd = NumCast(activeItem.config_clipEnd) < NumCast(activeItem.config_clipStart) ? clipEnd - clipStart : NumCast(activeItem.config_clipEnd);
             return (
                 <div className={'presBox-ribbon'} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
                     <div>
@@ -1842,7 +1894,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                     <div className="slider-text" style={{ fontWeight: 500 }}>
                                         Start time (s)
                                     </div>
-                                    <div id={'startTime'} className="slider-number" style={{ backgroundColor: Colors.LIGHT_GRAY }}>
+                                    <div id="startTime" className="slider-number" style={{ color: SettingsManager.userColor, backgroundColor: SettingsManager.userBackgroundColor }}>
                                         <input
                                             className="presBox-input"
                                             style={{ textAlign: 'center', width: '100%', height: 15, fontSize: 10 }}
@@ -1850,9 +1902,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                             readOnly={true}
                                             value={NumCast(activeItem.config_clipStart).toFixed(2)}
                                             onKeyDown={e => e.stopPropagation()}
-                                            onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
-                                                activeItem.config_clipStart = Number(e.target.value);
-                                            })}
+                                            onChange={action(e => (activeItem.config_clipStart = Number(e.target.value)))}
                                         />
                                     </div>
                                 </div>
@@ -1860,25 +1910,23 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                     <div className="slider-text" style={{ fontWeight: 500 }}>
                                         Duration (s)
                                     </div>
-                                    <div className="slider-number" style={{ backgroundColor: Colors.LIGHT_BLUE }}>
-                                        {Math.round((NumCast(activeItem.config_clipEnd) - NumCast(activeItem.config_clipStart)) * 10) / 10}
+                                    <div className="slider-number" style={{ color: SettingsManager.userColor, backgroundColor: SettingsManager.userBackgroundColor }}>
+                                        {Math.round((config_clipEnd - NumCast(activeItem.config_clipStart)) * 10) / 10}
                                     </div>
                                 </div>
                                 <div className="slider-block">
                                     <div className="slider-text" style={{ fontWeight: 500 }}>
                                         End time (s)
                                     </div>
-                                    <div id={'endTime'} className="slider-number" style={{ backgroundColor: Colors.LIGHT_GRAY }}>
+                                    <div id="endTime" className="slider-number" style={{ color: SettingsManager.userColor, backgroundColor: SettingsManager.userBackgroundColor }}>
                                         <input
                                             className="presBox-input"
                                             onKeyDown={e => e.stopPropagation()}
                                             style={{ textAlign: 'center', width: '100%', height: 15, fontSize: 10 }}
                                             type="number"
                                             readOnly={true}
-                                            value={NumCast(activeItem.config_clipEnd).toFixed(2)}
-                                            onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
-                                                activeItem.config_clipEnd = Number(e.target.value);
-                                            })}
+                                            value={config_clipEnd.toFixed(2)}
+                                            onChange={action(e => (activeItem.config_clipEnd = Number(e.target.value)))}
                                         />
                                     </div>
                                 </div>
@@ -1889,16 +1937,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                     step="0.1"
                                     min={clipStart}
                                     max={clipEnd}
-                                    value={NumCast(activeItem.config_clipEnd)}
-                                    style={{ gridColumn: 1, gridRow: 1 }}
+                                    value={config_clipEnd}
+                                    style={{ gridColumn: 1, gridRow: 1, background: SettingsManager.userColor, color: SettingsManager.userVariantColor }}
                                     className={`toolbar-slider ${'end'}`}
                                     id="toolbar-slider"
                                     onPointerDown={e => {
                                         this._batch = UndoManager.StartBatch('config_clipEnd');
                                         const endBlock = document.getElementById('endTime');
                                         if (endBlock) {
-                                            endBlock.style.color = Colors.LIGHT_GRAY;
-                                            endBlock.style.backgroundColor = Colors.MEDIUM_BLUE;
+                                            endBlock.style.backgroundColor = SettingsManager.userVariantColor;
                                         }
                                         e.stopPropagation();
                                     }}
@@ -1906,8 +1953,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                         this._batch?.end();
                                         const endBlock = document.getElementById('endTime');
                                         if (endBlock) {
-                                            endBlock.style.color = Colors.BLACK;
-                                            endBlock.style.backgroundColor = Colors.LIGHT_GRAY;
+                                            endBlock.style.backgroundColor = SettingsManager.userBackgroundColor;
                                         }
                                     }}
                                     onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
@@ -1928,8 +1974,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                         this._batch = UndoManager.StartBatch('config_clipStart');
                                         const startBlock = document.getElementById('startTime');
                                         if (startBlock) {
-                                            startBlock.style.color = Colors.LIGHT_GRAY;
-                                            startBlock.style.backgroundColor = Colors.MEDIUM_BLUE;
+                                            startBlock.style.backgroundColor = SettingsManager.userVariantColor;
                                         }
                                         e.stopPropagation();
                                     }}
@@ -1937,8 +1982,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                         this._batch?.end();
                                         const startBlock = document.getElementById('startTime');
                                         if (startBlock) {
-                                            startBlock.style.color = Colors.BLACK;
-                                            startBlock.style.backgroundColor = Colors.LIGHT_GRAY;
+                                            startBlock.style.backgroundColor = SettingsManager.userBackgroundColor;
                                         }
                                     }}
                                     onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
@@ -1958,22 +2002,46 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             <div className="presBox-subheading">Start playing:</div>
                             <div className="presBox-radioButtons">
                                 <div className="checkbox-container">
-                                    <input className="presBox-checkbox" type="checkbox" onChange={() => (activeItem.mediaStart = 'manual')} checked={activeItem.mediaStart === 'manual'} />
+                                    <input
+                                        className="presBox-checkbox"
+                                        type="checkbox"
+                                        style={{ border: `solid 1px ${SettingsManager.userColor}` }}
+                                        onChange={() => (activeItem.presentation_mediaStart = 'manual')}
+                                        checked={activeItem.presentation_mediaStart === 'manual'}
+                                    />
                                     <div>On click</div>
                                 </div>
                                 <div className="checkbox-container">
-                                    <input className="presBox-checkbox" type="checkbox" onChange={() => (activeItem.mediaStart = 'auto')} checked={activeItem.mediaStart === 'auto'} />
+                                    <input
+                                        className="presBox-checkbox"
+                                        style={{ border: `solid 1px ${SettingsManager.userColor}` }}
+                                        type="checkbox"
+                                        onChange={() => (activeItem.presentation_mediaStart = 'auto')}
+                                        checked={activeItem.presentation_mediaStart === 'auto'}
+                                    />
                                     <div>Automatically</div>
                                 </div>
                             </div>
                             <div className="presBox-subheading">Stop playing:</div>
                             <div className="presBox-radioButtons">
                                 <div className="checkbox-container">
-                                    <input className="presBox-checkbox" type="checkbox" onChange={() => (activeItem.mediaStop = 'manual')} checked={activeItem.mediaStop === 'manual'} />
-                                    <div>At audio end time</div>
+                                    <input
+                                        className="presBox-checkbox"
+                                        type="checkbox"
+                                        style={{ border: `solid 1px ${SettingsManager.userColor}` }}
+                                        onChange={() => (activeItem.presentation_mediaStop = 'manual')}
+                                        checked={activeItem.presentation_mediaStop === 'manual'}
+                                    />
+                                    <div>At media end time</div>
                                 </div>
                                 <div className="checkbox-container">
-                                    <input className="presBox-checkbox" type="checkbox" onChange={() => (activeItem.mediaStop = 'auto')} checked={activeItem.mediaStop === 'auto'} />
+                                    <input
+                                        className="presBox-checkbox"
+                                        type="checkbox"
+                                        style={{ border: `solid 1px ${SettingsManager.userColor}` }}
+                                        onChange={() => (activeItem.presentation_mediaStop = 'auto')}
+                                        checked={activeItem.presentation_mediaStop === 'auto'}
+                                    />
                                     <div>On slide change</div>
                                 </div>
                                 {/* <div className="checkbox-container">
@@ -2221,8 +2289,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const propTitle = SettingsManager.propertiesWidth > 0 ? 'Close Presentation Panel' : 'Open Presentation Panel';
         const mode = StrCast(this.rootDoc._type_collection) as CollectionViewType;
         const isMini: boolean = this.toolbarWidth <= 100;
-        const activeColor = Colors.LIGHT_BLUE;
-        const inactiveColor = Colors.WHITE;
+        const activeColor = SettingsManager.userVariantColor;
+        const inactiveColor = SettingsManager.userColor;
         return mode === CollectionViewType.Carousel3D || Doc.IsInMyOverlay(this.rootDoc) ? null : (
             <div id="toolbarContainer" className={'presBox-toolbar'}>
                 {/* <Tooltip title={<><div className="dash-tooltip">{"Add new slide"}</div></>}><div className={`toolbar-button ${this.newDocumentTools ? "active" : ""}`} onClick={action(() => this.newDocumentTools = !this.newDocumentTools)}>
diff --git a/src/client/views/selectedDoc/SelectedDocView.tsx b/src/client/views/selectedDoc/SelectedDocView.tsx
index 955a4a174..2139919e0 100644
--- a/src/client/views/selectedDoc/SelectedDocView.tsx
+++ b/src/client/views/selectedDoc/SelectedDocView.tsx
@@ -1,12 +1,14 @@
 import React = require('react');
-import { Doc } from "../../../fields/Doc";
-import { observer } from "mobx-react";
-import { computed } from "mobx";
-import { StrCast } from "../../../fields/Types";
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Colors, ListBox } from 'browndash-components';
+import { ListBox } from 'browndash-components';
+import { computed } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc } from '../../../fields/Doc';
+import { StrCast } from '../../../fields/Types';
 import { DocumentManager } from '../../util/DocumentManager';
 import { DocFocusOptions } from '../nodes/DocumentView';
+import { emptyFunction } from '../../../Utils';
+import { SettingsManager } from '../../util/SettingsManager';
 
 export interface SelectedDocViewProps {
     selectedDocs: Doc[];
@@ -14,34 +16,33 @@ export interface SelectedDocViewProps {
 
 @observer
 export class SelectedDocView extends React.Component<SelectedDocViewProps> {
-
     @computed get selectedDocs() {
         return this.props.selectedDocs;
     }
-    
 
     render() {
-        return <div className={`selectedDocView-container`}>
-            <ListBox
-                items={this.selectedDocs.map((doc) => {
-                    const icon = Doc.toIcon(doc);
-                    const iconEle = <FontAwesomeIcon size={'1x'} icon={icon} />;
-                    const text = StrCast(doc.title)
-                    const finished = () => {
-                        
-                    };
-                    const options: DocFocusOptions = {
-                        playAudio: false,
-                    };
-                    return {
-                        text: text,
-                        val: StrCast(doc._id),
-                        icon: iconEle,
-                        onClick: () => {DocumentManager.Instance.showDocument(doc, options, finished);}
-                    }
-                })}
-                color={StrCast(Doc.UserDoc().userColor)}
-            />
-        </div>
+        return (
+            <div className="selectedDocView-container">
+                <ListBox
+                    items={this.selectedDocs.map(doc => {
+                        const options: DocFocusOptions = {
+                            playAudio: false,
+                            playMedia: false,
+                            willPan: true,
+                        };
+                        return {
+                            text: StrCast(doc.title),
+                            val: StrCast(doc._id),
+                            color: SettingsManager.userColor,
+                            background: SettingsManager.userBackgroundColor,
+                            icon: <FontAwesomeIcon size={'1x'} icon={Doc.toIcon(doc)} />,
+                            onClick: () => DocumentManager.Instance.showDocument(doc, options, emptyFunction),
+                        };
+                    })}
+                    color={SettingsManager.userColor}
+                    background={SettingsManager.userBackgroundColor}
+                />
+            </div>
+        );
     }
-}
\ No newline at end of file
+}
-- 
cgit v1.2.3-70-g09d2


From 888ddd78737781f8a2d045b5f1e41788c512c94d Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 8 Sep 2023 17:02:49 -0400
Subject: don't remove current recording from overlay, added closeOnSelect
 option  for dropdown.   place recording box on screen.  fixe overlayView to
 keep views on screen. fixed schema view checkboxes. don't cause tab to
 rerender when colelctoin type changes.  add dropdown to filter indicator to
 find filters.

---
 package-lock.json                                  | 166 +++++++++++++--------
 package.json                                       |   2 +-
 src/client/util/CurrentUserUtils.ts                |   1 -
 src/client/util/SettingsManager.tsx                |   6 +-
 src/client/util/reportManager/ReportManager.tsx    |   2 +
 src/client/views/DashboardView.tsx                 |   1 +
 src/client/views/MainView.tsx                      |   6 +-
 src/client/views/OverlayView.tsx                   |  15 ++
 src/client/views/PropertiesButtons.tsx             |   1 +
 src/client/views/PropertiesView.tsx                |  39 +++--
 src/client/views/StyleProvider.scss                |   9 +-
 src/client/views/StyleProvider.tsx                 |  41 ++++-
 src/client/views/UndoStack.tsx                     |  12 +-
 src/client/views/collections/TabDocView.tsx        |  10 +-
 .../collectionSchema/CollectionSchemaView.tsx      |   8 +-
 src/client/views/nodes/FontIconBox/FontIconBox.tsx |   3 +-
 .../views/nodes/RecordingBox/RecordingBox.tsx      |   9 +-
 17 files changed, 213 insertions(+), 118 deletions(-)

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

diff --git a/package-lock.json b/package-lock.json
index b6c03c6ba..6055e9d1e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -142,20 +142,20 @@
             "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ=="
         },
         "@babel/core": {
-            "version": "7.22.15",
-            "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.15.tgz",
-            "integrity": "sha512-PtZqMmgRrvj8ruoEOIwVA3yoF91O+Hgw9o7DAUTNBA6Mo2jpu31clx9a7Nz/9JznqetTR6zwfC4L3LAjKQXUwA==",
+            "version": "7.22.17",
+            "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.17.tgz",
+            "integrity": "sha512-2EENLmhpwplDux5PSsZnSbnSkB3tZ6QTksgO25xwEL7pIDcNOMhF5v/s6RzwjMZzZzw9Ofc30gHv5ChCC8pifQ==",
             "requires": {
                 "@ampproject/remapping": "^2.2.0",
                 "@babel/code-frame": "^7.22.13",
                 "@babel/generator": "^7.22.15",
                 "@babel/helper-compilation-targets": "^7.22.15",
-                "@babel/helper-module-transforms": "^7.22.15",
+                "@babel/helper-module-transforms": "^7.22.17",
                 "@babel/helpers": "^7.22.15",
-                "@babel/parser": "^7.22.15",
+                "@babel/parser": "^7.22.16",
                 "@babel/template": "^7.22.15",
-                "@babel/traverse": "^7.22.15",
-                "@babel/types": "^7.22.15",
+                "@babel/traverse": "^7.22.17",
+                "@babel/types": "^7.22.17",
                 "convert-source-map": "^1.7.0",
                 "debug": "^4.1.0",
                 "gensync": "^1.0.0-beta.2",
@@ -209,9 +209,9 @@
                     }
                 },
                 "@babel/traverse": {
-                    "version": "7.22.15",
-                    "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.15.tgz",
-                    "integrity": "sha512-DdHPwvJY0sEeN4xJU5uRLmZjgMMDIvMPniLuYzUVXj/GGzysPl0/fwt44JBkyUIzGJPV8QgHMcQdQ34XFuKTYQ==",
+                    "version": "7.22.17",
+                    "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.17.tgz",
+                    "integrity": "sha512-xK4Uwm0JnAMvxYZxOVecss85WxTEIbTa7bnGyf/+EgCL5Zt3U7htUpEOWv9detPlamGKuRzCqw74xVglDWpPdg==",
                     "requires": {
                         "@babel/code-frame": "^7.22.13",
                         "@babel/generator": "^7.22.15",
@@ -219,16 +219,16 @@
                         "@babel/helper-function-name": "^7.22.5",
                         "@babel/helper-hoist-variables": "^7.22.5",
                         "@babel/helper-split-export-declaration": "^7.22.6",
-                        "@babel/parser": "^7.22.15",
-                        "@babel/types": "^7.22.15",
+                        "@babel/parser": "^7.22.16",
+                        "@babel/types": "^7.22.17",
                         "debug": "^4.1.0",
                         "globals": "^11.1.0"
                     }
                 },
                 "@babel/types": {
-                    "version": "7.22.15",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.15.tgz",
-                    "integrity": "sha512-X+NLXr0N8XXmN5ZsaQdm9U2SSC3UbIYq/doL++sueHOTisgZHoKaQtZxGuV2cUPQHMfjKEfg/g6oy7Hm6SKFtA==",
+                    "version": "7.22.17",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.17.tgz",
+                    "integrity": "sha512-YSQPHLFtQNE5xN9tHuZnzu8vPr61wVTBZdfv1meex1NBosa4iT05k/Jw06ddJugi4bk7The/oSwQGFcksmEJQg==",
                     "requires": {
                         "@babel/helper-string-parser": "^7.22.5",
                         "@babel/helper-validator-identifier": "^7.22.15",
@@ -300,9 +300,9 @@
                     "integrity": "sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ=="
                 },
                 "@babel/types": {
-                    "version": "7.22.15",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.15.tgz",
-                    "integrity": "sha512-X+NLXr0N8XXmN5ZsaQdm9U2SSC3UbIYq/doL++sueHOTisgZHoKaQtZxGuV2cUPQHMfjKEfg/g6oy7Hm6SKFtA==",
+                    "version": "7.22.17",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.17.tgz",
+                    "integrity": "sha512-YSQPHLFtQNE5xN9tHuZnzu8vPr61wVTBZdfv1meex1NBosa4iT05k/Jw06ddJugi4bk7The/oSwQGFcksmEJQg==",
                     "requires": {
                         "@babel/helper-string-parser": "^7.22.5",
                         "@babel/helper-validator-identifier": "^7.22.15",
@@ -472,9 +472,9 @@
                     "integrity": "sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ=="
                 },
                 "@babel/types": {
-                    "version": "7.22.15",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.15.tgz",
-                    "integrity": "sha512-X+NLXr0N8XXmN5ZsaQdm9U2SSC3UbIYq/doL++sueHOTisgZHoKaQtZxGuV2cUPQHMfjKEfg/g6oy7Hm6SKFtA==",
+                    "version": "7.22.17",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.17.tgz",
+                    "integrity": "sha512-YSQPHLFtQNE5xN9tHuZnzu8vPr61wVTBZdfv1meex1NBosa4iT05k/Jw06ddJugi4bk7The/oSwQGFcksmEJQg==",
                     "requires": {
                         "@babel/helper-string-parser": "^7.22.5",
                         "@babel/helper-validator-identifier": "^7.22.15",
@@ -492,9 +492,9 @@
             }
         },
         "@babel/helper-module-transforms": {
-            "version": "7.22.15",
-            "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.15.tgz",
-            "integrity": "sha512-l1UiX4UyHSFsYt17iQ3Se5pQQZZHa22zyIXURmvkmLCD4t/aU+dvNWHatKac/D9Vm9UES7nvIqHs4jZqKviUmQ==",
+            "version": "7.22.17",
+            "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.17.tgz",
+            "integrity": "sha512-XouDDhQESrLHTpnBtCKExJdyY4gJCdrvH2Pyv8r8kovX2U8G0dRUOT45T9XlbLtuu9CLXP15eusnkprhoPV5iQ==",
             "requires": {
                 "@babel/helper-environment-visitor": "^7.22.5",
                 "@babel/helper-module-imports": "^7.22.15",
@@ -517,9 +517,9 @@
                     "integrity": "sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ=="
                 },
                 "@babel/types": {
-                    "version": "7.22.15",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.15.tgz",
-                    "integrity": "sha512-X+NLXr0N8XXmN5ZsaQdm9U2SSC3UbIYq/doL++sueHOTisgZHoKaQtZxGuV2cUPQHMfjKEfg/g6oy7Hm6SKFtA==",
+                    "version": "7.22.17",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.17.tgz",
+                    "integrity": "sha512-YSQPHLFtQNE5xN9tHuZnzu8vPr61wVTBZdfv1meex1NBosa4iT05k/Jw06ddJugi4bk7The/oSwQGFcksmEJQg==",
                     "requires": {
                         "@babel/helper-string-parser": "^7.22.5",
                         "@babel/helper-validator-identifier": "^7.22.15",
@@ -542,13 +542,13 @@
             "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ=="
         },
         "@babel/helper-remap-async-to-generator": {
-            "version": "7.22.9",
-            "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.9.tgz",
-            "integrity": "sha512-8WWC4oR4Px+tr+Fp0X3RHDVfINGpF3ad1HIbrc8A77epiR6eMMc6jsgozkzT2uDiOOdoS9cLIQ+XD2XvI2WSmQ==",
+            "version": "7.22.17",
+            "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.17.tgz",
+            "integrity": "sha512-bxH77R5gjH3Nkde6/LuncQoLaP16THYPscurp1S8z7S9ZgezCyV3G8Hc+TZiCmY8pz4fp8CvKSgtJMW0FkLAxA==",
             "requires": {
                 "@babel/helper-annotate-as-pure": "^7.22.5",
                 "@babel/helper-environment-visitor": "^7.22.5",
-                "@babel/helper-wrap-function": "^7.22.9"
+                "@babel/helper-wrap-function": "^7.22.17"
             },
             "dependencies": {
                 "@babel/helper-annotate-as-pure": {
@@ -611,28 +611,64 @@
             "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA=="
         },
         "@babel/helper-wrap-function": {
-            "version": "7.22.10",
-            "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.10.tgz",
-            "integrity": "sha512-OnMhjWjuGYtdoO3FmsEFWvBStBAe2QOgwOLsLNDjN+aaiMD8InJk1/O3HSD8lkqTjCgg5YI34Tz15KNNA3p+nQ==",
+            "version": "7.22.17",
+            "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.17.tgz",
+            "integrity": "sha512-nAhoheCMlrqU41tAojw9GpVEKDlTS8r3lzFmF0lP52LwblCPbuFSO7nGIZoIcoU5NIm1ABrna0cJExE4Ay6l2Q==",
             "requires": {
                 "@babel/helper-function-name": "^7.22.5",
-                "@babel/template": "^7.22.5",
-                "@babel/types": "^7.22.10"
+                "@babel/template": "^7.22.15",
+                "@babel/types": "^7.22.17"
             },
             "dependencies": {
-                "@babel/helper-validator-identifier": {
+                "@babel/code-frame": {
+                    "version": "7.22.13",
+                    "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
+                    "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==",
+                    "requires": {
+                        "@babel/highlight": "^7.22.13",
+                        "chalk": "^2.4.2"
+                    }
+                },
+                "@babel/highlight": {
+                    "version": "7.22.13",
+                    "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.13.tgz",
+                    "integrity": "sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==",
+                    "requires": {
+                        "@babel/helper-validator-identifier": "^7.22.5",
+                        "chalk": "^2.4.2",
+                        "js-tokens": "^4.0.0"
+                    }
+                },
+                "@babel/parser": {
+                    "version": "7.22.16",
+                    "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.16.tgz",
+                    "integrity": "sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA=="
+                },
+                "@babel/template": {
                     "version": "7.22.15",
-                    "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.15.tgz",
-                    "integrity": "sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ=="
+                    "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
+                    "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
+                    "requires": {
+                        "@babel/code-frame": "^7.22.13",
+                        "@babel/parser": "^7.22.15",
+                        "@babel/types": "^7.22.15"
+                    }
                 },
                 "@babel/types": {
-                    "version": "7.22.15",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.15.tgz",
-                    "integrity": "sha512-X+NLXr0N8XXmN5ZsaQdm9U2SSC3UbIYq/doL++sueHOTisgZHoKaQtZxGuV2cUPQHMfjKEfg/g6oy7Hm6SKFtA==",
+                    "version": "7.22.17",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.17.tgz",
+                    "integrity": "sha512-YSQPHLFtQNE5xN9tHuZnzu8vPr61wVTBZdfv1meex1NBosa4iT05k/Jw06ddJugi4bk7The/oSwQGFcksmEJQg==",
                     "requires": {
                         "@babel/helper-string-parser": "^7.22.5",
                         "@babel/helper-validator-identifier": "^7.22.15",
                         "to-fast-properties": "^2.0.0"
+                    },
+                    "dependencies": {
+                        "@babel/helper-validator-identifier": {
+                            "version": "7.22.15",
+                            "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.15.tgz",
+                            "integrity": "sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ=="
+                        }
                     }
                 }
             }
@@ -693,9 +729,9 @@
                     }
                 },
                 "@babel/traverse": {
-                    "version": "7.22.15",
-                    "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.15.tgz",
-                    "integrity": "sha512-DdHPwvJY0sEeN4xJU5uRLmZjgMMDIvMPniLuYzUVXj/GGzysPl0/fwt44JBkyUIzGJPV8QgHMcQdQ34XFuKTYQ==",
+                    "version": "7.22.17",
+                    "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.17.tgz",
+                    "integrity": "sha512-xK4Uwm0JnAMvxYZxOVecss85WxTEIbTa7bnGyf/+EgCL5Zt3U7htUpEOWv9detPlamGKuRzCqw74xVglDWpPdg==",
                     "requires": {
                         "@babel/code-frame": "^7.22.13",
                         "@babel/generator": "^7.22.15",
@@ -703,16 +739,16 @@
                         "@babel/helper-function-name": "^7.22.5",
                         "@babel/helper-hoist-variables": "^7.22.5",
                         "@babel/helper-split-export-declaration": "^7.22.6",
-                        "@babel/parser": "^7.22.15",
-                        "@babel/types": "^7.22.15",
+                        "@babel/parser": "^7.22.16",
+                        "@babel/types": "^7.22.17",
                         "debug": "^4.1.0",
                         "globals": "^11.1.0"
                     }
                 },
                 "@babel/types": {
-                    "version": "7.22.15",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.15.tgz",
-                    "integrity": "sha512-X+NLXr0N8XXmN5ZsaQdm9U2SSC3UbIYq/doL++sueHOTisgZHoKaQtZxGuV2cUPQHMfjKEfg/g6oy7Hm6SKFtA==",
+                    "version": "7.22.17",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.17.tgz",
+                    "integrity": "sha512-YSQPHLFtQNE5xN9tHuZnzu8vPr61wVTBZdfv1meex1NBosa4iT05k/Jw06ddJugi4bk7The/oSwQGFcksmEJQg==",
                     "requires": {
                         "@babel/helper-string-parser": "^7.22.5",
                         "@babel/helper-validator-identifier": "^7.22.15",
@@ -1691,9 +1727,9 @@
                     }
                 },
                 "@babel/types": {
-                    "version": "7.22.15",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.15.tgz",
-                    "integrity": "sha512-X+NLXr0N8XXmN5ZsaQdm9U2SSC3UbIYq/doL++sueHOTisgZHoKaQtZxGuV2cUPQHMfjKEfg/g6oy7Hm6SKFtA==",
+                    "version": "7.22.17",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.17.tgz",
+                    "integrity": "sha512-YSQPHLFtQNE5xN9tHuZnzu8vPr61wVTBZdfv1meex1NBosa4iT05k/Jw06ddJugi4bk7The/oSwQGFcksmEJQg==",
                     "requires": {
                         "@babel/helper-string-parser": "^7.22.5",
                         "@babel/helper-validator-identifier": "^7.22.15",
@@ -2028,9 +2064,9 @@
                     "integrity": "sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ=="
                 },
                 "@babel/types": {
-                    "version": "7.22.15",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.15.tgz",
-                    "integrity": "sha512-X+NLXr0N8XXmN5ZsaQdm9U2SSC3UbIYq/doL++sueHOTisgZHoKaQtZxGuV2cUPQHMfjKEfg/g6oy7Hm6SKFtA==",
+                    "version": "7.22.17",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.17.tgz",
+                    "integrity": "sha512-YSQPHLFtQNE5xN9tHuZnzu8vPr61wVTBZdfv1meex1NBosa4iT05k/Jw06ddJugi4bk7The/oSwQGFcksmEJQg==",
                     "requires": {
                         "@babel/helper-string-parser": "^7.22.5",
                         "@babel/helper-validator-identifier": "^7.22.15",
@@ -6651,9 +6687,9 @@
             }
         },
         "browndash-components": {
-            "version": "0.1.23",
-            "resolved": "https://registry.npmjs.org/browndash-components/-/browndash-components-0.1.23.tgz",
-            "integrity": "sha512-yZPnSUBXdToufaq4mTYHMgDwrijQWsuVKK8j+NilRKMqmOrkZeqyRhncTDz077DcMLcjeIj+SobT3GPK04TXmQ==",
+            "version": "0.1.25",
+            "resolved": "https://registry.npmjs.org/browndash-components/-/browndash-components-0.1.25.tgz",
+            "integrity": "sha512-YgzUsfeUKwtaOLRYNDcxybZG2Ip7KSQV38VbUrgC+ycwOX/tzq9IWlfw2z4h/EVuVcq+ERCaBS4pb3mMU6pKGQ==",
             "requires": {
                 "@emotion/react": "^11.11.1",
                 "@emotion/styled": "^11.11.0",
@@ -10110,14 +10146,14 @@
                     }
                 },
                 "caniuse-lite": {
-                    "version": "1.0.30001528",
-                    "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001528.tgz",
-                    "integrity": "sha512-0Db4yyjR9QMNlsxh+kKWzQtkyflkG/snYheSzkjmvdEtEXB1+jt7A2HmSEiO6XIJPIbo92lHNGNySvE5pZcs5Q=="
+                    "version": "1.0.30001529",
+                    "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001529.tgz",
+                    "integrity": "sha512-n2pUQYGAkrLG4QYj2desAh+NqsJpHbNmVZz87imptDdxLAtjxary7Df/psdfyDGmskJK/9Dt9cPnx5RZ3CU4Og=="
                 },
                 "electron-to-chromium": {
-                    "version": "1.4.510",
-                    "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.510.tgz",
-                    "integrity": "sha512-xPfLIPFcN/WLXBpQ/K4UgE98oUBO5Tia6BD4rkSR0wE7ep/PwBVlgvPJQrIBpmJGVAmUzwPKuDbVt9XV6+uC2g=="
+                    "version": "1.4.513",
+                    "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.513.tgz",
+                    "integrity": "sha512-cOB0xcInjm+E5qIssHeXJ29BaUyWpMyFKT5RB3bsLENDheCja0wMkHJyiPl0NBE/VzDI7JDuNEQWhe6RitEUcw=="
                 }
             }
         },
diff --git a/package.json b/package.json
index c218d80ff..9f0b91628 100644
--- a/package.json
+++ b/package.json
@@ -178,7 +178,7 @@
         "body-parser": "^1.19.2",
         "bootstrap": "^4.6.1",
         "brotli": "^1.3.3",
-        "browndash-components": "^0.1.23",
+        "browndash-components": "^0.1.25",
         "browser-assert": "^1.2.1",
         "bson": "^4.6.1",
         "canvas": "^2.9.3",
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index aa5f2658a..8bedea562 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -763,7 +763,6 @@ export class CurrentUserUtils {
     }
     /// Initializes all the default buttons for the top bar context menu
     static setupTopbarButtons(doc: Doc, field="myTopBarBtns") {
-        if (Doc.UserDoc().currentRecording) Doc.RemFromMyOverlay(DocCast(Doc.UserDoc().currentRecording));
         Doc.UserDoc().currentRecording = undefined;
         Doc.UserDoc().workspaceRecordingState = undefined;
         Doc.UserDoc().workspaceReplayingState = undefined;
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 720badd40..53cfbc947 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -142,6 +142,7 @@ export class SettingsManager extends React.Component<{}> {
                     formLabel="Theme"
                     size={Size.SMALL}
                     type={Type.TERT}
+                    closeOnSelect={false}
                     selectedVal={userTheme}
                     setSelectedVal={scheme => this.changeColorScheme(scheme as string)}
                     items={colorSchemes.map((scheme, i) => ({
@@ -294,6 +295,7 @@ export class SettingsManager extends React.Component<{}> {
                                         },
                                     };
                                 })}
+                                closeOnSelect={true}
                                 dropdownType={DropdownType.SELECT}
                                 type={Type.TERT}
                                 selectedVal={StrCast(Doc.UserDoc().fontFamily)}
@@ -373,6 +375,7 @@ export class SettingsManager extends React.Component<{}> {
                     <div className="tab-column-content">
                         <Dropdown
                             formLabel={'Mode'}
+                            closeOnSelect={true}
                             items={[
                                 {
                                     text: 'Novice',
@@ -403,7 +406,8 @@ export class SettingsManager extends React.Component<{}> {
                     </div>
                     <div className="tab-column-content">
                         <Dropdown
-                            formLabel={'Scroll Mode'}
+                            formLabel="Scroll Mode"
+                            closeOnSelect={true}
                             items={[
                                 {
                                     text: 'Scroll to Pan',
diff --git a/src/client/util/reportManager/ReportManager.tsx b/src/client/util/reportManager/ReportManager.tsx
index 6a236face..08467448d 100644
--- a/src/client/util/reportManager/ReportManager.tsx
+++ b/src/client/util/reportManager/ReportManager.tsx
@@ -308,6 +308,7 @@ export class ReportManager extends React.Component<{}> {
                     <Dropdown
                         color={StrCast(Doc.UserDoc().userColor)}
                         formLabel={'Type'}
+                        closeOnSelect={true}
                         items={bugDropdownItems}
                         selectedVal={this.formData.type}
                         setSelectedVal={val => {
@@ -320,6 +321,7 @@ export class ReportManager extends React.Component<{}> {
                     <Dropdown
                         color={StrCast(Doc.UserDoc().userColor)}
                         formLabel={'Priority'}
+                        closeOnSelect={true}
                         items={priorityDropdownItems}
                         selectedVal={this.formData.priority}
                         setSelectedVal={val => {
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 1d1d0eaab..014a6358f 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -371,6 +371,7 @@ export class DashboardView extends React.Component {
 
         Doc.AddDocToList(Doc.MyHeaderBar, 'data', freeformDoc);
         dashboardDoc['pane-count'] = 1;
+        freeformDoc.embedContainer = dashboardDoc;
 
         Doc.AddDocToList(Doc.MyDashboards, 'data', dashboardDoc);
 
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 8adb28fe1..2b4f4d5f7 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -810,7 +810,11 @@ export class MainView extends React.Component {
                             </div>
                         )}
                         <div className="properties-container" style={{ width: this.propertiesWidth(), color: SettingsManager.userColor }}>
-                            {this.propertiesWidth() < 10 ? null : <PropertiesView styleProvider={DefaultStyleProvider} addDocTab={DocumentViewInternal.addDocTabFunc} width={this.propertiesWidth()} height={this.propertiesHeight()} />}
+                            {
+                                <div style={{ display: this.propertiesWidth() < 10 ? 'none' : undefined }}>
+                                    <PropertiesView styleProvider={DefaultStyleProvider} addDocTab={DocumentViewInternal.addDocTabFunc} width={this.propertiesWidth()} height={this.propertiesHeight()} />
+                                </div>
+                            }
                         </div>
                     </div>
                 </div>
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 5d95c5fda..c174befc0 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -16,6 +16,7 @@ import { LightboxView } from './LightboxView';
 import { DocumentView, DocumentViewInternal } from './nodes/DocumentView';
 import './OverlayView.scss';
 import { DefaultStyleProvider } from './StyleProvider';
+const _global = (window /* browser */ || global) /* node */ as any;
 
 export type OverlayDisposer = () => void;
 
@@ -115,6 +116,20 @@ export class OverlayView extends React.Component {
         super(props);
         if (!OverlayView.Instance) {
             OverlayView.Instance = this;
+            new _global.ResizeObserver(
+                action((entries: any) => {
+                    for (const entry of entries) {
+                        DocListCast(Doc.MyOverlayDocs?.data).forEach(doc => {
+                            if (NumCast(doc.overlayX) > entry.contentRect.width - 10) {
+                                doc.overlayX = entry.contentRect.width - 10;
+                            }
+                            if (NumCast(doc.overlayY) > entry.contentRect.height - 10) {
+                                doc.overlayY = entry.contentRect.height - 10;
+                            }
+                        });
+                    }
+                })
+            ).observe(window.document.body);
         }
     }
 
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 42db0b9be..d939470e9 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -376,6 +376,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
             <Dropdown
                 tooltip={'Choose onClick behavior'}
                 items={items}
+                closeOnSelect={true}
                 selectedVal={this.onClickVal}
                 setSelectedVal={val => this.handleOptionChange(val as string)}
                 title={'Choose onClick behaviour'}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 72ff906f6..9cc75b1c6 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -52,6 +52,12 @@ interface PropertiesViewProps {
 export class PropertiesView extends React.Component<PropertiesViewProps> {
     private _widthUndo?: UndoManager.Batch;
 
+    public static Instance: PropertiesView | undefined;
+    constructor(props: any) {
+        super(props);
+        PropertiesView.Instance = this;
+    }
+
     @computed get MAX_EMBED_HEIGHT() {
         return 200;
     }
@@ -75,6 +81,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     @observable layoutFields: boolean = false;
+    @observable layoutDocAcls: boolean = false;
 
     @observable openOptions: boolean = true;
     @observable openSharing: boolean = true;
@@ -86,8 +93,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     @observable openTransform: boolean = true;
     @observable openFilters: boolean = false;
 
-    @observable layoutDocAcls: boolean = false;
-
     //Pres Trails booleans:
     @observable openPresTransitions: boolean = true;
     @observable openPresProgressivize: boolean = false;
@@ -103,7 +108,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         this._disposers.link = reaction(
             () => LinkManager.currentLink,
             link => {
-                link && this.onDoubleClick();
+                link && this.CloseAll();
                 link && (this.openLinks = true);
             },
             { fireImmediately: true }
@@ -970,7 +975,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     };
 
     @action
-    onDoubleClick = () => {
+    CloseAll = () => {
         this.openContexts = false;
         this.openLinks = false;
         this.openOptions = false;
@@ -1120,7 +1125,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 isOpen={this.openOptions}
                 setInSection={bool => (this.inOptions = bool)}
                 setIsOpen={bool => (this.openOptions = bool)}
-                onDoubleClick={this.onDoubleClick}
+                onDoubleClick={this.CloseAll}
             />
         );
     }
@@ -1147,7 +1152,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 }
                 isOpen={this.openSharing}
                 setIsOpen={bool => (this.openSharing = bool)}
-                onDoubleClick={() => this.onDoubleClick()}
+                onDoubleClick={() => this.CloseAll()}
             />
         );
     }
@@ -1187,7 +1192,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 }
                 isOpen={this.openFilters}
                 setIsOpen={bool => (this.openFilters = bool)}
-                onDoubleClick={() => this.onDoubleClick()}
+                onDoubleClick={() => this.CloseAll()}
             />
         );
     }
@@ -1197,8 +1202,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
         return (
             <>
-                <PropertiesSection title="Appearance" content={this.isInk ? this.appearanceEditor : null} isOpen={this.openAppearance} setIsOpen={bool => (this.openAppearance = bool)} onDoubleClick={() => this.onDoubleClick()} />
-                <PropertiesSection title="Transform" content={this.transformEditor} isOpen={this.openTransform} setIsOpen={bool => (this.openTransform = bool)} onDoubleClick={() => this.onDoubleClick()} />
+                <PropertiesSection title="Appearance" content={this.isInk ? this.appearanceEditor : null} isOpen={this.openAppearance} setIsOpen={bool => (this.openAppearance = bool)} onDoubleClick={() => this.CloseAll()} />
+                <PropertiesSection title="Transform" content={this.transformEditor} isOpen={this.openTransform} setIsOpen={bool => (this.openTransform = bool)} onDoubleClick={() => this.CloseAll()} />
             </>
         );
     }
@@ -1210,29 +1215,23 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 content={<div className="propertiesView-content fields">{Doc.noviceMode ? this.noviceFields : this.expandedField}</div>}
                 isOpen={this.openFields}
                 setIsOpen={bool => (this.openFields = bool)}
-                onDoubleClick={() => this.onDoubleClick()}
+                onDoubleClick={() => this.CloseAll()}
             />
         );
     }
 
     @computed get contextsSubMenu() {
         return (
-            <PropertiesSection
-                title="Other Contexts"
-                content={this.contextCount > 0 ? this.contexts : 'There are no other contexts.'}
-                isOpen={this.openContexts}
-                setIsOpen={bool => (this.openContexts = bool)}
-                onDoubleClick={() => this.onDoubleClick()}
-            />
+            <PropertiesSection title="Other Contexts" content={this.contextCount > 0 ? this.contexts : 'There are no other contexts.'} isOpen={this.openContexts} setIsOpen={bool => (this.openContexts = bool)} onDoubleClick={() => this.CloseAll()} />
         );
     }
 
     @computed get linksSubMenu() {
-        return <PropertiesSection title="Linked To" content={this.linkCount > 0 ? this.links : 'There are no current links.'} isOpen={this.openLinks} setIsOpen={bool => (this.openLinks = bool)} onDoubleClick={this.onDoubleClick} />;
+        return <PropertiesSection title="Linked To" content={this.linkCount > 0 ? this.links : 'There are no current links.'} isOpen={this.openLinks} setIsOpen={bool => (this.openLinks = bool)} onDoubleClick={this.CloseAll} />;
     }
 
     @computed get layoutSubMenu() {
-        return <PropertiesSection title="Layout" content={this.layoutPreview} isOpen={this.openLayout} setIsOpen={bool => (this.openLayout = bool)} onDoubleClick={this.onDoubleClick} />;
+        return <PropertiesSection title="Layout" content={this.layoutPreview} isOpen={this.openLayout} setIsOpen={bool => (this.openLayout = bool)} onDoubleClick={this.CloseAll} />;
     }
 
     @computed get description() {
@@ -1723,7 +1722,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                         {this.contextsSubMenu}
                         {this.fieldsSubMenu}
                         {isNovice ? null : this.sharingSubMenu}
-                        {isNovice ? null : this.filtersSubMenu}
+                        {this.filtersSubMenu}
                         {isNovice ? null : this.layoutSubMenu}
                     </div>
                 );
diff --git a/src/client/views/StyleProvider.scss b/src/client/views/StyleProvider.scss
index c06bb287e..f069e7e1b 100644
--- a/src/client/views/StyleProvider.scss
+++ b/src/client/views/StyleProvider.scss
@@ -18,10 +18,15 @@
     cursor: default;
 }
 .styleProvider-filter {
-    right: 0;
+    right: 15;
+    .styleProvider-filterShift {
+        left: 0;
+        top: 0;
+        position: absolute;
+    }
 }
 .styleProvider-audio {
-    right: 15;
+    right: 30;
 }
 .styleProvider-lock:hover,
 .styleProvider-audio:hover,
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 069bfd049..8a5ad3139 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -1,7 +1,7 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
-import { IconButton, Shadows, Size } from 'browndash-components';
+import { Dropdown, DropdownType, IconButton, IListItemProps, ListBox, ListItem, Popup, Shadows, Size, Type } from 'browndash-components';
 import { action, runInAction } from 'mobx';
 import { extname } from 'path';
 import { BsArrowDown, BsArrowDownUp, BsArrowUp } from 'react-icons/bs';
@@ -24,6 +24,8 @@ import { KeyValueBox } from './nodes/KeyValueBox';
 import { SliderBox } from './nodes/SliderBox';
 import './StyleProvider.scss';
 import React = require('react');
+import { PropertiesView } from './PropertiesView';
+import { FaFilter } from 'react-icons/fa';
 
 export enum StyleProp {
     TreeViewIcon = 'treeView_Icon',
@@ -283,13 +285,44 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
             const filter = () => {
                 const showFilterIcon =
                     StrListCast(doc?._childFilters).length || StrListCast(doc?._childFiltersByRanges).length
-                        ? '#18c718bd' //'hasFilter'
+                        ? 'green' // #18c718bd' //'hasFilter'
                         : docProps?.childFilters?.().filter(f => Utils.IsRecursiveFilter(f) && f !== Utils.noDragsDocFilter).length || docProps?.childFiltersByRanges().length
                         ? 'orange' //'inheritsFilter'
                         : undefined;
                 return !showFilterIcon ? null : (
-                    <div className="styleProvider-filter" onClick={action(() => (SettingsManager.propertiesWidth = 250))}>
-                        <FontAwesomeIcon icon={'filter'} size="lg" style={{ position: 'absolute', top: '1%', right: '1%', cursor: 'pointer', padding: 1, color: showFilterIcon, zIndex: 1 }} />
+                    <div className="styleProvider-filter">
+                        <Dropdown
+                            type={Type.TERT}
+                            dropdownType={DropdownType.CLICK}
+                            fillWidth
+                            iconProvider={(active:boolean) => <div className='styleProvider-filterShift'><FaFilter/></div>}
+                            closeOnSelect={true}
+                            setSelectedVal={
+                                action((dv) => {
+                                    (dv as any).select(false);
+                                    (SettingsManager.propertiesWidth = 250);
+                                    setTimeout(action(() => {
+                                        if (PropertiesView.Instance) {
+                                            PropertiesView.Instance.CloseAll();
+                                            PropertiesView.Instance.openFilters = true;
+                                        }
+                                    }));
+                                })
+                            }
+                            size={Size.XSMALL}
+                            width={15}
+                            height={15}
+                            title={showFilterIcon === 'green' ? 
+                                "This view is filtered.  Click to view/change filters":
+                                "this view inherits filters from one of its parents"}
+                            color={SettingsManager.userColor}
+                            background={showFilterIcon}
+                            items={[...(props?.docViewPath?.()??[]), ...(props?.DocumentView?[props?.DocumentView?.()]:[])].map(dv => ({
+                                text: StrCast(dv.rootDoc.title),
+                                val: dv as any,
+                                style: {color:SettingsManager.userColor, background:SettingsManager.userBackgroundColor},
+                            } as IListItemProps)) }
+                        />
                     </div>
                 );
             };
diff --git a/src/client/views/UndoStack.tsx b/src/client/views/UndoStack.tsx
index 1afd5ad22..093bb8b9c 100644
--- a/src/client/views/UndoStack.tsx
+++ b/src/client/views/UndoStack.tsx
@@ -1,14 +1,12 @@
-import { action, observable } from 'mobx';
+import { Tooltip } from '@mui/material';
+import { Popup, Type } from 'browndash-components';
+import { observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { UndoManager } from '../util/UndoManager';
-import './UndoStack.scss';
 import { StrCast } from '../../fields/Types';
-import { Doc } from '../../fields/Doc';
-import { Popup, Type, isDark } from 'browndash-components';
-import { Colors } from './global/globalEnums';
 import { SettingsManager } from '../util/SettingsManager';
-import { Tooltip } from '@mui/material';
+import { UndoManager } from '../util/UndoManager';
+import './UndoStack.scss';
 
 interface UndoStackProps {
     width?: number;
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 9ea1ed2de..d9faab063 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -17,6 +17,7 @@ import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'
 import { DocumentManager } from '../../util/DocumentManager';
 import { DragManager, dropActionType } from '../../util/DragManager';
 import { SelectionManager } from '../../util/SelectionManager';
+import { SettingsManager } from '../../util/SettingsManager';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
 import { undoable, UndoManager } from '../../util/UndoManager';
@@ -33,7 +34,6 @@ import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormV
 import { CollectionView } from './CollectionView';
 import './TabDocView.scss';
 import React = require('react');
-import { SettingsManager } from '../../util/SettingsManager';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 interface TabDocViewProps {
@@ -420,7 +420,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     PanelHeight = () => this._panelHeight;
     miniMapColor = () => Colors.MEDIUM_GRAY;
     tabView = () => this._view;
-    disableMinimap = () => !this._document || this._document.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this._document)) || this._document?._type_collection !== CollectionViewType.Freeform;
+    disableMinimap = () => !this._document;
     whenChildContentActiveChanges = (isActive: boolean) => (this._isAnyChildContentActive = isActive);
     isContentActive = () => this._isContentActive;
     waitForDoubleClick = () => (DocumentView.ExploreMode ? 'never' : undefined);
@@ -461,9 +461,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     bringToFront={emptyFunction}
                     pinToPres={TabDocView.PinDoc}
                 />
-                {this.disableMinimap() || this._document._type_collection !== CollectionViewType.Freeform ? null : (
-                    <TabMinimapView key="minimap" addDocTab={this.addDocTab} PanelHeight={this.PanelHeight} PanelWidth={this.PanelWidth} background={this.miniMapColor} document={this._document} tabView={this.tabView} />
-                )}
+                {this.disableMinimap() ? null : <TabMinimapView key="minimap" addDocTab={this.addDocTab} PanelHeight={this.PanelHeight} PanelWidth={this.PanelWidth} background={this.miniMapColor} document={this._document} tabView={this.tabView} />}
             </>
         );
     }
@@ -615,7 +613,7 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
         );
     };
     render() {
-        return (
+        return this.props.document.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this.props.document)) || this.props.document?._type_collection !== CollectionViewType.Freeform ? null : (
             <div className="miniMap-hidden">
                 <Popup icon={<FontAwesomeIcon icon="globe-asia" size="lg" />} color={SettingsManager.userVariantColor} type={Type.TERT} onPointerDown={e => e.stopPropagation()} placement={'top-end'} popup={this.popup} />
             </div>
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 182a33fd1..5c7dcc1a4 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -609,10 +609,10 @@ export class CollectionSchemaView extends CollectionSubView() {
         this._menuKeys = this.documentKeys.filter(value => value.toLowerCase().includes(this._menuValue.toLowerCase()));
     };
 
-    getFieldFilters = (field: string) => StrListCast(this.Document._childFilters).filter(filter => filter.split(':')[0] == field);
+    getFieldFilters = (field: string) => StrListCast(this.Document._childFilters).filter(filter => filter.split(Doc.FilterSep)[0] == field);
 
     removeFieldFilters = (field: string) => {
-        this.getFieldFilters(field).forEach(filter => Doc.setDocFilter(this.Document, field, filter.split(':')[1], 'remove'));
+        this.getFieldFilters(field).forEach(filter => Doc.setDocFilter(this.Document, field, filter.split(Doc.FilterSep)[1], 'remove'));
     };
 
     onFilterKeyDown = (e: React.KeyboardEvent) => {
@@ -766,8 +766,8 @@ export class CollectionSchemaView extends CollectionSubView() {
         return keyOptions.map(key => {
             let bool = false;
             if (filters !== undefined) {
-                const ind = filters.findIndex(filter => filter.split(':')[1] === key);
-                const fields = ind === -1 ? undefined : filters[ind].split(':');
+                const ind = filters.findIndex(filter => filter.split(Doc.FilterSep)[1] === key);
+                const fields = ind === -1 ? undefined : filters[ind].split(Doc.FilterSep);
                 bool = fields ? fields[2] === 'check' : false;
             }
             return (
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index 1eb6fd51c..0a59818ad 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -246,10 +246,11 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         return (
             <Dropdown
                 selectedVal={text}
-                setSelectedVal={undoable(val => script.script.run({ this: this.layoutDoc, self: this.rootDoc, val }), `dropdown select ${this.label}`)}
+                setSelectedVal={undoable(value => script.script.run({ this: this.layoutDoc, self: this.rootDoc, value }), `dropdown select ${this.label}`)}
                 color={SettingsManager.userColor}
                 background={SettingsManager.userVariantColor}
                 type={Type.TERT}
+                closeOnSelect={false}
                 dropdownType={DropdownType.SELECT}
                 onItemDown={this.dropdownItemDown}
                 items={list}
diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
index 1f113110b..481e43feb 100644
--- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
@@ -119,7 +119,7 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
     public static replayWorkspace(value: Doc) {
         Doc.UserDoc().currentRecording = value;
         value.overlayX = 70;
-        value.overlayY = 590;
+        value.overlayY = window.innerHeight - 180;
         Doc.AddToMyOverlay(value);
         DocumentManager.Instance.AddViewRenderedCb(value, docView => {
             Doc.UserDoc().currentRecording = docView.rootDoc;
@@ -147,11 +147,10 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     public static resumeWorkspaceReplaying(doc: Doc) {
         const docView = DocumentManager.Instance.getDocumentView(doc);
-        const videoBox = docView?.ComponentView as VideoBox;
-        if (videoBox) {
-            videoBox.Play();
-            Doc.UserDoc().workspaceReplayingState = media_state.Playing;
+        if (docView?.ComponentView instanceof VideoBox) {
+            docView.ComponentView.Play();
         }
+        Doc.UserDoc().workspaceReplayingState = media_state.Playing;
     }
 
     @action
-- 
cgit v1.2.3-70-g09d2


From 18e39170619a9b32cbecc50f5b857631c1a33251 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 8 Sep 2023 17:35:17 -0400
Subject: from last

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

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

diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index 0a59818ad..394108be4 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -185,6 +185,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
             emptyFunction,
             emptyFunction
         );
+        return false;
     };
 
     /**
-- 
cgit v1.2.3-70-g09d2


From 34f3bab0f4d55b2f55f38e451bfd024994ecff42 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 20 Sep 2023 13:20:53 -0400
Subject: added carousel3D to novice and fixed it's button positions and its
 focus so that it works with trails. added dragging documents within single
 collection for rearranging order of docs in a tab collection.  fixed freeform
 views set to fitWidth to show decorations properly. turned off scaling of
 tree views.  updated lightbox buttons.  fixed tooltips on properties toggles.

---
 src/client/util/CurrentUserUtils.ts                |   4 +-
 src/client/util/DragManager.ts                     |   2 +-
 src/client/views/LightboxView.scss                 |  11 +--
 src/client/views/LightboxView.tsx                  | 106 +++++++++++++--------
 src/client/views/PropertiesButtons.tsx             |  12 ++-
 .../views/collections/CollectionCarousel3DView.tsx |  17 +++-
 src/client/views/collections/CollectionSubView.tsx |   5 +-
 .../views/collections/CollectionTreeView.tsx       |   4 +-
 src/client/views/collections/TreeView.tsx          |   6 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   9 +-
 src/client/views/nodes/FontIconBox/FontIconBox.tsx |   2 +-
 11 files changed, 109 insertions(+), 69 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 259c028cc..2ea5972ee 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -492,8 +492,8 @@ export class CurrentUserUtils {
         const childContextMenuIcons = ["tv", "camera", "users", "times", "trash"]; // entries must be kept in synch with childContextMenuScripts, childContextMenuLabels, and childContextMenuFilters
         const reqdOpts:DocumentOptions = {
             title: "My Dashboards", childHideLinkButton: true, treeView_FreezeChildren: "remove|add", treeView_HideTitle: true, layout_boxShadow: "0 0", childDontRegisterViews: true,
-            dropAction: "same", treeView_Type: TreeViewType.fileSystem, isFolder: true, isSystem: true,  treeView_TruncateTitleWidth: 350, ignoreClick: true, 
-            layout_headerButton: newDashboardButton, childDragAction: "none",
+            dropAction: "inSame", treeView_Type: TreeViewType.fileSystem, isFolder: true, isSystem: true,  treeView_TruncateTitleWidth: 350, ignoreClick: true, 
+            layout_headerButton: newDashboardButton, childDragAction: "inSame",
             _layout_showTitle: "title", _height: 400, _gridGap: 5, _forceActive: true, _lockedPosition: true,
             contextMenuLabels:new List<string>(contextMenuLabels),
             contextMenuIcons:new List<string>(contextMenuIcons),
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index ed22e70bd..f86f9a3e5 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -14,7 +14,7 @@ import { SelectionManager } from './SelectionManager';
 import { SnappingManager } from './SnappingManager';
 import { UndoManager } from './UndoManager';
 
-export type dropActionType = 'embed' | 'copy' | 'move' | 'add' | 'same' | 'proto' | 'none' | undefined; // undefined = move, "same" = move but don't call dropPropertiesToRemove
+export type dropActionType = 'embed' | 'copy' | 'move' | 'add' | 'same' | 'inSame' | 'proto' | 'none' | undefined; // undefined = move, "same" = move but don't call dropPropertiesToRemove
 
 /**
  * Initialize drag
diff --git a/src/client/views/LightboxView.scss b/src/client/views/LightboxView.scss
index f86a1d211..9a9b8a437 100644
--- a/src/client/views/LightboxView.scss
+++ b/src/client/views/LightboxView.scss
@@ -5,7 +5,6 @@
     top: 10;
     background: transparent;
     border-radius: 8;
-    color: white;
     opacity: 0.7;
     width: 25;
     flex-direction: column;
@@ -17,11 +16,10 @@
 .lightboxView-tabBtn {
     margin: auto;
     position: absolute;
-    right: 38;
+    right: 45;
     top: 10;
     background: transparent;
     border-radius: 8;
-    color: white;
     opacity: 0.7;
     width: 25;
     flex-direction: column;
@@ -33,11 +31,10 @@
 .lightboxView-penBtn {
     margin: auto;
     position: absolute;
-    right: 70;
+    right: 80;
     top: 10;
     background: transparent;
     border-radius: 8;
-    color: white;
     opacity: 0.7;
     width: 25;
     flex-direction: column;
@@ -49,11 +46,10 @@
 .lightboxView-exploreBtn {
     margin: auto;
     position: absolute;
-    right: 100;
+    right: 115;
     top: 10;
     background: transparent;
     border-radius: 8;
-    color: white;
     opacity: 0.7;
     width: 25;
     flex-direction: column;
@@ -68,7 +64,6 @@
     left: 0;
     width: 100%;
     height: 100%;
-    background: #000000bb;
     z-index: 1000;
     .lightboxView-contents {
         position: absolute;
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index 8f081b321..93eaec959 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -1,15 +1,17 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Toggle, ToggleType, Type } from 'browndash-components';
 import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
 import { InkTool } from '../../fields/InkField';
-import { Cast, NumCast, StrCast } from '../../fields/Types';
+import { BoolCast, Cast, NumCast, StrCast } from '../../fields/Types';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnTrue } from '../../Utils';
 import { DocUtils } from '../documents/Documents';
 import { DocumentManager } from '../util/DocumentManager';
 import { LinkManager } from '../util/LinkManager';
 import { SelectionManager } from '../util/SelectionManager';
+import { SettingsManager } from '../util/SettingsManager';
 import { Transform } from '../util/Transform';
 import { CollectionDockingView } from './collections/CollectionDockingView';
 import { CollectionStackedTimeline } from './collections/CollectionStackedTimeline';
@@ -118,7 +120,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                     width: bottom !== undefined ? undefined : Math.min(this.props.PanelWidth / 4, this.props.maxBorder[0]),
                     bottom,
                 }}>
-                <div className="lightboxView-navBtn" title={color} style={{ top, color: color ? 'red' : 'white', background: color ? 'white' : undefined }} onClick={click}>
+                <div className="lightboxView-navBtn" title={color} style={{ top, color: SettingsManager.userColor, background: undefined }} onClick={click}>
                     <div style={{ height: 10 }}>{color}</div>
                     <FontAwesomeIcon icon={icon as any} size="3x" />
                 </div>
@@ -229,6 +231,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                     downx = e.clientX;
                     downy = e.clientY;
                 }}
+                style={{ background: SettingsManager.userBackgroundColor }}
                 onClick={e => {
                     if (Math.abs(downx - e.clientX) < 4 && Math.abs(downy - e.clientY) < 4) {
                         LightboxView.SetLightboxDoc(undefined);
@@ -242,6 +245,8 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                         width: this.lightboxWidth(),
                         height: this.lightboxHeight(),
                         clipPath: `path('${Doc.UserDoc().renderStyle === 'comic' ? wavyBorderPath(this.lightboxWidth(), this.lightboxHeight()) : undefined}')`,
+                        background: SettingsManager.userBackgroundColor,
+                        color: SettingsManager.userColor,
                     }}>
                     {/* <CollectionMenu /> TODO:glr This is where it would go*/}
 
@@ -299,47 +304,68 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                     this.future()?.length.toString()
                 )}
                 <LightboxTourBtn navBtn={this.navBtn} future={this.future} stepInto={this.stepInto} />
-                <div
-                    className="lightboxView-navBtn"
-                    title="toggle fit width"
-                    onClick={e => {
-                        e.stopPropagation();
-                        LightboxView.LightboxDoc!._layout_fitWidth = !LightboxView.LightboxDoc!._layout_fitWidth;
-                    }}>
-                    <FontAwesomeIcon icon={LightboxView.LightboxDoc?._layout_fitWidth ? 'arrows-alt-h' : 'arrows-alt-v'} size="2x" />
+                <div className="lightboxView-navBtn">
+                    <Toggle
+                        tooltip="toggle reading view"
+                        color={SettingsManager.userColor}
+                        background={BoolCast(LightboxView.LightboxDoc!._layout_fitWidth) ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor}
+                        toggleType={ToggleType.BUTTON}
+                        type={Type.TERT}
+                        toggleStatus={BoolCast(LightboxView.LightboxDoc!._layout_fitWidth)}
+                        onClick={e => {
+                            e.stopPropagation();
+                            LightboxView.LightboxDoc!._layout_fitWidth = !LightboxView.LightboxDoc!._layout_fitWidth;
+                        }}
+                        icon={<FontAwesomeIcon icon={LightboxView.LightboxDoc?._layout_fitWidth ? 'book-open' : 'book'} size="sm" />}
+                    />
                 </div>
-                <div
-                    className="lightboxView-tabBtn"
-                    title="open in tab"
-                    onClick={e => {
-                        const lightdoc = LightboxView._docTarget || LightboxView._doc!;
-                        e.stopPropagation();
-                        Doc.RemoveDocFromList(Doc.MyRecentlyClosed, 'data', lightdoc);
-                        CollectionDockingView.AddSplit(lightdoc, OpenWhereMod.none);
-                        SelectionManager.DeselectAll();
-                        LightboxView.SetLightboxDoc(undefined);
-                    }}>
-                    <FontAwesomeIcon icon={'file-download'} size="2x" />
+                <div className="lightboxView-tabBtn">
+                    <Toggle
+                        tooltip="open document in a tab"
+                        color={SettingsManager.userColor}
+                        background={SettingsManager.userBackgroundColor}
+                        toggleType={ToggleType.BUTTON}
+                        type={Type.TERT}
+                        icon={<FontAwesomeIcon icon="file-download" size="sm" />}
+                        onClick={e => {
+                            const lightdoc = LightboxView._docTarget || LightboxView._doc!;
+                            e.stopPropagation();
+                            Doc.RemoveDocFromList(Doc.MyRecentlyClosed, 'data', lightdoc);
+                            CollectionDockingView.AddSplit(lightdoc, OpenWhereMod.none);
+                            SelectionManager.DeselectAll();
+                            LightboxView.SetLightboxDoc(undefined);
+                        }}
+                    />
                 </div>
-                <div
-                    className="lightboxView-penBtn"
-                    title="toggle pen annotation"
-                    style={{ background: Doc.ActiveTool === InkTool.Pen ? 'white' : undefined }}
-                    onClick={e => {
-                        e.stopPropagation();
-                        Doc.ActiveTool = Doc.ActiveTool === InkTool.Pen ? InkTool.None : InkTool.Pen;
-                    }}>
-                    <FontAwesomeIcon color={Doc.ActiveTool === InkTool.Pen ? 'black' : 'white'} icon={'pen'} size="2x" />
+                <div className="lightboxView-penBtn">
+                    <Toggle
+                        tooltip="toggle pen annotation"
+                        color={SettingsManager.userColor}
+                        background={Doc.ActiveTool === InkTool.Pen ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor}
+                        toggleType={ToggleType.BUTTON}
+                        toggleStatus={Doc.ActiveTool === InkTool.Pen}
+                        type={Type.TERT}
+                        icon={<FontAwesomeIcon icon="pen" size="sm" />}
+                        onClick={e => {
+                            e.stopPropagation();
+                            Doc.ActiveTool = Doc.ActiveTool === InkTool.Pen ? InkTool.None : InkTool.Pen;
+                        }}
+                    />
                 </div>
-                <div
-                    className="lightboxView-exploreBtn"
-                    title="toggle explore mode to navigate among documents only"
-                    style={{ background: DocumentView.ExploreMode ? 'white' : undefined }}
-                    onClick={action(e => {
-                        e.stopPropagation();
-                        DocumentView.ExploreMode = !DocumentView.ExploreMode;
-                    })}>
-                    <FontAwesomeIcon color={DocumentView.ExploreMode ? 'black' : 'white'} icon={'globe-americas'} size="2x" />
+                <div className="lightboxView-exploreBtn">
+                    <Toggle
+                        tooltip="toggle explore mode to navigate among documents only"
+                        color={SettingsManager.userColor}
+                        background={DocumentView.ExploreMode ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor}
+                        toggleType={ToggleType.BUTTON}
+                        type={Type.TERT}
+                        toggleStatus={DocumentView.ExploreMode}
+                        icon={<FontAwesomeIcon icon="globe-americas" size="sm" />}
+                        onClick={action(e => {
+                            e.stopPropagation();
+                            DocumentView.ExploreMode = !DocumentView.ExploreMode;
+                        })}
+                    />
                 </div>
             </div>
         );
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 40d42a4de..d1561fd67 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -57,6 +57,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
         return !targetDoc ? null : (
             <Toggle
                 toggleStatus={BoolCast(targetDoc[property])}
+                tooltip={tooltip(BoolCast(targetDoc[property]))}
                 text={label(targetDoc?.[property])}
                 color={SettingsManager.userColor}
                 icon={icon(targetDoc?.[property] as any)}
@@ -165,9 +166,9 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     @computed get forceActiveButton() {
         //select text
         return this.propertyToggleBtn(
-            on => (on ? 'INACTIVE INTERACTION' : 'ACTIVE INTERACTION'),
+            on => (on ? 'SELECT TO INTERACT' : 'ALWAYS INTERACTIVE'),
             '_forceActive',
-            on => `${on ? 'Select to activate' : 'Contents always active'} `,
+            on => `${on ? 'Document must be selected to interact with its contents' : 'Contents always active (respond to click/drag events)'} `,
             on => <MdTouchApp /> // 'eye'
         );
     }
@@ -210,9 +211,12 @@ export class PropertiesButtons extends React.Component<{}, {}> {
 
     @computed get layout_fitWidthButton() {
         return this.propertyToggleBtn(
-            on => (on ? 'RESTRICT WIDTH' : 'FIT WIDTH'), //'Fit\xA0Width',
+            on => (on ? 'SCALED VIEW' : 'READING VIEW'), //'Fit\xA0Width',
             '_layout_fitWidth',
-            on => `${on ? "Don't" : 'Do'} fit content to width of container`,
+            on =>
+                on
+                    ? "Scale document so it's width and height fit container (no effect when document is viewed on freeform canvas)"
+                    : "Scale document so it's width fits container and its height expands/contracts to fit available space  (no effect when document is viewed on freeform canvas)",
             on => (on ? <AiOutlineColumnWidth /> : <RxWidth />) // 'arrows-alt-h'
         );
     }
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index d94e552b4..cd59a91a1 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -2,14 +2,15 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { computed } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { Doc } from '../../../fields/Doc';
+import { Doc, DocListCast } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
-import { NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { returnFalse, returnZero, Utils } from '../../../Utils';
+import { DocumentType } from '../../documents/DocumentTypes';
 import { DragManager } from '../../util/DragManager';
 import { SelectionManager } from '../../util/SelectionManager';
 import { CAROUSEL3D_CENTER_SCALE, CAROUSEL3D_SIDE_SCALE, CAROUSEL3D_TOP } from '../global/globalCssVariables.scss';
-import { DocumentView } from '../nodes/DocumentView';
+import { DocFocusOptions, DocumentView } from '../nodes/DocumentView';
 import { StyleProp } from '../StyleProvider';
 import './CollectionCarousel3DView.scss';
 import { CollectionSubView } from './CollectionSubView';
@@ -46,6 +47,15 @@ export class CollectionCarousel3DView extends CollectionSubView() {
             .translate(-this.panelWidth() + ((this.centerScale - 1) * this.panelWidth()) / 2, -((Number(CAROUSEL3D_TOP) / 100) * this.props.PanelHeight()) + ((this.centerScale - 1) * this.panelHeight()) / 2)
             .scale(1 / this.centerScale);
 
+    focus = (anchor: Doc, options: DocFocusOptions) => {
+        const docs = DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]);
+        if (anchor.type !== DocumentType.CONFIG && !docs.includes(anchor)) return;
+        options.didMove = true;
+        const target = DocCast(anchor.annotationOn) ?? anchor;
+        const index = docs.indexOf(target);
+        index !== -1 && (this.layoutDoc._carousel_index = index);
+        return undefined;
+    };
     @computed get content() {
         const currentIndex = NumCast(this.layoutDoc._carousel_index);
         const displayDoc = (childPair: { layout: Doc; data: Doc }) => {
@@ -61,6 +71,7 @@ export class CollectionCarousel3DView extends CollectionSubView() {
                     LayoutTemplateString={this.props.childLayoutString}
                     Document={childPair.layout}
                     DataDoc={childPair.data}
+                    focus={this.focus}
                     ScreenToLocalTransform={this.childScreenToLocal}
                     isContentActive={this.isChildContentActive}
                     isDocumentActive={this.props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 54a60271a..26272d2ee 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -210,11 +210,12 @@ export function CollectionSubView<X>(moreProps?: X) {
                 const targetDocments = DocListCast(this.dataDoc[this.props.fieldKey]);
                 const someMoved = !dropAction && docDragData.draggedDocuments.some(drag => targetDocments.includes(drag));
                 if (someMoved) docDragData.droppedDocuments = docDragData.droppedDocuments.map((drop, i) => (targetDocments.includes(docDragData.draggedDocuments[i]) ? docDragData.draggedDocuments[i] : drop));
-                if ((!dropAction || dropAction === 'same' || dropAction === 'move' || someMoved) && docDragData.moveDocument) {
+                if ((!dropAction || dropAction === 'inSame' || dropAction === 'same' || dropAction === 'move' || someMoved) && docDragData.moveDocument) {
                     const movedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] === d);
                     const addedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] !== d);
                     if (movedDocs.length) {
-                        const canAdd = de.embedKey || dropAction || Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.rootDoc);
+                        const canAdd =
+                            (de.embedKey || dropAction || Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.rootDoc)) && (dropAction !== 'inSame' || docDragData.draggedDocuments.every(d => d.embedContainer === this.rootDoc));
                         const moved = docDragData.moveDocument(movedDocs, this.rootDoc, canAdd ? this.addDocument : returnFalse);
                         added = canAdd || moved ? moved : undefined;
                     } else {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index eed04b3ee..9e5ac77d9 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -149,7 +149,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
             if (isAlreadyInTree() !== sameTree) {
                 console.log('WHAAAT');
             }
-            dragData.dropAction = dropAction && !isAlreadyInTree() ? dropAction : sameTree ? 'same' : dragData.dropAction;
+            dragData.dropAction = dropAction && !isAlreadyInTree() ? dropAction : sameTree && dragData.dropAction !== 'inSame' ? 'same' : dragData.dropAction;
             e.stopPropagation();
         }
     };
@@ -438,7 +438,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     render() {
         TraceMobx();
 
-        const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1) || 1;
+        const scale = this.props.NativeDimScaling?.() || 1;
         return (
             <div style={{ transform: `scale(${scale})`, transformOrigin: 'top left', width: `${100 / scale}%`, height: `${100 / scale}%` }}>
                 {!(this.doc instanceof Doc) || !this.treeChildren ? null : this.doc.treeView_HasOverlay ? (
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index cd526c77e..f89aa065b 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -433,9 +433,9 @@ export class TreeView extends React.Component<TreeViewProps> {
             return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && innerAdd(doc), true as boolean);
         };
         const addDoc = inside ? localAdd : parentAddDoc;
-        const move = (!dropAction || dropAction === 'proto' || dropAction === 'move' || dropAction === 'same') && moveDocument;
+        const move = (!dropAction || dropAction === 'proto' || dropAction === 'move' || dropAction === 'same' || dropAction === 'inSame') && moveDocument;
         const canAdd = (!this.props.treeView.outlineMode && !StrCast((inside ? this.props.document : this.props.treeViewParent)?.treeView_FreezeChildren).includes('add')) || forceAdd;
-        if (canAdd) {
+        if (canAdd && (dropAction !== 'inSame' || droppedDocuments.every(d => d.embedContainer === this.props.parentTreeView?.doc))) {
             this.props.parentTreeView instanceof TreeView && (this.props.parentTreeView.dropping = true);
             const res = droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) || (dropAction === 'proto' ? addDoc(d) : false) : addDoc(d)) || added, false);
             this.props.parentTreeView instanceof TreeView && (this.props.parentTreeView.dropping = false);
@@ -598,7 +598,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                 }
                 const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[key])) instanceof ComputedField;
                 const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
-                !dataIsComputed && added && Doc.SetContainer(doc, DocCast(this.doc.embedContainer));
+                !dataIsComputed && added && Doc.SetContainer(doc, this.doc);
 
                 return added;
             };
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 8a812c671..9df96fabc 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -33,7 +33,6 @@ import { undoBatch, UndoManager } from '../../../util/UndoManager';
 import { COLLECTION_BORDER_WIDTH } from '../../../views/global/globalCssVariables.scss';
 import { Timeline } from '../../animationtimeline/Timeline';
 import { ContextMenu } from '../../ContextMenu';
-import { DocumentDecorations } from '../../DocumentDecorations';
 import { GestureOverlay } from '../../GestureOverlay';
 import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, InkingStroke, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke';
 import { LightboxView } from '../../LightboxView';
@@ -187,7 +186,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         return this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth);
     }
     @computed get cachedGetTransform(): Transform {
-        return this.getContainerTransform().translate(-this.cachedCenteringShiftX, -this.cachedCenteringShiftY).transform(this.cachedGetLocalTransform);
+        return this.getContainerTransform()
+            .scale(this.props.isAnnotationOverlay ? 1 : 1 / this.nativeDim())
+            .translate(-this.cachedCenteringShiftX, -this.cachedCenteringShiftY)
+            .transform(this.cachedGetLocalTransform);
     }
 
     public static gotoKeyframe(timer: NodeJS.Timeout | undefined, docs: Doc[], duration: number) {
@@ -862,7 +864,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 // create a new curve by appending all curves of the current segment together in order to render a single new stroke.
                 if (!e.shiftKey) {
                     this._eraserLock++;
-                    this.segmentInkStroke(intersect.inkView, intersect.t).forEach(segment =>
+                    const segments = this.segmentInkStroke(intersect.inkView, intersect.t);
+                    segments.forEach(segment =>
                         this.forceStrokeGesture(
                             e,
                             GestureUtils.Gestures.Stroke,
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index 394108be4..14a3d16ef 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -226,7 +226,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
             } else {
                 return <Button text="None Selected" type={Type.TERT} color={SettingsManager.userColor} background={SettingsManager.userVariantColor} fillWidth inactive />;
             }
-            noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking, CollectionViewType.NoteTaking];
+            noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Carousel3D, CollectionViewType.Stacking, CollectionViewType.NoteTaking];
         } else {
             text = script?.script.run({ this: this.layoutDoc, self: this.rootDoc, value: '', _readOnly_: true }).result;
             // text = StrCast((RichTextMenu.Instance?.TextView?.EditorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
-- 
cgit v1.2.3-70-g09d2


From 0df1fc3fd85c1dea84dcaec5a934fc08f0da6832 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 4 Oct 2023 07:46:05 -0400
Subject: several fixes for webclips, linking to pdf/web, fonticon dropdowns.
 removed sliderbox. reduce uses of scriptContext

fixed web clipping annotations to be in correct spot and not to crash because of using a url that it doesn't have.
fixed pdf/web links to not use anchor from other end of link. because of sharing of GetAnchor global.
added a backup when presbox overwrites a doc's data field.
removed sliderBox.
fixed fontIcon dropdowns to not call click script twice.
removed scriptContext where it wasn't needed which is everywhere except TreeViews.
---
 src/client/documents/DocumentTypes.ts              |   3 +-
 src/client/documents/Documents.ts                  |   8 --
 src/client/util/CurrentUserUtils.ts                |   2 +-
 src/client/views/MainView.tsx                      |   2 -
 src/client/views/StyleProvider.tsx                 |   2 -
 .../views/collections/CollectionNoteTakingView.tsx |   1 -
 .../collections/CollectionStackedTimeline.tsx      |  37 +++-----
 .../views/collections/CollectionStackingView.tsx   |   1 -
 .../collectionSchema/CollectionSchemaView.tsx      |   1 -
 src/client/views/nodes/DocumentContentsView.tsx    |   2 -
 src/client/views/nodes/FontIconBox/FontIconBox.tsx |   1 -
 src/client/views/nodes/PDFBox.tsx                  |   2 +-
 src/client/views/nodes/SliderBox.scss              |  19 ----
 src/client/views/nodes/SliderBox.tsx               | 103 ---------------------
 src/client/views/nodes/WebBox.scss                 |   1 -
 src/client/views/nodes/WebBox.tsx                  |  80 +++++++++++-----
 src/client/views/nodes/trails/PresBox.tsx          |   7 +-
 src/client/views/pdf/PDFViewer.tsx                 |   5 +-
 18 files changed, 82 insertions(+), 195 deletions(-)
 delete mode 100644 src/client/views/nodes/SliderBox.scss
 delete mode 100644 src/client/views/nodes/SliderBox.tsx

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

diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts
index f8d129e79..87010f770 100644
--- a/src/client/documents/DocumentTypes.ts
+++ b/src/client/documents/DocumentTypes.ts
@@ -30,14 +30,13 @@ export enum DocumentType {
     // special purpose wrappers that either take no data or are compositions of lower level types
     LINK = 'link',
     IMPORT = 'import',
-    SLIDER = 'slider',
     PRES = 'presentation',
     PRESELEMENT = 'preselement',
     COLOR = 'color',
     YOUTUBE = 'youtube',
     COMPARISON = 'comparison',
     GROUP = 'group',
-    PUSHPIN = "pushpin",
+    PUSHPIN = 'pushpin',
 
     SCRIPTDB = 'scriptdb', // database of scripts
     GROUPDB = 'groupdb', // database of groups
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index f7ceef4f4..a79a62b57 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -54,7 +54,6 @@ import { PhysicsSimulationBox } from '../views/nodes/PhysicsBox/PhysicsSimulatio
 import { RecordingBox } from '../views/nodes/RecordingBox/RecordingBox';
 import { ScreenshotBox } from '../views/nodes/ScreenshotBox';
 import { ScriptingBox } from '../views/nodes/ScriptingBox';
-import { SliderBox } from '../views/nodes/SliderBox';
 import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox';
 import { PresBox } from '../views/nodes/trails/PresBox';
 import { PresElementBox } from '../views/nodes/trails/PresElementBox';
@@ -628,13 +627,6 @@ export namespace Docs {
                     options: {},
                 },
             ],
-            [
-                DocumentType.SLIDER,
-                {
-                    layout: { view: SliderBox, dataField: defaultDataKey },
-                    options: {},
-                },
-            ],
             [
                 DocumentType.PRES,
                 {
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 2ea5972ee..cc8f72ddf 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -820,7 +820,7 @@ export class CurrentUserUtils {
     // When the user views one of these documents, it will be added to the sharing documents 'viewed' list field
     // The sharing document also stores the user's color value which helps distinguish shared documents from personal documents
     static setupSharedDocs(doc: Doc, sharingDocumentId: string) {
-        const dblClkScript = "{scriptContext.openLevel(documentView); addDocToList(scriptContext.props.treeView.props.Document, 'viewed', documentView.rootDoc);}";
+        const dblClkScript = "{scriptContext.openLevel(documentView); addDocToList(documentView.props.treeViewDoc, 'viewed', documentView.rootDoc);}";
         
         const sharedScripts = {  treeView_ChildDoubleClick: dblClkScript,  }
         const sharedDocOpts:DocumentOptions = {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index d6f5d63fe..0a3389fc2 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -728,7 +728,6 @@ export class MainView extends React.Component {
                         PanelHeight={this.leftMenuFlyoutHeight}
                         renderDepth={0}
                         isContentActive={returnTrue}
-                        scriptContext={CollectionDockingView.Instance?.props.Document}
                         focus={emptyFunction}
                         whenChildContentsActiveChanged={emptyFunction}
                         bringToFront={emptyFunction}
@@ -766,7 +765,6 @@ export class MainView extends React.Component {
                     childFilters={returnEmptyFilter}
                     childFiltersByRanges={returnEmptyFilter}
                     searchFilterDocs={returnEmptyDoclist}
-                    scriptContext={this}
                 />
             </div>
         );
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index d7537bffb..8417a6f5b 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -21,7 +21,6 @@ import { InkingStroke } from './InkingStroke';
 import { DocumentView, DocumentViewProps } from './nodes/DocumentView';
 import { FieldViewProps } from './nodes/FieldView';
 import { KeyValueBox } from './nodes/KeyValueBox';
-import { SliderBox } from './nodes/SliderBox';
 import './StyleProvider.scss';
 import React = require('react');
 import { PropertiesView } from './PropertiesView';
@@ -215,7 +214,6 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                 case DocumentType.SCREENSHOT:
                 case DocumentType.VID:         docColor = docColor || (Colors.LIGHT_GRAY); break;
                 case DocumentType.COL:
-                    if (StrCast(Doc.LayoutField(doc)).includes(SliderBox.name)) break;
                     docColor = docColor || (Doc.IsSystem(doc)
                             ? SettingsManager.userBackgroundColor
                             : doc.annotationOn
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index a69049b59..c7ad80f11 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -269,7 +269,6 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                 whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
                 addDocTab={this.props.addDocTab}
                 bringToFront={returnFalse}
-                scriptContext={this.props.scriptContext}
                 pinToPres={this.props.pinToPres}
             />
         );
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index ad3160a08..c4650647c 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -58,9 +58,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
     @observable static SelectingRegion: CollectionStackedTimeline | undefined = undefined;
     @observable public static CurrentlyPlaying: DocumentView[];
 
-    static RangeScript: ScriptField;
     static LabelScript: ScriptField;
-    static RangePlayScript: ScriptField;
     static LabelPlayScript: ScriptField;
 
     private _timeline: HTMLDivElement | null = null; // ref to actual timeline div
@@ -114,25 +112,6 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
         return this._zoomFactor;
     }
 
-    constructor(props: any) {
-        super(props);
-        // onClick play scripts
-        CollectionStackedTimeline.RangeScript =
-            CollectionStackedTimeline.RangeScript ||
-            ScriptField.MakeFunction(`scriptContext.clickAnchor(this, clientX)`, {
-                self: Doc.name,
-                scriptContext: 'any',
-                clientX: 'number',
-            })!;
-        CollectionStackedTimeline.RangePlayScript =
-            CollectionStackedTimeline.RangePlayScript ||
-            ScriptField.MakeFunction(`scriptContext.playOnClick(this, clientX)`, {
-                self: Doc.name,
-                scriptContext: 'any',
-                clientX: 'number',
-            })!;
-    }
-
     componentDidMount() {
         document.addEventListener('keydown', this.keyEvents, true);
     }
@@ -179,8 +158,19 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
         return Math.max(this.clipStart, Math.min(this.clipEnd, (screen_delta / width) * this.clipDuration + this.clipStart));
     };
 
-    rangeClickScript = () => CollectionStackedTimeline.RangeScript;
-    rangePlayScript = () => CollectionStackedTimeline.RangePlayScript;
+    @computed get rangeClick() {
+        // prettier-ignore
+        return ScriptField.MakeFunction('stackedTimeline.clickAnchor(this, clientX)',
+            { stackedTimeline: 'any', clientX: 'number' }, { stackedTimeline: this as any }
+        )!;
+    }
+    @computed get rangePlay() {
+        // prettier-ignore
+        return ScriptField.MakeFunction('stackedTimeline.playOnClick(this, clientX)', 
+            { stackedTimeline: 'any', clientX: 'number' }, { stackedTimeline: this as any })!;
+    }
+    rangeClickScript = () => this.rangeClick;
+    rangePlayScript = () => this.rangePlay;
 
     // handles key events for for creating key anchors, scrubbing, exiting trim
     @action
@@ -835,7 +825,6 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
                     hideResizeHandles={true}
                     bringToFront={emptyFunction}
                     contextMenuItems={this.contextMenuItems}
-                    scriptContext={this.props.stackedTimeline}
                 />
             ),
         };
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 36f8bc101..c43a9d2b8 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -361,7 +361,6 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                 whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
                 addDocTab={this.props.addDocTab}
                 bringToFront={returnFalse}
-                scriptContext={this.props.scriptContext}
                 pinToPres={this.props.pinToPres}
             />
         );
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index d2b61167e..f73c037f4 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -972,7 +972,6 @@ class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsP
                                 hideDocumentButtonBar={true}
                                 hideLinkAnchors={true}
                                 layout_fitWidth={returnTrue}
-                                scriptContext={this}
                             />
                         </div>
                     );
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 8ac9d6804..e1de2fa76 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -41,7 +41,6 @@ import { PhysicsSimulationBox } from './PhysicsBox/PhysicsSimulationBox';
 import { RecordingBox } from './RecordingBox';
 import { ScreenshotBox } from './ScreenshotBox';
 import { ScriptingBox } from './ScriptingBox';
-import { SliderBox } from './SliderBox';
 import { PresBox } from './trails/PresBox';
 import { VideoBox } from './VideoBox';
 import { WebBox } from './WebBox';
@@ -240,7 +239,6 @@ export class DocumentContentsView extends React.Component<
                     FontIconBox,
                     LabelBox,
                     EquationBox,
-                    SliderBox,
                     FieldView,
                     CollectionFreeFormView,
                     CollectionDockingView,
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index 14a3d16ef..d2e1293da 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -240,7 +240,6 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 text: typeof value === 'string' ? value.charAt(0).toUpperCase() + value.slice(1) : StrCast(DocCast(value)?.title),
                 val: value,
                 style: getStyle(value),
-                onClick: undoable(() => script.script.run({ this: this.layoutDoc, self: this.rootDoc, value }), value),
                 // shortcut: '#',
             }));
 
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 73a5be90a..cf44649a2 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -242,7 +242,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 title: StrCast(this.rootDoc.title + '@' + NumCast(this.layoutDoc._layout_scrollTop)?.toFixed(0)),
                 annotationOn: this.rootDoc,
             });
-        const visibleAnchor = this._pdfViewer?._getAnchor(this._pdfViewer.savedAnnotations(), true);
+        const visibleAnchor = this._pdfViewer?._getAnchor?.(this._pdfViewer.savedAnnotations(), true);
         const anchor = visibleAnchor ?? docAnchor();
         PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: true, pannable: true } }, this.rootDoc);
         anchor.text = ele?.textContent ?? '';
diff --git a/src/client/views/nodes/SliderBox.scss b/src/client/views/nodes/SliderBox.scss
deleted file mode 100644
index 4206a368d..000000000
--- a/src/client/views/nodes/SliderBox.scss
+++ /dev/null
@@ -1,19 +0,0 @@
-.sliderBox-outerDiv {
-    width: calc(100% - 14px);   // 14px accounts for handles that are at the max value of the slider that would extend outside the box
-    height: 100%;
-    border-radius: inherit;
-    display: flex;
-    flex-direction: column;
-    position: relative;
-    .slider-tracks {
-        top: 7px;
-        position: relative;
-    }
-    .slider-ticks {
-        position: relative;
-    }
-    .slider-handles {
-        top: 7px;
-        position: relative;
-    }
-}
\ No newline at end of file
diff --git a/src/client/views/nodes/SliderBox.tsx b/src/client/views/nodes/SliderBox.tsx
deleted file mode 100644
index 430b20eb5..000000000
--- a/src/client/views/nodes/SliderBox.tsx
+++ /dev/null
@@ -1,103 +0,0 @@
-import { runInAction } from 'mobx';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import { Handles, Rail, Slider, Ticks, Tracks } from 'react-compound-slider';
-import { NumCast, ScriptCast, StrCast } from '../../../fields/Types';
-import { ContextMenu } from '../ContextMenu';
-import { ContextMenuProps } from '../ContextMenuItem';
-import { ViewBoxBaseComponent } from '../DocComponent';
-import { ScriptBox } from '../ScriptBox';
-import { StyleProp } from '../StyleProvider';
-import { FieldView, FieldViewProps } from './FieldView';
-import { Handle, Tick, TooltipRail, Track } from './SliderBox-components';
-import './SliderBox.scss';
-
-@observer
-export class SliderBox extends ViewBoxBaseComponent<FieldViewProps>() {
-    public static LayoutString(fieldKey: string) {
-        return FieldView.LayoutString(SliderBox, fieldKey);
-    }
-
-    get minThumbKey() {
-        return this.fieldKey + '-minThumb';
-    }
-    get maxThumbKey() {
-        return this.fieldKey + '-maxThumb';
-    }
-    get minKey() {
-        return this.fieldKey + '-min';
-    }
-    get maxKey() {
-        return this.fieldKey + '-max';
-    }
-    specificContextMenu = (e: React.MouseEvent): void => {
-        const funcs: ContextMenuProps[] = [];
-        funcs.push({ description: 'Edit Thumb Change Script', icon: 'edit', event: (obj: any) => ScriptBox.EditButtonScript('On Thumb Change ...', this.props.Document, 'onThumbChange', obj.x, obj.y) });
-        ContextMenu.Instance.addItem({ description: 'Options...', subitems: funcs, icon: 'asterisk' });
-    };
-    onChange = (values: readonly number[]) =>
-        runInAction(() => {
-            this.dataDoc[this.minThumbKey] = values[0];
-            this.dataDoc[this.maxThumbKey] = values[1];
-            ScriptCast(this.layoutDoc.onThumbChanged, null)?.script.run({
-                self: this.rootDoc,
-                scriptContext: this.props.scriptContext,
-                range: values,
-                this: this.layoutDoc,
-            });
-        });
-
-    render() {
-        const domain = [NumCast(this.layoutDoc[this.minKey]), NumCast(this.layoutDoc[this.maxKey])];
-        const defaultValues = [NumCast(this.dataDoc[this.minThumbKey]), NumCast(this.dataDoc[this.maxThumbKey])];
-        return domain[1] <= domain[0] ? null : (
-            <div className="sliderBox-outerDiv" onContextMenu={this.specificContextMenu} onPointerDown={e => e.stopPropagation()} style={{ boxShadow: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BoxShadow) }}>
-                <div
-                    className="sliderBox-mainButton"
-                    onContextMenu={this.specificContextMenu}
-                    style={{
-                        background: StrCast(this.layoutDoc.backgroundColor),
-                        color: StrCast(this.layoutDoc.color, 'black'),
-                        fontSize: StrCast(this.layoutDoc._text_fontSize),
-                        letterSpacing: StrCast(this.layoutDoc.letterSpacing),
-                    }}>
-                    <Slider mode={2} step={Math.min(1, 0.1 * (domain[1] - domain[0]))} domain={domain} rootStyle={{ position: 'relative', width: '100%' }} onChange={this.onChange} values={defaultValues}>
-                        <Rail>{railProps => <TooltipRail {...railProps} />}</Rail>
-                        <Handles>
-                            {({ handles, activeHandleID, getHandleProps }) => (
-                                <div className="slider-handles">
-                                    {handles.map((handle, i) => {
-                                        const value = i === 0 ? defaultValues[0] : defaultValues[1];
-                                        return (
-                                            <div title={String(value)}>
-                                                <Handle key={handle.id} handle={handle} domain={domain} isActive={handle.id === activeHandleID} getHandleProps={getHandleProps} />
-                                            </div>
-                                        );
-                                    })}
-                                </div>
-                            )}
-                        </Handles>
-                        <Tracks left={false} right={false}>
-                            {({ tracks, getTrackProps }) => (
-                                <div className="slider-tracks">
-                                    {tracks.map(({ id, source, target }) => (
-                                        <Track key={id} source={source} target={target} disabled={false} getTrackProps={getTrackProps} />
-                                    ))}
-                                </div>
-                            )}
-                        </Tracks>
-                        <Ticks count={5}>
-                            {({ ticks }) => (
-                                <div className="slider-ticks">
-                                    {ticks.map(tick => (
-                                        <Tick key={tick.id} tick={tick} count={ticks.length} format={(val: number) => val.toString()} />
-                                    ))}
-                                </div>
-                            )}
-                        </Ticks>
-                    </Slider>
-                </div>
-            </div>
-        );
-    }
-}
diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss
index 75847c100..511c91da0 100644
--- a/src/client/views/nodes/WebBox.scss
+++ b/src/client/views/nodes/WebBox.scss
@@ -176,7 +176,6 @@
             width: 100%;
             height: 100%;
             transform-origin: top left;
-            overflow: auto;
 
             .webBox-iframe {
                 width: 100%;
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 27c19105f..d4ba7a48f 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -62,10 +62,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     private _searchRef = React.createRef<HTMLInputElement>();
     private _searchString = '';
     private _scrollTimer: any;
+    private _getAnchor: (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => Opt<Doc> = () => undefined;
 
-    private get _getAnchor() {
-        return AnchorMenu.Instance?.GetAnchor;
-    }
     @observable private _webUrl = ''; // url of the src parameter of the embedded iframe but not necessarily the rendered page - eg, when following a link, the rendered page changes but we don't want the src parameter to also change as that would cause an unnecessary re-render.
     @observable private _hackHide = false; // apparently changing the value of the 'sandbox' prop doesn't necessarily apply it to the active iframe.  so thisforces the ifrmae to be rebuilt when allowScripts is toggled
     @observable private _searching: boolean = false;
@@ -186,13 +184,15 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.props.setContentView?.(this); // this tells the DocumentView that this WebBox is the "content" of the document.  this allows the DocumentView to call WebBox relevant methods to configure the UI (eg, show back/forward buttons)
 
         runInAction(() => {
-            this._annotationKeySuffix = () => this._urlHash + '_annotations';
-            const reqdFuncs: { [key: string]: string } = {};
+            this._annotationKeySuffix = () => (this._urlHash ? this._urlHash + '_' : '') + 'annotations';
             // bcz: need to make sure that doc.data_annotations points to the currently active web page's annotations (this could/should be when the doc is created)
-            reqdFuncs[this.fieldKey + '_annotations'] = `copyField(this["${this.fieldKey}_"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"_annotations"])`;
-            reqdFuncs[this.fieldKey + '_annotations-setter'] = `this["${this.fieldKey}_"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"_annotations"] = value`;
-            reqdFuncs[this.fieldKey + '_sidebar'] = `copyField(this["${this.fieldKey}_"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"_sidebar"])`;
-            DocUtils.AssignScripts(this.dataDoc, {}, reqdFuncs);
+            if (this._url) {
+                const reqdFuncs: { [key: string]: string } = {};
+                reqdFuncs[this.fieldKey + '_annotations'] = `copyField(this["${this.fieldKey}_"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"annotations"])`;
+                reqdFuncs[this.fieldKey + '_annotations-setter'] = `this["${this.fieldKey}_"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"annotations"] = value`;
+                reqdFuncs[this.fieldKey + '_sidebar'] = `copyField(this["${this.fieldKey}_"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"sidebar"])`;
+                DocUtils.AssignScripts(this.dataDoc, {}, reqdFuncs);
+            }
         });
         this._disposers.urlchange = reaction(
             () => WebCast(this.rootDoc.data),
@@ -264,14 +264,16 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             const clientRects = selRange.getClientRects();
             for (let i = 0; i < clientRects.length; i++) {
                 const rect = clientRects.item(i);
+                const mainrect = this._url ? { translateX: 0, translateY: 0, scale: 1 } : Utils.GetScreenTransform(this._mainCont.current);
                 if (rect && rect.width !== this._mainCont.current.clientWidth) {
                     const annoBox = document.createElement('div');
                     annoBox.className = 'marqueeAnnotator-annotationBox';
+                    const scale = this._url ? 1 : this.props.ScreenToLocalTransform().Scale;
                     // transforms the positions from screen onto the pdf div
-                    annoBox.style.top = (rect.top + this._mainCont.current.scrollTop).toString();
-                    annoBox.style.left = rect.left.toString();
-                    annoBox.style.width = rect.width.toString();
-                    annoBox.style.height = rect.height.toString();
+                    annoBox.style.top = ((rect.top - mainrect.translateY) * scale + (this._url ? this._mainCont.current.scrollTop : NumCast(this.layoutDoc.layout_scrollTop))).toString();
+                    annoBox.style.left = ((rect.left - mainrect.translateX) * scale).toString();
+                    annoBox.style.width = (rect.width * scale).toString();
+                    annoBox.style.height = (rect.height * scale).toString();
                     this._annotationLayer.current && MarqueeAnnotator.previewNewAnnotation(this._savedAnnotations, this._annotationLayer.current, annoBox, 1);
                 }
             }
@@ -340,7 +342,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             });
         PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: pinProps?.pinData ? true : false, pannable: true } }, this.rootDoc);
         anchor.text = ele?.textContent ?? '';
-        anchor.text_html = ele?.innerHTML;
+        anchor.text_html = ele?.innerHTML ?? this._selectionText;
         addAsAnnotation && this.addDocumentWrapper(anchor);
         return anchor;
     };
@@ -362,12 +364,49 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 this._textAnnotationCreator = () => this.createTextAnnotation(sel, !sel.isCollapsed ? sel.getRangeAt(0) : undefined);
                 AnchorMenu.Instance.jumpTo(e.clientX * scale + mainContBounds.translateX, e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._layout_scrollTop) * scale);
                 // Changing which document to add the annotation to (the currently selected WebBox)
-                GPTPopup.Instance.setSidebarId(`${this.props.fieldKey}_${this._urlHash}_sidebar`);
+                GPTPopup.Instance.setSidebarId(`${this.props.fieldKey}_${this._urlHash ? this._urlHash + '_' : ''}sidebar`);
                 GPTPopup.Instance.addDoc = this.sidebarAddDocument;
             }
         }
     };
     @action
+    webClipDown = (e: React.PointerEvent) => {
+        const mainContBounds = Utils.GetScreenTransform(this._mainCont.current!);
+        const scale = (this.props.NativeDimScaling?.() || 1) * mainContBounds.scale;
+        const word = getWordAtPoint(e.target, e.clientX, e.clientY);
+        this._setPreviewCursor?.(e.clientX, e.clientY, false, true, this.rootDoc);
+        MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
+        e.button !== 2 && (this._marqueeing = [e.clientX, e.clientY]);
+        if (word || (e.target as any)?.className?.includes('rangeslider') || (e.target as any)?.onclick || (e.target as any)?.parentNode?.onclick) {
+            e.stopPropagation();
+            setTimeout(
+                action(() => (this._marqueeing = undefined)),
+                100
+            ); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it.
+        } else {
+            this._isAnnotating = true;
+            this.props.select(false);
+            e.stopPropagation();
+            e.preventDefault();
+        }
+        document.addEventListener('pointerup', this.webClipUp);
+    };
+    webClipUp = (e: PointerEvent) => {
+        document.removeEventListener('pointerup', this.webClipUp);
+        this._getAnchor = AnchorMenu.Instance?.GetAnchor; // need to save AnchorMenu's getAnchor since a subsequent selection on another doc will overwrite this value
+        const sel = window.getSelection();
+        if (sel && !sel.isCollapsed) {
+            const selRange = sel.getRangeAt(0);
+            this._selectionText = sel.toString();
+            AnchorMenu.Instance.setSelectedText(sel.toString());
+            this._textAnnotationCreator = () => this.createTextAnnotation(sel, selRange);
+            AnchorMenu.Instance.jumpTo(e.clientX, e.clientY);
+            // Changing which document to add the annotation to (the currently selected WebBox)
+            GPTPopup.Instance.setSidebarId(`${this.props.fieldKey}_${this._urlHash ? this._urlHash + '_' : ''}sidebar`);
+            GPTPopup.Instance.addDoc = this.sidebarAddDocument;
+        }
+    };
+    @action
     iframeDown = (e: PointerEvent) => {
         const mainContBounds = Utils.GetScreenTransform(this._mainCont.current!);
         const scale = (this.props.NativeDimScaling?.() || 1) * mainContBounds.scale;
@@ -396,9 +435,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             ContextMenu.Instance.setIgnoreEvents(true);
         }
     };
-    isFirefox = () => {
-        return 'InstallTrigger' in window; // navigator.userAgent.indexOf("Chrome") !== -1;
-    };
+    isFirefox = () => 'InstallTrigger' in window; // navigator.userAgent.indexOf("Chrome") !== -1;
     iframeClick = () => this._iframeClick;
     iframeScaling = () => 1 / this.props.ScreenToLocalTransform().Scale;
 
@@ -722,10 +759,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         }
     };
     @action finishMarquee = (x?: number, y?: number, e?: PointerEvent) => {
+        this._getAnchor = AnchorMenu.Instance?.GetAnchor;
         this._marqueeing = undefined;
         this._isAnnotating = false;
         this._iframeClick = undefined;
-        const sel = this._iframe?.contentDocument?.getSelection();
+        const sel = this._url ? this._iframe?.contentDocument?.getSelection() : window.document.getSelection();
         if (sel?.empty) sel.empty(); // Chrome
         else if (sel?.removeAllRanges) sel.removeAllRanges(); // Firefox
         if (x !== undefined && y !== undefined) {
@@ -750,7 +788,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         );
         const field = this.rootDoc[this.props.fieldKey];
         if (field instanceof HtmlField) {
-            return <span className="webBox-htmlSpan" contentEditable onPointerDown={e => e.stopPropagation()} dangerouslySetInnerHTML={{ __html: field.html }} />;
+            return <span className="webBox-htmlSpan" ref={r => r && (this._scrollHeight = Number(getComputedStyle(r).height.replace('px', '')))} contentEditable onPointerDown={this.webClipDown} dangerouslySetInnerHTML={{ __html: field.html }} />;
         }
         if (field instanceof WebField) {
             const url = this.layoutDoc[this.fieldKey + '_useCors'] ? Utils.CorsProxy(this._webUrl) : this._webUrl;
@@ -775,7 +813,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     }
 
     addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => {
-        (doc instanceof Doc ? [doc] : doc).forEach(doc => (doc.config_data = new WebField(this._url)));
+        this._url && (doc instanceof Doc ? [doc] : doc).forEach(doc => (doc.config_data = new WebField(this._url)));
         return this.addDocument(doc, annotationKey);
     };
 
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 2a3b232bd..383b400c8 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -465,7 +465,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const fkey = Doc.LayoutFieldKey(bestTarget);
             const setData = bestTargetView?.ComponentView?.setData;
             if (setData) setData(activeItem.config_data);
-            else Doc.GetProto(bestTarget)[fkey] = activeItem.config_data instanceof ObjectField ? activeItem.config_data[Copy]() : activeItem.config_data;
+            else {
+                const current = Doc.GetProto(bestTarget)[fkey];
+                Doc.GetProto(bestTarget)[fkey + '_' + Date.now()] = current instanceof ObjectField ? current[Copy]() : current;
+                Doc.GetProto(bestTarget)[fkey] = activeItem.config_data instanceof ObjectField ? activeItem.config_data[Copy]() : activeItem.config_data;
+            }
             bestTarget[fkey + '_usePath'] = activeItem.config_usePath;
             setTimeout(() => (bestTarget._dataTransition = undefined), transTime + 10);
         }
@@ -2650,7 +2654,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 removeDocument={returnFalse}
                                 dontRegisterView={true}
                                 focus={this.focusElement}
-                                scriptContext={this}
                                 ScreenToLocalTransform={this.getTransform}
                                 AddToMap={this.AddToMap}
                                 RemFromMap={this.RemFromMap}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 2a191477b..23dc084ad 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -82,9 +82,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
     private _ignoreScroll = false;
     private _initialScroll: { loc: Opt<number>; easeFunc: 'linear' | 'ease' | undefined } | undefined;
     private _forcedScroll = true;
-    get _getAnchor() {
-        return AnchorMenu.Instance?.GetAnchor;
-    }
+    _getAnchor: (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => Opt<Doc> = () => undefined;
 
     selectionText = () => this._selectionText;
     selectionContent = () => this._selectionContent;
@@ -400,6 +398,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
 
     @action
     finishMarquee = (x?: number, y?: number) => {
+        this._getAnchor = AnchorMenu.Instance?.GetAnchor;
         this.isAnnotating = false;
         this._marqueeing = undefined;
         this._textSelecting = true;
-- 
cgit v1.2.3-70-g09d2

