From 705975eb43e7904c62e7e847478f6d0dac60d443 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sun, 23 Mar 2025 18:14:31 -0400
Subject: more _props.Document  to .Document refactoring.  type updates to
 prosemirrortransfer

---
 src/client/views/collections/collectionSchema/SchemaCellField.tsx | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

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

diff --git a/src/client/views/collections/collectionSchema/SchemaCellField.tsx b/src/client/views/collections/collectionSchema/SchemaCellField.tsx
index e89822b4c..daffdf1f5 100644
--- a/src/client/views/collections/collectionSchema/SchemaCellField.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaCellField.tsx
@@ -21,11 +21,11 @@ import DOMPurify from 'dompurify';
  */
 
 export interface SchemaCellFieldProps {
+    Doc: Doc;
     contents: FieldType | undefined;
     fieldContents?: FieldViewProps;
     editing?: boolean;
     oneLine?: boolean;
-    Document: Doc;
     fieldKey: string;
     // eslint-disable-next-line no-use-before-define
     refSelectModeInfo: { enabled: boolean; currEditing: SchemaCellField | undefined };
@@ -55,7 +55,7 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
         }); //must be moved to end of batch or else other docs aren't loaded, so render as d-1 in function
     }
 
-    get docIndex(){return DocumentView.getDocViewIndex(this._props.Document);} // prettier-ignore
+    get docIndex(){return DocumentView.getDocViewIndex(this._props.Doc);} // prettier-ignore
 
     get selfRefPattern() {
         return `d${this.docIndex}.${this._props.fieldKey}`;
-- 
cgit v1.2.3-70-g09d2


From 730d568fb4cb37c5d673517179e8e5dc532ad9c8 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 26 Mar 2025 23:19:28 -0400
Subject: fixed keyValue box assignments so that '_' assigns to template unless
 value starts with '=' (then root doc). fixed deleting template keys in
 keyValuePair. fixed schemaTableCell to show current editable value (not last
 edited value) when editing layout and data doc values.  updated
 DocumentOptions API.

---
 src/client/documents/DocUtils.ts                   |  8 +++----
 src/client/documents/Documents.ts                  | 23 ++++++++++--------
 src/client/util/CurrentUserUtils.ts                |  4 ++--
 src/client/views/EditableView.tsx                  | 22 ++++++++++--------
 .../CollectionMulticolumnView.tsx                  |  3 ---
 .../collectionSchema/SchemaCellField.tsx           |  6 ++---
 .../collectionSchema/SchemaTableCell.tsx           |  3 +--
 src/client/views/nodes/KeyValueBox.tsx             | 17 ++++++++++----
 src/client/views/nodes/KeyValuePair.tsx            | 27 +++++++---------------
 src/fields/Doc.ts                                  |  8 ++++---
 10 files changed, 60 insertions(+), 61 deletions(-)

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

diff --git a/src/client/documents/DocUtils.ts b/src/client/documents/DocUtils.ts
index 0bb6c0ce9..69bf4815e 100644
--- a/src/client/documents/DocUtils.ts
+++ b/src/client/documents/DocUtils.ts
@@ -283,10 +283,10 @@ export namespace DocUtils {
                 }
                 return val1 === val2;
             };
