From b420caf2c7ecd386cae2cc550904522474b541aa Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 26 Mar 2024 22:34:10 -0400
Subject: added empty image tool and click on empty image to select from
 filesystem.   fixed following links in lightbox and showing links to
 stackedTimelines.  fixed embedding docs into text.  fixed not resizing text
 boxes that also show up in pivot view.  prevent context menu from going off
 top of screen.   fixed freeform clustering colors and click to type. fixed
 links to stackedTimeline marks, and titles for marks.  made title editing
 from doc deco and header use same syntax as keyValue.  fixed marquee
 selection on webBoxes.  turn off transitions in freeformdocview after
 timeout. enabled iconifying templates to propagate to "offspring".  fixes
 images in templates.  don't show headr on schema views.

---
 src/client/views/nodes/ImageBox.tsx | 55 +++++++++++++++++++++++++++++--------
 1 file changed, 44 insertions(+), 11 deletions(-)

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

diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 251235b93..86e8ed60a 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -9,7 +9,7 @@ import { DocData } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { InkTool } from '../../../fields/InkField';
 import { ObjectField } from '../../../fields/ObjectField';
-import { Cast, NumCast, StrCast } from '../../../fields/Types';
+import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types';
 import { ImageField } from '../../../fields/URLField';
 import { TraceMobx } from '../../../fields/util';
 import { DashColor, emptyFunction, returnEmptyString, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from '../../../Utils';
@@ -29,6 +29,12 @@ import { OpenWhere } from './DocumentView';
 import { FocusViewOptions, FieldView, FieldViewProps } from './FieldView';
 import './ImageBox.scss';
 import { PinProps, PresBox } from './trails';
+import { Colors } from 'browndash-components';
+import { listSpec } from '../../../fields/Schema';
+import { List } from '../../../fields/List';
+import { url } from 'inspector';
+import { OverlayView } from '../OverlayView';
+import { Networking } from '../../Network';
 
 @observer
 export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implements ViewBoxInterface {
@@ -134,7 +140,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl
             if (de.metaKey || targetIsBullseye(e.target as HTMLElement)) {
                 added = de.complete.docDragData.droppedDocuments.reduce((last: boolean, drop: Doc) => {
                     this.layoutDoc[this.fieldKey + '_usePath'] = 'alternate:hover';
-                    return last && Doc.AddDocToList(this.dataDoc, this.fieldKey + '-alternates', drop);
+                    return last && Doc.AddDocToList(this.dataDoc, this.fieldKey + '_alternates', drop);
                 }, true);
             } else if (de.altKey || !this.dataDoc[this.fieldKey]) {
                 const layoutDoc = de.complete.docDragData?.draggedDocuments[0];
@@ -259,7 +265,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl
         const lower = url.href.toLowerCase();
         if (url.protocol === 'data') return url.href;
         if (url.href.indexOf(window.location.origin) === -1 && url.href.indexOf('dashblobstore') === -1) return Utils.CorsProxy(url.href);
-        if (!/\.(png|jpg|jpeg|gif|webp)$/.test(lower)) return `/assets/unknown-file-icon-hi.png`;
+        if (!/\.(png|jpg|jpeg|gif|webp)$/.test(lower) || lower.endsWith('/assets/unknown-file-icon-hi.png')) return `/assets/unknown-file-icon-hi.png`;
 
         const ext = extname(url.href);
         return url.href.replace(ext, (this._error ? '_o' : this._curSuffix) + ext);
@@ -297,7 +303,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl
                     ref={this._overlayIconRef}
                     onPointerDown={e => setupMoveUpEvents(e.target, e, returnFalse, emptyFunction, e => (this.layoutDoc[`_${this.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined))}
                     style={{
-                        display: (this._props.isContentActive() !== false && DragManager.DocDragData?.canEmbed) || DocListCast(this.dataDoc[this.fieldKey + '-alternates']).length ? 'block' : 'none',
+                        display: (this._props.isContentActive() !== false && DragManager.DocDragData?.canEmbed) || this.dataDoc[this.fieldKey + '_alternates'] ? 'block' : 'none',
                         width: 'min(10%, 25px)',
                         height: 'min(10%, 25px)',
                         background: usePath === undefined ? 'white' : usePath === 'alternate' ? 'black' : 'gray',
@@ -311,13 +317,15 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl
 
     @computed get paths() {
         const field = Cast(this.dataDoc[this.fieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc
-        const alts = DocListCast(this.dataDoc[this.fieldKey + '-alternates']); // retrieve alternate documents that may be rendered as alternate images
-        const altpaths = alts
-            .map(doc => Cast(doc[Doc.LayoutFieldKey(doc)], ImageField, null)?.url)
-            .filter(url => url)
-            .map(url => this.choosePath(url)); // access the primary layout data of the alternate documents
+        const alts = this.dataDoc[this.fieldKey + '_alternates'] as any as List<Doc>; // retrieve alternate documents that may be rendered as alternate images
+        const defaultUrl = new URL(Utils.prepend('/assets/unknown-file-icon-hi.png'));
+        const altpaths =
+            alts
+                ?.map(doc => (doc instanceof Doc ? ImageCast(doc[Doc.LayoutFieldKey(doc)])?.url ?? defaultUrl : defaultUrl))
+                .filter(url => url)
+                .map(url => this.choosePath(url)) ?? []; // acc  ess the primary layout data of the alternate documents
         const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths;
-        return paths.length ? paths : [Utils.CorsProxy('https://cs.brown.edu/~bcz/noImage.png')];
+        return paths.length ? paths : [defaultUrl.href];
     }
 
     @observable _error = '';
@@ -326,7 +334,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl
     @computed get content() {
         TraceMobx();
 
-        const backColor = DashColor(this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor));
+        const backColor = DashColor(this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor) ?? Colors.WHITE);
         const backAlpha = backColor.red() === 0 && backColor.green() === 0 && backColor.blue() === 0 ? backColor.alpha() : 1;
         const srcpath = this.layoutDoc.hideImage ? '' : this.paths[0];
         const fadepath = this.layoutDoc.hideImage ? '' : this.paths.lastElement();
@@ -370,6 +378,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl
     }
     screenToLocalTransform = () => this.ScreenToLocalBoxXf().translate(0, NumCast(this.layoutDoc._layout_scrollTop) * this.ScreenToLocalBoxXf().Scale);
     marqueeDown = (e: React.PointerEvent) => {
+        if (!this.dataDoc[this.fieldKey]) return this.chooseImage();
         if (!e.altKey && e.button === 0 && NumCast(this.layoutDoc._freeform_scale, 1) <= NumCast(this.dataDoc.freeform_scaleMin, 1) && this._props.isContentActive() && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
             setupMoveUpEvents(
                 this,
@@ -468,4 +477,28 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl
             </div>
         );
     }
+
+    public chooseImage = () => {
+        const input = document.createElement('input');
+        input.type = 'file';
+        input.multiple = true;
+        input.accept = 'image/*';
+        input.onchange = async _e => {
+            const file = input.files?.[0];
+            if (file) {
+                const disposer = OverlayView.ShowSpinner();
+                const [{ result }] = await Networking.UploadFilesToServer({ file });
+                if (result instanceof Error) {
+                    alert('Error uploading files - possibly due to unsupported file types');
+                } else {
+                    this.dataDoc[this.fieldKey] = new ImageField(result.accessPaths.agnostic.client);
+                    !(result instanceof Error) && DocUtils.assignImageInfo(result, this.dataDoc);
+                }
+                disposer();
+            } else {
+                console.log('No file selected');
+            }
+        };
+        input.click();
+    };
 }
-- 
cgit v1.2.3-70-g09d2


From a9baa52c557faaa82fd3f696f0d603dd10b982a8 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 30 Mar 2024 02:18:07 -0400
Subject: updated mermaids template. updated paintFunc to also render the
 underlying code text so that the paintFunc will update if dashFields in the
 code change.  fixed schema views to render templates properly in preview. 
 fixed text to only modify the data doc.  changed setPixels in imageBox to not
 consider scaling of outer containers.

---
 src/client/util/CurrentUserUtils.ts                          |  4 ++--
 src/client/views/StyleProvider.tsx                           |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx            |  6 ++++--
 .../views/collections/collectionSchema/SchemaTableCell.tsx   |  6 +++---
 src/client/views/nodes/ImageBox.tsx                          |  3 +--
 src/client/views/nodes/formattedText/FormattedTextBox.tsx    | 12 ++++++++----
 6 files changed, 19 insertions(+), 14 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 3855bdeb6..dbf9641b6 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -321,9 +321,9 @@ export class CurrentUserUtils {
                         {type:"text",text:" "},
                         {type:"text",text:"Minerals in my tap water"},
                         {type:"text",text:"\n    \"Calcium\" : "},
-                        {type:"dashField",attrs:{fieldKey:"calcium",docId:"",hideKey:false,hideValue:false,editable:true}},
+                        {type:"dashField",attrs:{fieldKey:"calcium",docId:"",hideKey:true,hideValue:false,editable:true}},
                         {type:"text",text:"\n    \"Potassium\" : "},
-                        {type:"dashField",attrs:{fieldKey:"pot",docId:"",hideKey:false,hideValue:false,editable:true}},
+                        {type:"dashField",attrs:{fieldKey:"pot",docId:"",hideKey:true,hideValue:false,editable:true}},
                         {type:"text",text:"\n    \"Magnesium\" : 10.01"}
                     ]}
                 ]},
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 8073d0842..c8b0c0560 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -139,7 +139,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
         case StyleProp.WidgetColor:return isAnnotated ? Colors.LIGHT_BLUE : 'dimgrey';
         case StyleProp.Opacity:    return props?.LayoutTemplateString?.includes(KeyValueBox.name) ? 1 : doc?.text_inlineAnnotations ? 0 :  Cast(doc?._opacity, "number", Cast(doc?.opacity, 'number', null));
         case StyleProp.FontSize:   return StrCast(doc?.[fieldKey + 'fontSize'],   StrCast(Doc.UserDoc().fontSize));
-        case StyleProp.FontFamily: return StrCast(doc?.[fieldKey + 'fontFamilyq'], StrCast(Doc.UserDoc().fontFamily));
+        case StyleProp.FontFamily: return StrCast(doc?.[fieldKey + 'fontFamily'], StrCast(Doc.UserDoc().fontFamily));
         case StyleProp.FontWeight: return StrCast(doc?.[fieldKey + 'fontWeight'],  StrCast(Doc.UserDoc().fontWeight));
         case StyleProp.FillColor:  return StrCast(doc?._fillColor, StrCast(doc?.fillColor, StrCast(doc?.backgroundColor, 'transparent')));
         case StyleProp.ShowCaption:return props?.hideCaptions || doc?._type_collection === CollectionViewType.Carousel ? undefined: StrCast(doc?._layout_showCaption);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index c53bad509..1b562939b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -76,7 +76,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @observable _paintedId = 'id' + Utils.GenerateGuid().replace(/-/g, '');
     @computed get paintFunc() {
-        const field = this.layoutDoc[this.fieldKey];
+        const field = this.dataDoc[this.fieldKey];
         const paintFunc = StrCast(Field.toJavascriptString(Cast(field, RichTextField, null)?.Text as Field)).trim();
         return !paintFunc
             ? ''
@@ -1870,7 +1870,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                     width: `${100 / (this.nativeDimScaling || 1)}%`,
                     height: this._props.getScrollHeight?.() ?? `${100 / (this.nativeDimScaling || 1)}%`,
                 }}>
-                {this.paintFunc ? null : this._lightboxDoc ? (
+                {this.paintFunc ? (
+                    <FormattedTextBox {...this.props} /> // need this so that any live dashfieldviews will update the underlying text that the code eval reads
+                ) : this._lightboxDoc ? (
                     <div style={{ padding: 15, width: '100%', height: '100%' }}>
                         <DocumentView
                             {...this._props}
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index ab05a62d3..bf36b2668 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -98,7 +98,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
             focus: emptyFunction,
             addDocTab: SchemaTableCell.addFieldDoc,
             pinToPres: returnZero,
-            Document,
+            Document: DocCast(Document.rootDocument, Document),
             fieldKey: fieldKey,
             PanelWidth: columnWidth,
             PanelHeight: props.rowHeight,
@@ -134,14 +134,14 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
                     contents={undefined}
                     fieldContents={fieldProps}
                     editing={this.selected ? undefined : false}
-                    GetValue={() => Field.toKeyValueString(this._props.Document, this._props.fieldKey, SnappingManager.MetaKey)}
+                    GetValue={() => Field.toKeyValueString(fieldProps.Document, this._props.fieldKey, SnappingManager.MetaKey)}
                     SetValue={undoable((value: string, shiftDown?: boolean, enterKey?: boolean) => {
                         if (shiftDown && enterKey) {
                             this._props.setColumnValues(this._props.fieldKey.replace(/^_/, ''), value);
                             this._props.finishEdit?.();
                             return true;
                         }
-                        const ret = KeyValueBox.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), value, Doc.IsDataProto(this._props.Document) ? true : undefined);
+                        const ret = KeyValueBox.SetField(fieldProps.Document, this._props.fieldKey.replace(/^_/, ''), value, Doc.IsDataProto(fieldProps.Document) ? true : undefined);
                         this._props.finishEdit?.();
                         return ret;
                     }, 'edit schema cell')}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 86e8ed60a..469869e21 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -165,8 +165,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl
 
     @undoBatch
     setNativeSize = action(() => {
-        const scaling = (this.DocumentView?.().screenToViewTransform().Scale || 1) / NumCast(this.layoutDoc._freeform_scale, 1);
-        const nscale = NumCast(this._props.PanelWidth()) / scaling;
+        const nscale = NumCast(this._props.PanelWidth()) * NumCast(this.layoutDoc._freeform_scale, 1);
         const nw = nscale / NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']);
         this.dataDoc[this.fieldKey + '_nativeHeight'] = NumCast(this.dataDoc[this.fieldKey + '_nativeHeight']) * nw;
         this.dataDoc[this.fieldKey + '_nativeWidth'] = NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']) * nw;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 3b583771a..b815342e3 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -326,10 +326,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
 
     leafText = (node: Node) => {
         if (node.type === this._editorView?.state.schema.nodes.dashField) {
-            const refDoc = !node.attrs.docId ? this.Document : (DocServer.GetCachedRefField(node.attrs.docId as string) as Doc);
+            const refDoc = !node.attrs.docId ? DocCast(this.Document.rootDocument, this.Document) : (DocServer.GetCachedRefField(node.attrs.docId as string) as Doc);
             const fieldKey = StrCast(node.attrs.fieldKey);
             return (
-                (node.attrs.hideKey ? '' : '"' + fieldKey + '"' + ':') + //
+                (node.attrs.hideKey ? '' : fieldKey + ':') + //
                 (node.attrs.hideValue ? '' : Field.toJavascriptString(refDoc[fieldKey] as Field))
             );
         }
@@ -346,7 +346,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     tryUpdateDoc = (force: boolean) => {
         if (this._editorView && (this._editorView as any).docView) {
             const state = this._editorView.state;
-            const dataDoc = Doc.IsDelegateField(DocCast(this.layoutDoc.proto), this.fieldKey) ? DocCast(this.layoutDoc.proto) : this.dataDoc;
+            const dataDoc = this.dataDoc;
             const newText = state.doc.textBetween(0, state.doc.content.size, ' \n', this.leafText);
             const newJson = JSON.stringify(state.toJSON());
             const prevData = Cast(this.layoutDoc[this.fieldKey], RichTextField, null); // the actual text in the text box
@@ -1608,7 +1608,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                 while (target && !target.dataset?.targethrefs) target = target.parentElement;
                 FormattedTextBoxComment.update(this, editor, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc, target?.dataset.nopreview === 'true');
             } else {
-                editor.dispatch(state.tr.setSelection(new NodeSelection(state.doc.resolve(xpos))));
+                try {
+                    editor.dispatch(state.tr.setSelection(new NodeSelection(state.doc.resolve(xpos))));
+                } catch (e) {
+                    editor.dispatch(state.tr.setSelection(new NodeSelection(state.doc.resolve(xpos - 1))));
+                }
             }
         }
     };
-- 
cgit v1.2.3-70-g09d2

