From d5f60e34e8ac9f99c0b442346db5386068af5de2 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 23 Mar 2023 10:06:39 -0400
Subject: changed showing keyvalue panes to not create a document, but to use
 the LayoutTemplateString.

---
 .../views/collections/CollectionDockingView.tsx      | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

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

diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 057c1e30f..e38905cbc 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -28,13 +28,12 @@ import React = require('react');
 import { OpenWhere, OpenWhereMod } from '../nodes/DocumentView';
 import { OverlayView } from '../OverlayView';
 import { ScriptingRepl } from '../ScriptingRepl';
-import { ScriptField } from '../../../fields/ScriptField';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 @observer
 export class CollectionDockingView extends CollectionSubView() {
     @observable public static Instance: CollectionDockingView | undefined;
-    public static makeDocumentConfig(document: Doc, panelName?: string, width?: number) {
+    public static makeDocumentConfig(document: Doc, panelName?: string, width?: number, keyValue?: boolean) {
         return {
             type: 'react-component',
             component: 'DocumentFrameRenderer',
@@ -42,6 +41,7 @@ export class CollectionDockingView extends CollectionSubView() {
             width: width,
             props: {
                 documentId: document[Id],
+                keyValue,
                 panelName, // name of tab that can be used to close or replace its contents
             },
         };
@@ -146,10 +146,10 @@ export class CollectionDockingView extends CollectionSubView() {
 
     @undoBatch
     @action
-    public static ReplaceTab(document: Doc, panelName: OpenWhereMod, stack: any, addToSplit?: boolean): boolean {
+    public static ReplaceTab(document: Doc, panelName: OpenWhereMod, stack: any, addToSplit?: boolean, keyValue?: boolean): boolean {
         const instance = CollectionDockingView.Instance;
         if (!instance) return false;
-        const newConfig = CollectionDockingView.makeDocumentConfig(document, panelName);
+        const newConfig = CollectionDockingView.makeDocumentConfig(document, panelName, undefined, keyValue);
         if (!panelName && stack) {
             const activeContentItemIndex = stack.contentItems.findIndex((item: any) => item.config === stack._activeContentItem.config);
             const newContentItem = stack.layoutManager.createContentItem(newConfig, instance._goldenLayout);
@@ -171,10 +171,10 @@ export class CollectionDockingView extends CollectionSubView() {
     }
 
     @undoBatch
-    public static ToggleSplit(doc: Doc, location: OpenWhereMod, stack?: any, panelName?: string) {
+    public static ToggleSplit(doc: Doc, location: OpenWhereMod, stack?: any, panelName?: string, keyValue?: boolean) {
         return CollectionDockingView.Instance && Array.from(CollectionDockingView.Instance.tabMap.keys()).findIndex(tab => tab.DashDoc === doc) !== -1
             ? CollectionDockingView.CloseSplit(doc)
-            : CollectionDockingView.AddSplit(doc, location, stack, panelName);
+            : CollectionDockingView.AddSplit(doc, location, stack, panelName, keyValue);
     }
 
     //
@@ -182,7 +182,7 @@ export class CollectionDockingView extends CollectionSubView() {
     //
     @undoBatch
     @action
-    public static AddSplit(document: Doc, pullSide: OpenWhereMod, stack?: any, panelName?: string) {
+    public static AddSplit(document: Doc, pullSide: OpenWhereMod, stack?: any, panelName?: string, keyValue?: boolean) {
         if (document?._viewType === CollectionViewType.Docking) return DashboardView.openDashboard(document);
         if (!CollectionDockingView.Instance) return false;
         const tab = Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === document);
@@ -193,7 +193,7 @@ export class CollectionDockingView extends CollectionSubView() {
         const instance = CollectionDockingView.Instance;
         const glayRoot = instance._goldenLayout.root;
         if (!instance) return false;
-        const docContentConfig = CollectionDockingView.makeDocumentConfig(document, panelName);
+        const docContentConfig = CollectionDockingView.makeDocumentConfig(document, panelName, undefined, keyValue);
 
         if (!pullSide && stack) {
             stack.addChild(docContentConfig, undefined);
@@ -601,6 +601,6 @@ ScriptingGlobals.add(
     'opens up document in screen overlay layer',
     '(doc: any)'
 );
-ScriptingGlobals.add(function useRightSplit(doc: any, shiftKey?: boolean) {
-    CollectionDockingView.ReplaceTab(doc, OpenWhereMod.right, undefined, shiftKey);
+ScriptingGlobals.add(function useRightSplit(doc: any, addToRightSplit?: boolean) {
+    CollectionDockingView.ReplaceTab(doc, OpenWhereMod.right, undefined, addToRightSplit);
 });
-- 
cgit v1.2.3-70-g09d2


From 44a6c5cabd35e8f7734d6f70128245ba5379d3c1 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 23 Mar 2023 16:17:19 -0400
Subject: fixed opening keyvalue for tabs and sidebar docs.  added more topbar
 context menu buttons for freeform views.

---
 src/client/util/CurrentUserUtils.ts                | 23 +++++++++++++-----
 src/client/views/MainView.tsx                      |  5 ++--
 .../views/collections/CollectionDockingView.tsx    |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  6 +++++
 src/client/views/nodes/button/FontIconBox.tsx      | 27 ++++++++++++++++++++++
 5 files changed, 54 insertions(+), 9 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 968c7a79b..814b7b072 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -46,6 +46,7 @@ interface Button {
     btnList?: List<string>;
     ignoreClick?: boolean;
     buttonText?: string;
+    backgroundColor?: string;
 
     // fields that do not correspond to DocumentOption fields
     scripts?: { script?: string; onClick?: string; onDoubleClick?: string }
@@ -608,11 +609,19 @@ export class CurrentUserUtils {
         return DocUtils.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), dockBtnsReqdOpts, btns);
     }
 
-    static zTools(): Button[] {
+    static freeTools(): Button[] {
         return [
-            { title: "Bottom",  icon: "arrows-down-to-line",  toolTip: "Make doc topmost",btnType: ButtonType.ClickButton,        expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode, true)'}, scripts: { onClick: 'sendToBack()'}}, // Only when floating document is selected in freeform
-            { title: "Top",     icon: "arrows-up-to-line",    toolTip: "Make doc bottommost",btnType: ButtonType.ClickButton,     expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode, true)'}, scripts: { onClick: 'bringToFront()'}}, // Only when floating document is selected in freeform
-            { title: "Z order", icon: "z",                    toolTip: "Bring Forward on Drag",btnType: ButtonType.ToggleButton,  expertMode: false, toolType:CollectionViewType.Freeform, funcs: {},                                                                                  scripts: { onClick: 'toggleRaiseOnDrag(_readOnly_)'}}, // Only when floating document is selected in freeform
+            { title: "Bottom",    icon: "arrows-down-to-line",toolTip: "Make doc topmost",    btnType: ButtonType.ClickButton,  expertMode: false,                        funcs: {}, scripts: { onClick: 'sendToBack()'}}, // Only when floating document is selected in freeform
+            { title: "Top",       icon: "arrows-up-to-line",  toolTip: "Make doc bottommost", btnType: ButtonType.ClickButton,  expertMode: false,                        funcs: {}, scripts: { onClick: 'bringToFront()'}}, // Only when floating document is selected in freeform
+        ]
+    }
+    static viewTools(): Button[] {
+        return [
+            { title: "Grid",         icon: "border-all",  toolTip: "Show Grid",           btnType: ButtonType.ToggleButton, expertMode: false, toolType:"grid",       funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform
+            { title: "Snap\xA0Lines",icon: "th",          toolTip: "Show Snap Lines",     btnType: ButtonType.ToggleButton, expertMode: false, toolType:"snap lines", funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform
+            { title: "View\xA0All",  icon: "object-group",toolTip: "Fit all Docs to View",btnType: ButtonType.ToggleButton, expertMode: false, toolType:"viewAll",    funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform
+            { title: "Clusters",     icon: "braille",     toolTip: "Show Doc Clusters",   btnType: ButtonType.ToggleButton, expertMode: false, toolType:"clusters",   funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform
+            { title: "Reset",        icon: "check",       toolTip: "Reset View",          btnType: ButtonType.ClickButton,  expertMode: false, backgroundColor:"transparent",    scripts: { onClick: 'resetView()'}}, // Only when floating document is selected in freeform
         ]
     }
     static textTools():Button[] {
@@ -677,12 +686,14 @@ export class CurrentUserUtils {
             { title: "Fill",    icon: "fill-drip",     toolTip: "Background Fill Color",btnType: ButtonType.ColorButton,         expertMode: false, toolType:"tab",                                                                                          ignoreClick: true, width: 20,  scripts: { script:  'return setBackgroundColor(value, _readOnly_)'}}, // Only when a document is selected
             { title: "Header",  icon: "heading",       toolTip: "Header Color",         btnType: ButtonType.ColorButton,         expertMode: false, toolType:"tab",                                                                                          ignoreClick: true,             scripts: { script:  'return setHeaderColor(value, _readOnly_)'}},
             { title: "Overlay", icon: "layer-group",   toolTip: "Overlay",              btnType: ButtonType.ToggleButton,        expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode, true)'},       scripts: { onClick: 'toggleOverlay(_readOnly_)'}}, // Only when floating document is selected in freeform
+            { title: "Z order", icon: "z",             toolTip: "Bring Forward on Drag",btnType: ButtonType.ToggleButton,        expertMode: false, toolType:CollectionViewType.Freeform, funcs: {},                                                                                        scripts: { onClick: 'toggleRaiseOnDrag(_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: 20,  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: 20,  scripts: { onClick: 'nextKeyFrame(_readOnly_)'}},
             { title: "Text",    icon: "Text",          toolTip: "Text functions",       subMenu: CurrentUserUtils.textTools(),   expertMode: false, toolType:DocumentType.RTF,            funcs: {                                                                             linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available
             { title: "Ink",     icon: "Ink",           toolTip: "Ink functions",        subMenu: CurrentUserUtils.inkTools(),    expertMode: false, toolType:DocumentType.INK,            funcs: {                                                                             linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`}, scripts: { onClick: 'setInkToolDefaults()'} }, // Always available
-            { title: "Z",       icon: "z",             toolTip: "Z order functions",    subMenu: CurrentUserUtils.zTools(),      expertMode: false, toolType:"tab",                       funcs: {                                                                             linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available
+            { 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)`, linearViewIsExpanded: `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)`, linearViewIsExpanded: `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)`, linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Web is selected
             { title: "Schema",  icon: "Schema",        toolTip: "Schema functions",     subMenu: CurrentUserUtils.schemaTools(), expertMode: false, toolType:CollectionViewType.Schema,   funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} } // Only when Schema is selected
         ];
@@ -692,7 +703,7 @@ export class CurrentUserUtils {
     static setupContextMenuButton(params:Button, btnDoc?:Doc) {
         const reqdOpts:DocumentOptions = {
             ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit,
-            backgroundColor: params.scripts?.onClick ? undefined: "transparent", ///  a bit hacky. if an onClick is specified, then assume a toggle uses onClick to get the backgroundColor (see below).  Otherwise, assume a transparent background
+            backgroundColor: params.backgroundColor ??"transparent", ///  a bit hacky. if an onClick is specified, then assume a toggle uses onClick to get the backgroundColor (see below).  Otherwise, assume a transparent background
             color: Colors.WHITE, system: true, dontUndo: true, 
             _nativeWidth: params.width ?? 30, _width: params.width ?? 30,
             _height: 30, _nativeHeight: 30, 
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index e755f204b..a30d139be 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -669,7 +669,8 @@ export class MainView extends React.Component {
     mainContainerXf = () => this.sidebarScreenToLocal().translate(-this.leftScreenOffsetOfMainDocView, 0);
     static addDocTabFunc = (doc: Doc, location: OpenWhere): boolean => {
         const whereFields = doc._viewType === CollectionViewType.Docking ? [OpenWhere.dashboard] : location.split(':');
-        const whereMods = whereFields.length > 1 ? (whereFields[1] as OpenWhereMod) : OpenWhereMod.none;
+        const keyValue = whereFields[1]?.includes('KeyValue');
+        const whereMods: OpenWhereMod = whereFields.length > 1 ? (whereFields[1].replace('KeyValue', '') as OpenWhereMod) : OpenWhereMod.none;
         if (doc.dockingConfig) return DashboardView.openDashboard(doc);
         // prettier-ignore
         switch (whereFields[0]) {
@@ -678,7 +679,7 @@ export class MainView extends React.Component {
             case OpenWhere.fullScreen: return CollectionDockingView.OpenFullScreen(doc);
             case OpenWhere.close:      return CollectionDockingView.CloseSplit(doc, whereMods);
             case OpenWhere.toggle:     return CollectionDockingView.ToggleSplit(doc, whereMods);
-            case OpenWhere.add:default:return CollectionDockingView.AddSplit(doc, whereMods);
+            case OpenWhere.add:default:return CollectionDockingView.AddSplit(doc, whereMods, undefined, undefined, keyValue);
         }
     };
 
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index e38905cbc..4d000542c 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -185,7 +185,7 @@ export class CollectionDockingView extends CollectionSubView() {
     public static AddSplit(document: Doc, pullSide: OpenWhereMod, stack?: any, panelName?: string, keyValue?: boolean) {
         if (document?._viewType === CollectionViewType.Docking) return DashboardView.openDashboard(document);
         if (!CollectionDockingView.Instance) return false;
-        const tab = Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === document);
+        const tab = Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === document && !keyValue);
         if (tab) {
             tab.header.parent.setActiveContentItem(tab.contentItem);
             return true;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index aea7a3ad9..c80bafe26 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -2286,3 +2286,9 @@ ScriptingGlobals.add(function bringToFront() {
 ScriptingGlobals.add(function sendToBack(doc: Doc) {
     SelectionManager.Views().forEach(view => view.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView.bringToFront(view.rootDoc, true));
 });
+ScriptingGlobals.add(function resetView() {
+    SelectionManager.Docs().forEach(doc => {
+        doc._panX = doc._panY = 0;
+        doc._viewScale = 1;
+    });
+});
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index cb962cad3..d9364e5b5 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -587,6 +587,33 @@ ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) {
     selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log('[FontIconBox.tsx] toggleOverlay failed');
 });
 
+ScriptingGlobals.add(function showFreeform(attr: 'grid' | 'snap lines' | 'clusters' | 'viewAll', checkResult?: boolean) {
+    const selected = SelectionManager.Docs().lastElement();
+    // prettier-ignore
+    const map: Map<'grid' | 'snap lines' | 'clusters' | 'viewAll', { checkResult: (doc:Doc) => any; setDoc: (doc:Doc) => void;}> = new Map([
+        ['grid', {
+            checkResult: (doc:Doc) => doc._backgroundGridShow,
+            setDoc: (doc:Doc) => doc._backgroundGridShow = !doc._backgroundGridShow,
+        }],
+        ['snap lines', {
+            checkResult: (doc:Doc) => doc.showSnapLines,
+            setDoc: (doc:Doc) => doc._showSnapLines = !doc._showSnapLines,
+        }],
+        ['viewAll', {
+            checkResult: (doc:Doc) => doc._fitContentsToBox,
+            setDoc: (doc:Doc) => doc._fitContentsToBox = !doc._fitContentsToBox,
+        }],
+        ['clusters', {
+            checkResult: (doc:Doc) => doc._useClusters,
+            setDoc: (doc:Doc) => doc._useClusters = !doc._useClusters,
+        }],
+    ]);
+
+    if (checkResult) {
+        return map.get(attr)?.checkResult(selected) ? Colors.MEDIUM_BLUE : 'transparent';
+    }
+    SelectionManager.Docs().map(dv => map.get(attr)?.setDoc(dv));
+});
 ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highlight' | 'fontSize', value: any, checkResult?: boolean) {
     const editorView = RichTextMenu.Instance?.TextView?.EditorView;
     const selected = SelectionManager.Docs().lastElement();
-- 
cgit v1.2.3-70-g09d2


From dbd531233ea7c10130976127361f84281026a308 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 8 May 2023 12:27:55 -0400
Subject: more fixes for tags - removed #<tag> fields.  just use 'tags' now.

---
 src/client/views/FilterPanel.tsx                                 | 2 +-
 src/client/views/PropertiesView.tsx                              | 6 ++----
 src/client/views/collections/CollectionDockingView.tsx           | 1 -
 src/client/views/collections/CollectionTimeView.tsx              | 2 +-
 .../collections/collectionFreeForm/CollectionFreeFormView.tsx    | 2 +-
 src/client/views/nodes/CollectionFreeFormDocumentView.tsx        | 1 +
 src/client/views/nodes/formattedText/DashDocView.tsx             | 3 +--
 src/client/views/nodes/formattedText/DashFieldView.tsx           | 8 ++++++--
 src/client/views/nodes/formattedText/EquationView.tsx            | 1 -
 src/client/views/nodes/formattedText/FormattedTextBox.tsx        | 9 ++-------
 src/client/views/nodes/formattedText/RichTextRules.ts            | 2 +-
 src/client/views/nodes/formattedText/SummaryView.tsx             | 1 -
 src/fields/Doc.ts                                                | 3 +--
 13 files changed, 17 insertions(+), 24 deletions(-)

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

diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
index d17a4ea25..84b2e1407 100644
--- a/src/client/views/FilterPanel.tsx
+++ b/src/client/views/FilterPanel.tsx
@@ -53,7 +53,7 @@ export class FilterPanel extends React.Component<filterProps> {
         this.allDocs.forEach(doc => SearchBox.documentKeys(doc).filter(key => keys.add(key)));
         const sortedKeys = Array.from(keys.keys())
             .filter(key => key[0])
-            .filter(key => key[0] === '#' || key.indexOf('lastModified') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('_')) || noviceFields.includes(key) || !Doc.noviceMode)
+            .filter(key => key.indexOf('lastModified') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('_')) || noviceFields.includes(key) || !Doc.noviceMode)
             .sort();
         noviceFields.forEach(key => sortedKeys.splice(sortedKeys.indexOf(key), 1));
         return [...noviceFields, ...sortedKeys];
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index fff8390b3..0b14c7b10 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -164,7 +164,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 const multiple = Array.from(docs.reduce((set,doc) => set.add(doc[key]), new Set<FieldResult>()).keys()).length > 1;
                 const editableContents = multiple ? '-multiple-' : Field.toKeyValueString(docs[0], key);
                 const displayContents = multiple ? '-multiple-' : Field.toString(docs[0][key] as Field);
-                const contentElement = key[0] === '#'  ? <></> : (
+                const contentElement = (
                     <EditableView
                         key="editableView"
                         contents={displayContents}
@@ -200,7 +200,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
     @computed get noviceFields() {
         const noviceReqFields = ['author', 'creationDate', 'tags', '_curPage'];
-        return this.editableFields(key => key[0] === '#' || key.indexOf('lastModified') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('acl')), noviceReqFields);
+        return this.editableFields(key => key.indexOf('lastModified') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('acl')), noviceReqFields);
     }
 
     @undoBatch
@@ -217,8 +217,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 }
                 return true;
             } else if (value[0] === '#') {
-                const newVal = value + `:'${value}'`;
-                doc[DataSym][value] = value;
                 const tags = StrListCast(doc.tags);
                 if (!tags.includes(value)) {
                     tags.push(value);
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 4d000542c..bb1f788d4 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -481,7 +481,6 @@ export class CollectionDockingView extends CollectionSubView() {
             Doc.RemoveDocFromList(dview, fieldKey, tab.DashDoc);
             this.tabMap.delete(tab);
             tab._disposers && Object.values(tab._disposers).forEach((disposer: any) => disposer?.());
-            //tab.reactComponents?.forEach((ele: any) => ReactDOM.unmountComponentAtNode(ele));
             this.stateChanged();
         }
     };
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index c3f77205a..3cdb460a3 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -202,7 +202,7 @@ export class CollectionTimeView extends CollectionSubView() {
         this.childLayoutPairs.map(pair =>
             this._allFacets
                 .filter(fieldKey => pair.layout[fieldKey] instanceof RichTextField || typeof pair.layout[fieldKey] === 'number' || typeof pair.layout[fieldKey] === 'boolean' || typeof pair.layout[fieldKey] === 'string')
-                .filter(fieldKey => fieldKey[0] !== '_' && (fieldKey[0] !== '#' || fieldKey === '#') && (fieldKey === 'tags' || fieldKey[0] === toUpper(fieldKey)[0]))
+                .filter(fieldKey => fieldKey[0] !== '_' && (fieldKey === 'tags' || fieldKey[0] === toUpper(fieldKey)[0]))
                 .map(fieldKey => keySet.add(fieldKey))
         );
         Array.from(keySet).map(fieldKey => docItems.push({ description: ':' + fieldKey, event: () => (this.layoutDoc._pivotField = fieldKey), icon: 'compress-arrows-alt' }));
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 25da868e0..91c5144d8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1375,7 +1375,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             rotation: Cast(_rotation, 'number'),
             color: Cast(color, 'string') ? StrCast(color) : this.props.styleProvider?.(childDoc, this.props, StyleProp.Color),
             backgroundColor: Cast(backgroundColor, 'string') ? StrCast(backgroundColor) : this.getClusterColor(childDoc, this.props, StyleProp.BackgroundColor),
-            opacity: this._keyframeEditing ? 1 : Cast(opacity, 'number') ?? this.props.styleProvider?.(childDoc, this.props, StyleProp.Opacity),
+            opacity: !_width ? 0 : this._keyframeEditing ? 1 : Cast(opacity, 'number') ?? this.props.styleProvider?.(childDoc, this.props, StyleProp.Opacity),
             zIndex: Cast(zIndex, 'number'),
             width: _width,
             height: _height,
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index b68bcc263..a8c8a0d2e 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -214,6 +214,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                     transformOrigin: '50% 50%',
                     transition: this.dataProvider?.transition ?? (this.props.dataTransition ? this.props.dataTransition : this.dataProvider ? this.dataProvider.transition : StrCast(this.layoutDoc.dataTransition)),
                     zIndex: this.ZInd,
+                    display: this.sizeProvider?.width ? undefined : 'none',
                     pointerEvents: isInk ? 'none' : undefined,
                 }}>
                 {this.props.renderCutoffProvider(this.props.Document) ? (
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 648c579d0..c05a30d1a 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -45,8 +45,7 @@ export class DashDocView {
         );
     }
     destroy() {
-        this.root.unmount();
-        // ReactDOM.unmountComponentAtNode(this.dom);
+        setTimeout(this.root.unmount);
     }
     selectNode() {}
 }
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 72e8aedac..bf6fa2ec6 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -64,7 +64,11 @@ export class DashFieldView {
         );
     }
     destroy() {
-        this.root.unmount();
+        setTimeout(() => {
+            try {
+                this.root.unmount();
+            } catch {}
+        });
     }
     deselectNode() {
         this.dom.classList.remove('ProseMirror-selectednode');
@@ -241,7 +245,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
             }
             list.map(c => c.heading).indexOf(this._fieldKey) === -1 && list.push(new SchemaHeaderField(this._fieldKey, '#f1efeb'));
             list.map(c => c.heading).indexOf('text') === -1 && list.push(new SchemaHeaderField('text', '#f1efeb'));
-            alias._pivotField = this._fieldKey.startsWith('#') ? '#' : this._fieldKey;
+            alias._pivotField = this._fieldKey.startsWith('#') ? 'tags' : this._fieldKey;
             this.props.tbox.props.addDocTab(alias, OpenWhere.addRight);
         }
     };
diff --git a/src/client/views/nodes/formattedText/EquationView.tsx b/src/client/views/nodes/formattedText/EquationView.tsx
index 714ae458c..5e62d94c2 100644
--- a/src/client/views/nodes/formattedText/EquationView.tsx
+++ b/src/client/views/nodes/formattedText/EquationView.tsx
@@ -33,7 +33,6 @@ export class EquationView {
     setEditor = (editor?: EquationEditor) => (this._editor = editor);
     destroy() {
         this.root.unmount();
-        // ReactDOM.unmountComponentAtNode(this.dom);
     }
     setSelection() {
         this._editor?.mathField.focus();
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 1d668d6a9..8a14b18b2 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -12,7 +12,7 @@ import { Fragment, Mark, Node, Slice } from 'prosemirror-model';
 import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from 'prosemirror-state';
 import { EditorView } from 'prosemirror-view';
 import { DateField } from '../../../../fields/DateField';
-import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, CssSym, Doc, DocListCast, Field, ForceServerWrite, HeightSym, Opt, UpdatingFromServer, WidthSym } from '../../../../fields/Doc';
+import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, CssSym, Doc, DocListCast, Field, ForceServerWrite, HeightSym, Opt, StrListCast, UpdatingFromServer, WidthSym } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
 import { InkTool } from '../../../../fields/InkField';
 import { PrefetchProxy } from '../../../../fields/Proxy';
@@ -314,12 +314,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                         accumTags.push(node.attrs.fieldKey);
                     }
                 });
-                const curTags = Object.keys(dataDoc).filter(key => key.startsWith('#'));
-                const added = accumTags.filter(tag => !curTags.includes(tag));
-                const removed = curTags.filter(tag => !accumTags.includes(tag));
-                removed.forEach(r => (dataDoc[r] = undefined));
-                added.forEach(a => (dataDoc[a] = a));
-                dataDoc.tags = curTags.length ? new List<string>(curTags) : undefined;
+                dataDoc.tags = accumTags.length ? new List<string>(Array.from(new Set<string>(accumTags))) : undefined;
 
                 let unchanged = true;
                 if (this._applyingChange !== this.fieldKey && removeSelection(json) !== removeSelection(curProto?.Data)) {
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 68b209332..fb929d20b 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -325,7 +325,7 @@ export class RichTextRules {
             new InputRule(new RegExp(/#([a-zA-Z_\-]+[a-zA-Z_\-0-9]*)\s$/), (state, match, start, end) => {
                 const tag = match[1];
                 if (!tag) return state.tr;
-                this.Document[DataSym]['#' + tag] = '#' + tag;
+                //this.Document[DataSym]['#' + tag] = '#' + tag;
                 const tags = StrListCast(this.Document[DataSym].tags);
                 if (!tags.includes(tag)) {
                     tags.push(tag);
diff --git a/src/client/views/nodes/formattedText/SummaryView.tsx b/src/client/views/nodes/formattedText/SummaryView.tsx
index 4e75d374c..3355e4529 100644
--- a/src/client/views/nodes/formattedText/SummaryView.tsx
+++ b/src/client/views/nodes/formattedText/SummaryView.tsx
@@ -43,7 +43,6 @@ export class SummaryView {
     className = (visible: boolean) => 'formattedTextBox-summarizer' + (visible ? '' : '-collapsed');
     destroy() {
         this.root.unmount();
-        // ReactDOM.unmountComponentAtNode(this.dom);
     }
     selectNode() {}
 
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 03ae91c50..b533bd10c 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1387,8 +1387,7 @@ export namespace Doc {
         if (typeof value === 'string') {
             value = value.replace(`,${Utils.noRecursionHack}`, '');
         }
-        const tagsString = doc.tags ? ':' + StrListCast(doc.tags).join(':') + ':' : '';
-        const fieldVal = key === '#' ? (tagsString.includes(':#' + value + ':') ? tagsString : undefined) : doc[key];
+        const fieldVal = doc[key];
         if (Cast(fieldVal, listSpec('string'), []).length) {
             const vals = Cast(fieldVal, listSpec('string'), []);
             const docs = vals.some(v => (v as any) instanceof Doc);
-- 
cgit v1.2.3-70-g09d2