-            Object.entries(reqdOpts).forEach(pair => {
-                const targetDoc = pair[0].startsWith('_') ? doc : Doc.GetProto(doc as Doc);
-                if (!Object.getOwnPropertyNames(targetDoc).includes(pair[0].replace(/^_/, '')) || !compareValues(pair[1], targetDoc[pair[0]])) {
-                    targetDoc[pair[0]] = pair[1];
+            Object.entries(reqdOpts).forEach(([key, val]) => {
+                const targetDoc = key.startsWith('_') ? doc : Doc.GetProto(doc as Doc);
+                if (!Object.getOwnPropertyNames(targetDoc).includes(key.replace(/^_/, '')) || !compareValues(val, targetDoc[key])) {
+                    targetDoc[key] = val as FieldType;
                 }
             });
             items?.forEach(item => !DocListCast(doc.data).includes(item) && Doc.AddDocToList(Doc.GetProto(doc), 'data', item));
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 668725d2b..972b1aa5c 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -5,6 +5,7 @@ import { ClientUtils, OmitKeys } from '../../ClientUtils';
 import { DateField } from '../../fields/DateField';
 import { CreateLinkToActiveAudio, Doc, FieldType, Opt, updateCachedAcls } from '../../fields/Doc';
 import { Initializing } from '../../fields/DocSymbols';
+import { Id } from '../../fields/FieldSymbols';
 import { HtmlField } from '../../fields/HtmlField';
 import { InkField } from '../../fields/InkField';
 import { List } from '../../fields/List';
@@ -18,7 +19,6 @@ import { PointData } from '../../pen-gestures/GestureTypes';
 import { DocServer } from '../DocServer';
 import { dropActionType } from '../util/DropActionTypes';
 import { CollectionViewType, DocumentType } from './DocumentTypes';
-import { Id } from '../../fields/FieldSymbols';
 
 class EmptyBox {
     public static LayoutString() {
@@ -43,7 +43,6 @@ export class FInfo {
     readOnly: boolean = false;
     fieldType?: FInfoFieldType;
     values?: FieldType[];
-    onLayout?: boolean;
     filterable?: boolean = true; // can be used as a Filter in FilterPanel
     // format?: string; // format to display values (e.g, decimal places, $, etc)
     // parse?: ScriptField; // parse a value from a string
@@ -156,12 +155,16 @@ type DROPt = DAInfo | dropActionType;
 type DATEt = DateInfo | number;
 type DTYPEt = DTypeInfo | string;
 export class DocumentOptions {
+    [key: string]: FInfo | FieldType | undefined;
     // coordinate and dimensions depending on view
     x?: NUMt = new NumInfo('horizontal coordinate in freeform view', false);
     y?: NUMt = new NumInfo('vertical coordinate in freeform view', false);
     z?: NUMt = new NumInfo('whether document is in overlay (1) or not (0)', false, false, [1, 0]);
+    zIndex?: NUMt = new NumInfo('stacking index of documents in freeform view (higher numbers are towards the top');
     overlayX?: NUMt = new NumInfo('horizontal coordinate in overlay view', false);
     overlayY?: NUMt = new NumInfo('vertical coordinate in overlay view', false);
+    embedContainer?: DOCt = new DocInfo('document that displays (contains) this document', false);
+
     text?: RTFt = new RtfInfo('plain or rich text', true);
     text_html?: STRt = new StrInfo('plain text or html', true);
     _dimMagnitude?: NUMt = new NumInfo("magnitude of collectionMulti{row,col} element's width or height", false);
@@ -215,7 +218,6 @@ export class DocumentOptions {
     author?: string; // STRt = new StrInfo('creator of document'); // bcz: don't change this. Otherwise, the userDoc's field Infos will have a FieldInfo assigned to its author field which will render it unreadable
     author_date?: DATEt = new DateInfo('date the document was created', true);
     annotationOn?: DOCt = new DocInfo('document annotated by this document', false);
-    _embedContainer?: DOCt = new DocInfo('document that displays (contains) this document', false);
     rootDocument?: DOCt = new DocInfo('document that supplies the information needed for a rendering template (eg, pres slide for PresElement)');
     color?: STRt = new StrInfo('foreground color data doc', false);
     hidden?: BOOLt = new BoolInfo('whether the document is not rendered by its collection', false);
@@ -686,10 +688,11 @@ export namespace Docs {
                 layout: layout.view?.LayoutString(layout.dataField),
             };
             Object.entries(options)
-                .filter(pair => typeof pair[1] === 'string' && pair[1].startsWith('@'))
-                .forEach(pair => {
-                    if (!existing || ScriptCast(existing[pair[0]])?.script.originalScript !== pair[1].substring(1)) {
-                        (options as { [key: string]: unknown })[pair[0]] = ComputedField.MakeFunction(pair[1].substring(1));
+                .filter(([, val]) => (val as string)?.startsWith?.('@'))
+                .map(([key, val]) => [key, val as string])
+                .forEach(([key, val]) => {
+                    if (!existing || ScriptCast(existing[key])?.script.originalScript !== val.substring(1)) {
+                        options[key] = ComputedField.MakeFunction(val.substring(1));
                     }
                 });
             return Doc.assign(existing ?? new Doc(prototypeId, true), OmitKeys(options, Object.keys(existing ?? {})).omit as { [key: string]: FieldType }, undefined, true);
@@ -722,7 +725,7 @@ export namespace Docs {
          */
         function InstanceFromProto(proto: Doc, data: FieldType | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = 'data', protoId?: string, placeholderDocIn?: Doc, noView?: boolean) {
             const placeholderDoc = placeholderDocIn;
-            const viewKeys = ['x', 'y', 'isSystem']; // keys that should be addded to the view document even though they don't begin with an "_"
+            const viewKeys = ['x', 'y', 'isSystem', 'overlayX', 'overlayY', 'zIndex', 'embedContainer']; // keys that should be addded to the view document even though they don't begin with an "_"
             const { omit: dataProps, extract: viewProps } = OmitKeys(options, viewKeys, '^_') as { omit: { [key: string]: FieldType | undefined }; extract: { [key: string]: FieldType | undefined } };
 
             // dataProps.acl_Override = SharingPermissions.Unset;
@@ -947,9 +950,9 @@ export namespace Docs {
 
             return InstanceFromProto(
                 Prototypes.get(DocumentType.JOURNAL),
-                "",
+                '',
                 {
-                    title: "",
+                    title: '',
                     ...options,
                 },
                 undefined,
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 537c703b4..44ed136f6 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -601,7 +601,7 @@ pie title Minerals in my tap water
         const myFilesystem = DocCast(doc[field]);
         
         const newFolderOpts: DocumentOptions = {
-            _forceActive: true, _dragOnlyWithinContainer: true, _embedContainer: Doc.MyFilesystem, _width: 30, _height: 30, undoIgnoreFields:new List<string>(['treeView_SortCriterion']),
+            _forceActive: true, _dragOnlyWithinContainer: true, embedContainer: Doc.MyFilesystem, _width: 30, _height: 30, undoIgnoreFields:new List<string>(['treeView_SortCriterion']),
             title: "New folder", color: Colors.BLACK, btnType: ButtonType.ClickButton, toolTip: "Create new folder", buttonText: "New folder", icon: "folder-plus", isSystem: true
         };
         const newFolderScript = { onClick: CollectionTreeView.AddTreeFunc};
@@ -867,7 +867,7 @@ pie title Minerals in my tap water
             _height: 30, _nativeHeight: 30, linearBtnWidth: params.linearBtnWidth,
             toolType: params.toolType, expertMode: params.expertMode,
             _dragOnlyWithinContainer: true, _lockedPosition: true,
-            _embedContainer: btnContainer
+            embedContainer: btnContainer
         };
         const reqdFuncs:{[key:string]:string} = {
             ...params.funcs,
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index e2490cec8..d9447b7ec 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -287,8 +287,8 @@ export class EditableView extends ObservableReactComponent<EditableProps> {
 
     staticDisplay = () => {
         let toDisplay;
-        const gval = this._props.GetValue()?.replace(/\n/g, '\\r\\n');
         if (this._props.inputString) {
+            const gval = this._props.GetValue()?.replace(/\n/g, '\\r\\n');
             toDisplay = (
                 <input
                     className="editableView-input"
@@ -315,15 +315,17 @@ export class EditableView extends ObservableReactComponent<EditableProps> {
     };
 
     render() {
-        const gval = this._props.GetValue()?.replace(/\n/g, '\\r\\n');
-        if (this._editing && gval !== undefined) {
-            return this._props.sizeToContent ? (
-                <div style={{ display: 'grid', minWidth: 100 }}>
-                    <div style={{ display: 'inline-block', position: 'relative', height: 0, width: '100%', overflow: 'hidden' }}>{this.renderEditor()}</div>
-                </div>
-            ) : (
-                this.renderEditor()
-            );
+        if (this._editing) {
+            const gval = this._props.GetValue()?.replace(/\n/g, '\\r\\n');
+            if (gval !== undefined) {
+                return this._props.sizeToContent ? (
+                    <div style={{ display: 'grid', minWidth: 100 }}>
+                        <div style={{ display: 'inline-block', position: 'relative', height: 0, width: '100%', overflow: 'hidden' }}>{this.renderEditor()}</div>
+                    </div>
+                ) : (
+                    this.renderEditor()
+                );
+            }
         }
         setTimeout(() => this._props.autosuggestProps?.resetValue());
         return (
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 58582a142..f27398427 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -202,7 +202,6 @@ export class CollectionMulticolumnView extends CollectionSubView() {
             return Transform.Identity(); // we're still waiting on promises to resolve
         }
         let offset = 0;
-        // eslint-disable-next-line no-restricted-syntax
         for (const { layout: candidate } of this.childLayoutPairs) {
             if (candidate === layout) {
                 return this.ScreenToLocalBoxXf().translate(-offset / (this._props.NativeDimScaling?.() || 1), 0);
@@ -316,7 +315,6 @@ export class CollectionMulticolumnView extends CollectionSubView() {
         const collector: JSX.Element[] = [];
         this.childLayouts.forEach((layout, i) => {
             collector.push(
-                // eslint-disable-next-line react/no-array-index-key
                 <Tooltip title={'Doc: ' + StrCast(layout.title)} key={'wrapper' + i}>
                     <div className="document-wrapper" style={{ flexDirection: 'column', width: this.lookupPixels(layout) }}>
                         {this.getDisplayDoc(layout)}
@@ -328,7 +326,6 @@ export class CollectionMulticolumnView extends CollectionSubView() {
                 </Tooltip>,
                 <ResizeBar
                     width={resizerWidth}
-                    // eslint-disable-next-line react/no-array-index-key
                     key={'resizer' + i}
                     styleProvider={this._props.styleProvider}
                     isContentActive={this._props.isContentActive}
diff --git a/src/client/views/collections/collectionSchema/SchemaCellField.tsx b/src/client/views/collections/collectionSchema/SchemaCellField.tsx
index daffdf1f5..1dff76df2 100644
--- a/src/client/views/collections/collectionSchema/SchemaCellField.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaCellField.tsx
@@ -96,10 +96,10 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
             { fireImmediately: true }
         );
         this._disposers.fieldUpdate = reaction(
-            () => this._props.GetValue(),
-            fieldVal => {
+            () => ({ fieldVal: this._props.GetValue(), editing: 0 }), // this._editing }),
+            ({ fieldVal, editing }) => {
                 this._unrenderedContent = fieldVal ?? '';
-                this.finalizeEdit(false, false, false);
+                !this._editing && this.finalizeEdit(false, false, false);
             }
         );
     }
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index e6fe46638..aeceedc30 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -211,8 +211,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
                             this._props.finishEdit?.();
                             return true;
                         }
-                        const hasNoLayout = Doc.IsDataProto(fieldProps.Document) ? true : undefined; // the "delegate" is a a data document so never write to it's proto
-                        const ret = Doc.SetField(fieldProps.Document, this._props.fieldKey.replace(/^_/, ''), value, hasNoLayout);
+                        const ret = Doc.SetField(fieldProps.Document, this._props.fieldKey, value);
                         this._submittedValue = value;
                         this._props.finishEdit?.();
                         return ret;
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 8911fac6d..b54004697 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -9,7 +9,7 @@ import { ComputedField, ScriptField } from '../../../fields/ScriptField';
 import { DocCast } from '../../../fields/Types';
 import { ImageField } from '../../../fields/URLField';
 import { DocumentType } from '../../documents/DocumentTypes';
-import { Docs } from '../../documents/Documents';
+import { Docs, DocumentOptions } from '../../documents/Documents';
 import { SetupDrag } from '../../util/DragManager';
 import { CompiledScript } from '../../util/Scripting';
 import { undoable } from '../../util/UndoManager';
@@ -121,10 +121,13 @@ export class KeyValueBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return false;
     };
 
-    public static SetField = undoable((doc: Doc, key: string, value: string, forceOnDelegate?: boolean, setResult?: (value: FieldResult) => void) => {
+    public static SetField = undoable((doc: Doc, key: string, value: string, forceOnDelegateIn?: boolean, setResult?: (value: FieldResult) => void) => {
         const script = KeyValueBox.CompileKVPScript(value);
         if (!script) return false;
-        return KeyValueBox.ApplyKVPScript(doc, key, script, forceOnDelegate, setResult);
+        const ldoc = key.startsWith('_') ? Doc.Layout(doc) : doc;
+        const forceOnDelegate = forceOnDelegateIn || (ldoc !== doc && !value.startsWith('='));
+        const setKey = value.startsWith('=') ? key.replace(/^_/, '') : key; // an '=' value redirects a key targeting the render template to the root document
+        return KeyValueBox.ApplyKVPScript(doc, setKey, script, forceOnDelegate, setResult);
     }, 'Set Doc Field');
 
     onPointerDown = (e: React.PointerEvent): void => {
@@ -156,12 +159,16 @@ export class KeyValueBox extends ViewBoxBaseComponent<FieldViewProps>() {
             });
         });
 
-        const layoutProtos = Doc.GetAllPrototypes(this.layoutDoc);
+        const docinfos = new DocumentOptions();
+
+        const layoutProtos = this.layoutDoc !== doc ? Doc.GetAllPrototypes(this.layoutDoc) : [];
         layoutProtos.forEach(proto => {
             Object.keys(proto)
+                .filter(key => !(key in ids) || docinfos['_' + key]) // if '_key' is in docinfos (as opposed to just 'key'), then the template Doc's value should be displayed instead of the root document's value
                 .map(key => '_' + key)
                 .forEach(key => {
-                    if (!(key.replace(/^_/, '') in ids) && doc[key] !== ComputedField.undefined) {
+                    if (doc[key] !== ComputedField.undefined) {
+                        if (key.replace(/^_/, '') in ids) delete ids[key.replace(/^_/, '')];
                         ids[key] = key;
                     }
                 });
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 4c082b2ad..e3c481a75 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -61,31 +61,22 @@ export class KeyValuePair extends ObservableReactComponent<KeyValuePairProps> {
 
     render() {
         let doc = this._props.keyName.startsWith('_') ? Doc.Layout(this._props.doc) : this._props.doc;
-        // let fieldKey = Object.keys(props.Document).indexOf(props.fieldKey) !== -1 ? props.fieldKey : "(" + props.fieldKey + ")";
         const layoutField = doc !== this._props.doc;
         const key = layoutField ? this._props.keyName.replace(/^_/, '') : this._props.keyName;
         let protoCount = 0;
-        while (doc) {
-            if (Object.keys(doc).includes(key)) {
-                break;
-            }
+        while (doc && !Object.keys(doc).includes(key)) {
             protoCount++;
             doc = DocCast(doc.proto);
         }
         const parenCount = Math.max(0, protoCount - 1);
         const keyStyle = layoutField ? 'red' : protoCount === 0 ? 'black' : 'blue';
-
         const hover = { transition: '0.3s ease opacity', opacity: this.isPointerOver || this.isChecked ? 1 : 0 };
-
+        const docOpts = Object.entries(new DocumentOptions());
         return (
             <tr
-                className={this._props.rowStyle}
-                onPointerEnter={action(() => {
-                    this.isPointerOver = true;
-                })}
-                onPointerLeave={action(() => {
-                    this.isPointerOver = false;
-                })}>
+                className={this._props.rowStyle} //
+                onPointerEnter={action(() => (this.isPointerOver = true))}
+                onPointerLeave={action(() => (this.isPointerOver = false))}>
                 <td className="keyValuePair-td-key" style={{ width: `${this._props.keyWidth}%` }}>
                     <div className="keyValuePair-td-key-container">
                         <button
@@ -93,14 +84,12 @@ export class KeyValuePair extends ObservableReactComponent<KeyValuePairProps> {
                             style={hover}
                             className="keyValuePair-td-key-delete"
                             onClick={undoable(() => {
-                                if (Object.keys(this._props.doc).indexOf(key) !== -1) {
-                                    delete this._props.doc[key];
-                                } else delete DocCast(this._props.doc.proto)?.[key];
+                                delete (Object.keys(doc).indexOf(key) !== -1 ? doc : DocCast(this._props.doc.proto))[key];
                             }, 'set key value')}>
                             X
                         </button>
                         <input className="keyValuePair-td-key-check" type="checkbox" style={hover} onChange={this.handleCheck} ref={this.checkbox} />
-                        <Tooltip title={Object.entries(new DocumentOptions()).find((pair: [string, FInfo]) => pair[0].replace(/^_/, '') === key)?.[1].description ?? ''}>
+                        <Tooltip title={(docOpts.find(([k]) => k.replace(/^_/, '') === key)?.[1] as FInfo)?.description ?? ''}>
                             <div className="keyValuePair-keyField" style={{ marginLeft: 20 * (key.match(/_/g)?.length || 0), color: keyStyle }}>
                                 {'('.repeat(parenCount)}
                                 {(keyStyle === 'black' ? '' : layoutField ? '_' : '$') + key}
@@ -135,7 +124,7 @@ export class KeyValuePair extends ObservableReactComponent<KeyValuePairProps> {
                                 pinToPres: returnZero,
                             }}
                             GetValue={() => Field.toKeyValueString(this._props.doc, this._props.keyName)}
-                            SetValue={(value: string) => Doc.SetField(this._props.doc, this._props.keyName, value)}
+                            SetValue={value => Doc.SetField(this._props.doc, this._props.keyName, value)}
                         />
                     </div>
                 </td>
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index ba088d674..32c664969 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -43,10 +43,12 @@ export namespace Field {
      * @param doc doc containing key
      * @param key field key to display
      * @param showComputedValue whether copmuted function should display its value instead of its function
+     * @param schemaCell
      * @returns string representation of the field
      */
-    export function toKeyValueString(doc: Doc, key: string, showComputedValue?: boolean, schemaCell?: boolean): string {
-        const isOnDelegate = !Doc.IsDataProto(doc) && Object.keys(doc).includes(key.replace(/^_/, ''));
+    export function toKeyValueString(docIn: Doc, key: string, showComputedValue?: boolean, schemaCell?: boolean): string {
+        const doc = key.startsWith('_') && Doc.Layout(docIn) !== docIn ? Doc.Layout(docIn) : docIn;
+        const isOnDelegate = !key.startsWith('_') && doc === docIn && !Doc.IsDataProto(doc) && Object.keys(doc).includes(key.replace(/^_/, ''));
         const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
         const valFunc = (field: FieldType): string => {
             const res =
@@ -64,7 +66,7 @@ export namespace Field {
                       .trim()
                       .replace(/^new List\((.*)\)$/, '$1');
         };
-        return !Field.IsField(cfield) ? (key.startsWith('_') ? '=' : '') : (isOnDelegate ? '=' : '') + valFunc(cfield);
+        return !Field.IsField(cfield) ? '' : (isOnDelegate ? '=' : '') + valFunc(cfield);
     }
     export function toScriptString(field: FieldType, schemaCell?: boolean) {
         switch (typeof field) {
-- 
cgit v1.2.3-70-g09d2


From 6d398efb788689eb1e7bf1ad49e52fd6f11b77d2 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 26 Mar 2025 23:26:57 -0400
Subject: from last

---
 src/client/views/collections/collectionSchema/SchemaCellField.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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

diff --git a/src/client/views/collections/collectionSchema/SchemaCellField.tsx b/src/client/views/collections/collectionSchema/SchemaCellField.tsx
index 1dff76df2..9a3ffc7f2 100644
--- a/src/client/views/collections/collectionSchema/SchemaCellField.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaCellField.tsx
@@ -96,7 +96,7 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
             { fireImmediately: true }
         );
         this._disposers.fieldUpdate = reaction(
-            () => ({ fieldVal: this._props.GetValue(), editing: 0 }), // this._editing }),
+            () => ({ fieldVal: this._props.GetValue(), editing: this._editing }),
             ({ fieldVal, editing }) => {
                 this._unrenderedContent = fieldVal ?? '';
                 !this._editing && this.finalizeEdit(false, false, false);
-- 
cgit v1.2.3-70-g09d2


From 94622ab7b5bef1eccf55adc761a4d71ef4acde55 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 26 Mar 2025 23:28:54 -0400
Subject: from last

---
 src/client/views/collections/collectionSchema/SchemaCellField.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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

diff --git a/src/client/views/collections/collectionSchema/SchemaCellField.tsx b/src/client/views/collections/collectionSchema/SchemaCellField.tsx
index 9a3ffc7f2..7d588e0e9 100644
--- a/src/client/views/collections/collectionSchema/SchemaCellField.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaCellField.tsx
@@ -99,7 +99,7 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
             () => ({ fieldVal: this._props.GetValue(), editing: this._editing }),
             ({ fieldVal, editing }) => {
                 this._unrenderedContent = fieldVal ?? '';
-                !this._editing && this.finalizeEdit(false, false, false);
+                !editing && this.finalizeEdit(false, false, false);
             }
         );
     }
-- 
cgit v1.2.3-70-g09d2


From a4536f6d397c5ca037ed79b59b1deb395b666145 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 27 Mar 2025 00:15:22 -0400
Subject: restore DashFieldView editing by changing schemaCellField to
 setContent on editing.

---
 src/client/views/collections/collectionSchema/SchemaCellField.tsx | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

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

diff --git a/src/client/views/collections/collectionSchema/SchemaCellField.tsx b/src/client/views/collections/collectionSchema/SchemaCellField.tsx
index 7d588e0e9..7e4518dbb 100644
--- a/src/client/views/collections/collectionSchema/SchemaCellField.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaCellField.tsx
@@ -85,6 +85,7 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
             () => this._editing,
             editing => {
                 if (editing) {
+                    this.setContent((this._unrenderedContent = this._props.GetValue() ?? ''));
                     this.setupRefSelect(this.refSelectConditionMet);
                 } else {
                     this._overlayDisposer?.();
@@ -96,10 +97,10 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
             { fireImmediately: true }
         );
         this._disposers.fieldUpdate = reaction(
-            () => ({ fieldVal: this._props.GetValue(), editing: this._editing }),
-            ({ fieldVal, editing }) => {
+            () => this._props.GetValue(),
+            fieldVal => {
                 this._unrenderedContent = fieldVal ?? '';
-                !editing && this.finalizeEdit(false, false, false);
+                this.finalizeEdit(false, false, false);
             }
         );
     }
-- 
cgit v1.2.3-70-g09d2


From b1f85154edc250a18d0bc033df91ee1249975946 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 27 Mar 2025 12:12:20 -0400
Subject: fix so that schemacellfield doesn't trigger a finalizeEdit when it's
 not editing.  added 'red' color for template fields to schematablecell. 
 fixed set field to allow setting data fields when field is an '_' and no
 template.  fixed setting '=' when getting a keyValueString for empty fields
 with an '_' key.

---
 src/client/views/collections/TreeView.tsx                    |  2 +-
 .../views/collections/collectionSchema/SchemaCellField.tsx   |  2 +-
 .../views/collections/collectionSchema/SchemaTableCell.tsx   |  7 ++++---
 src/client/views/nodes/ComparisonBox.tsx                     |  2 +-
 src/client/views/nodes/KeyValueBox.tsx                       | 12 ++++++++----
 src/fields/Doc.ts                                            |  8 ++++----
 6 files changed, 19 insertions(+), 14 deletions(-)

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

diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index cb7da8c84..9889766d5 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -594,7 +594,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
                             const key = match[1];
                             const assign = match[2];
                             const val = match[3];
-                            Doc.SetField(doc, key, assign + val, false);
+                            Doc.SetField(doc, key, assign + val);
                             return true;
                         }
                         return false;
diff --git a/src/client/views/collections/collectionSchema/SchemaCellField.tsx b/src/client/views/collections/collectionSchema/SchemaCellField.tsx
index 7e4518dbb..9ad94cb31 100644
--- a/src/client/views/collections/collectionSchema/SchemaCellField.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaCellField.tsx
@@ -100,7 +100,7 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
             () => this._props.GetValue(),
             fieldVal => {
                 this._unrenderedContent = fieldVal ?? '';
-                this.finalizeEdit(false, false, false);
+                this._editing && this.finalizeEdit(false, false, false);
             }
         );
     }
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index dc5dca3c3..72838074e 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -104,13 +104,14 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
     public static renderProps(props: SchemaTableCellProps) {
         const { Doc: Document, fieldKey, /* getFinfo,*/ columnWidth, isRowActive } = props;
         let protoCount = 0;
-        let doc: Doc | undefined = Document;
+        const layoutDoc = fieldKey.startsWith('_') ? Doc.Layout(Document) : Document;
+        let doc = Document;
         while (doc) {
             if (Object.keys(doc).includes(fieldKey.replace(/^_/, ''))) break;
             protoCount++;
             doc = DocCast(doc.proto);
         }
-        const color = protoCount === 0 || (fieldKey.startsWith('_') && Document[fieldKey] === undefined) ? 'black' : 'blue'; // color of text in cells
+        const color = layoutDoc !== Document ? 'red' : protoCount === 0 || (fieldKey.startsWith('_') && Document[fieldKey] === undefined) ? 'black' : 'blue'; // color of text in cells
         const textDecoration = '';
         const fieldProps: FieldViewProps = {
             childFilters: returnEmptyFilter,
@@ -130,7 +131,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
             focus: emptyFunction,
             addDocTab: SchemaTableCell.addFieldDoc,
             pinToPres: returnZero,
-            Document: DocCast(Document.rootDocument, Document),
+            Document: Document,
             fieldKey: fieldKey,
             PanelWidth: columnWidth,
             PanelHeight: props.rowHeight,
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 992fbba66..482af336c 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -657,7 +657,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
         if (whichSlot === this.backKey && !layoutTemplateString?.includes(whichSlot)) {
             const queryText = altText?.replace('(this)', subjectText); // TODO: this should be done in Doc.setField but it doesn't know about the fieldKey ...
             if (queryText?.match(/\(\(.*\)\)/)) {
-                Doc.SetField(this.Document, whichSlot, ':=' + queryText, false); // make the second slot be a computed field on the data doc that calls ChatGpt
+                Doc.SetField(this.Document, whichSlot, ':=' + queryText); // make the second slot be a computed field on the data doc that calls ChatGpt
             }
         }
         return layoutTemplateString;
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index b54004697..bb711a5fc 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -91,10 +91,11 @@ export class KeyValueBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return !script.compiled ? undefined : { script, type, onDelegate };
     };
 
-    public static ApplyKVPScript = (doc: Doc, key: string, kvpScript: KVPScript, forceOnDelegate?: boolean, setResult?: (value: FieldResult) => void) => {
+    public static ApplyKVPScript = (doc: Doc, keyIn: string, kvpScript: KVPScript, forceOnDelegate?: boolean, setResult?: (value: FieldResult) => void) => {
         const { script, type, onDelegate } = kvpScript;
-        // const target = onDelegate ? Doc.Layout(doc.layout) : Doc.GetProto(doc); // bcz: TODO need to be able to set fields on layout templates
-        const target = forceOnDelegate || onDelegate || key.startsWith('_') ? doc : DocCast(doc.proto, doc);
+        const chooseDelegate = forceOnDelegate || onDelegate || keyIn.startsWith('_');
+        const key = chooseDelegate && keyIn.startsWith('$') ? keyIn.slice(1) : keyIn;
+        const target = chooseDelegate ? doc : DocCast(doc.proto, doc);
         let field: FieldType | undefined;
         switch (type) {
             case 'computed': field = new ComputedField(script); break; // prettier-ignore
@@ -126,7 +127,10 @@ export class KeyValueBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (!script) return false;
         const ldoc = key.startsWith('_') ? Doc.Layout(doc) : doc;
         const forceOnDelegate = forceOnDelegateIn || (ldoc !== doc && !value.startsWith('='));
-        const setKey = value.startsWith('=') ? key.replace(/^_/, '') : key; // an '=' value redirects a key targeting the render template to the root document
+        // an '=' value redirects a key targeting the render template to the root document.
+        // also, if ldoc and doc are equal, allow redirecting to data document when not using an equal
+        // in either case, get rid of initial '_' which forces writing to layout
+        const setKey = value.startsWith('=') || ldoc === doc ? key.replace(/^_/, '') : key;
         return KeyValueBox.ApplyKVPScript(doc, setKey, script, forceOnDelegate, setResult);
     }, 'Set Doc Field');
 
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 32c664969..65719374f 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -46,9 +46,7 @@ export namespace Field {
      * @param schemaCell
      * @returns string representation of the field
      */
-    export function toKeyValueString(docIn: Doc, key: string, showComputedValue?: boolean, schemaCell?: boolean): string {
-        const doc = key.startsWith('_') && Doc.Layout(docIn) !== docIn ? Doc.Layout(docIn) : docIn;
-        const isOnDelegate = !key.startsWith('_') && doc === docIn && !Doc.IsDataProto(doc) && Object.keys(doc).includes(key.replace(/^_/, ''));
+    export function toKeyValueString(doc: Doc, key: string, showComputedValue?: boolean, schemaCell?: boolean): string {
         const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
         const valFunc = (field: FieldType): string => {
             const res =
@@ -66,7 +64,9 @@ export namespace Field {
                       .trim()
                       .replace(/^new List\((.*)\)$/, '$1');
         };
-        return !Field.IsField(cfield) ? '' : (isOnDelegate ? '=' : '') + valFunc(cfield);
+        const notOnTemplate = !key.startsWith('_') || Doc.Layout(doc) === doc;
+        const isOnDelegate = notOnTemplate && !Doc.IsDataProto(doc) && ((key.startsWith('_') && !Field.IsField(cfield)) || Object.keys(doc).includes(key.replace(/^_/, '')));
+        return (isOnDelegate ? '=' : '') + (!Field.IsField(cfield) ? '' : valFunc(cfield));
     }
     export function toScriptString(field: FieldType, schemaCell?: boolean) {
         switch (typeof field) {
-- 
cgit v1.2.3-70-g09d2

