From d60734c4cdd8fe64d50e3de32182cc6b04afc747 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 8 Apr 2025 14:26:56 -0400
Subject: fixed gptImageLabel calls and added ImageCastToNameType to deal with
 filenames with '.'s in them.  fixed vertical centered text.

---
 src/client/views/collections/CollectionView.tsx | 1 -
 1 file changed, 1 deletion(-)

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

diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 7ba8989ce..6a7336bca 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -130,7 +130,6 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr
                 { description: 'Calendar',    event: () => func(CollectionViewType.Calendar), icon: 'columns' },
                 { description: 'Pivot',       event: () => func(CollectionViewType.Pivot), icon: 'columns' },
                 { description: 'Time',        event: () => func(CollectionViewType.Time), icon: 'columns' },
-                { description: 'Map',         event: () => func(CollectionViewType.Map), icon: 'globe-americas' },
                 { description: 'Grid',        event: () => func(CollectionViewType.Grid), icon: 'th-list' },
             ];
 
-- 
cgit v1.2.3-70-g09d2


From dd5c4e9b427196f8b0a34774a4667cfcaf6c8edf Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 8 Apr 2025 14:56:44 -0400
Subject: change isLightbox to be a datadoc flag.

---
 src/client/documents/DocUtils.ts                                        | 2 +-
 src/client/documents/Documents.ts                                       | 2 +-
 src/client/views/PropertiesButtons.tsx                                  | 2 +-
 src/client/views/collections/CollectionView.tsx                         | 2 +-
 .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx     | 2 +-
 src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx   | 2 +-
 6 files changed, 6 insertions(+), 6 deletions(-)

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

diff --git a/src/client/documents/DocUtils.ts b/src/client/documents/DocUtils.ts
index a657b673a..f5eee84aa 100644
--- a/src/client/documents/DocUtils.ts
+++ b/src/client/documents/DocUtils.ts
@@ -578,7 +578,7 @@ export namespace DocUtils {
                 Docs.Create.FreeformDocument([], {
                     _width: NumCast(layoutDoc._width) + 10,
                     _height: Math.max(NumCast(layoutDoc._height), NumCast(layoutDoc._width) + 10),
-                    _isLightbox: true,
+                    isLightbox: true,
                     _layout_fitWidth: true,
                     title: StrCast(doc.title) + ' [Portal]',
                 }),
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index bab9edf0e..0e3c614fe 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -368,7 +368,7 @@ export class DocumentOptions {
     isGroup?: BOOLt = new BoolInfo('should collection use a grouping UI behavior');
     isFolder?: BOOLt = new BoolInfo('is document a tree view folder');
     _isTimelineLabel?: BOOLt = new BoolInfo('is document a timeline label');
-    _isLightbox?: BOOLt = new BoolInfo('whether a collection acts as a lightbox by opening lightbox links by hiding all other documents in collection besides link target');
+    isLightbox?: BOOLt = new BoolInfo('whether a collection acts as a lightbox by opening lightbox links by hiding all other documents in collection besides link target');
     cloneOnCopy?: BOOLt = new BoolInfo('if this Doc is a field of another Doc, then it should be copied when the other Doc is copied');
 
     mapPin?: DOCt = new DocInfo('pin associated with a config anchor', false);
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 8f18de33f..6778a6691 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -293,7 +293,7 @@ export class PropertiesButtons extends React.Component {
     @computed get onClickVal() {
         const linkButton = IsFollowLinkScript(this.selectedDoc.onClick);
         const followLoc = this.selectedDoc._followLinkLocation;
-        const linkedToLightboxView = () => Doc.Links(this.selectedDoc).some(link => Doc.getOppositeAnchor(link, this.selectedDoc)?._isLightbox);
+        const linkedToLightboxView = () => Doc.Links(this.selectedDoc).some(link => Doc.getOppositeAnchor(link, this.selectedDoc)?.$isLightbox);
 
         if (followLoc === OpenWhere.lightbox && !linkedToLightboxView()) return 'linkInPlace';
         if (linkButton && followLoc === OpenWhere.addRight) return 'linkOnRight';
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 6a7336bca..c9af92a1b 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -161,7 +161,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr
             if (this.Document.childClickedOpenTemplateView instanceof Doc) {
                 optionItems.push({ description: 'View Child Detailed Layout', event: () => this._props.addDocTab(this.Document.childClickedOpenTemplateView as Doc, OpenWhere.addRight), icon: 'project-diagram' });
             }
-            !Doc.noviceMode && optionItems.push({ description: `${this.layoutDoc._isLightbox ? 'Unset' : 'Set'} is Lightbox`, event: () => { this.layoutDoc._isLightbox = !this.layoutDoc._isLightbox; }, icon: 'project-diagram' }); // prettier-ignore
+            !Doc.noviceMode && optionItems.push({ description: `${this.dataDoc.$isLightbox ? 'Unset' : 'Set'} is Lightbox`, event: () => { this.dataDoc.$isLightbox = !this.dataDoc.$isLightbox; }, icon: 'project-diagram' }); // prettier-ignore
 
             !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'hand-point-right' });
 
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 1f789432d..626538976 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1587,7 +1587,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             }
             case undefined:
             case OpenWhere.lightbox:
-                if (this.layoutDoc._isLightbox) {
+                if (this.dataDoc.$isLightbox) {
                     this._lightboxDoc = docs[0];
                     return true;
                 }
diff --git a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
index e4d38eb4a..c7aa410c6 100644
--- a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
+++ b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
@@ -11,7 +11,7 @@ import { emptyFunction } from '../../../../Utils';
 import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
 import { DocData } from '../../../../fields/DocSymbols';
 import { List } from '../../../../fields/List';
-import { DocCast, ImageCast, ImageCastToNameType, NumCast, StrCast } from '../../../../fields/Types';
+import { DocCast, ImageCastToNameType, NumCast, StrCast } from '../../../../fields/Types';
 import { DocumentType } from '../../../documents/DocumentTypes';
 import { Docs } from '../../../documents/Documents';
 import { DragManager } from '../../../util/DragManager';
-- 
cgit v1.2.3-70-g09d2


From e8f1d494d36a5e1f1ee33d59faa4be2559cd752e Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 22 Apr 2025 10:54:38 -0400
Subject: adding drop handling code for scrapbookBox

---
 src/client/documents/DocUtils.ts                   | 60 +++++++--------
 src/client/views/DocumentDecorations.tsx           |  2 +-
 src/client/views/InkingStroke.tsx                  |  2 +-
 src/client/views/Main.tsx                          |  4 +-
 .../views/collections/CollectionStackingView.tsx   |  5 +-
 src/client/views/collections/CollectionSubView.tsx |  3 +-
 src/client/views/collections/CollectionView.tsx    | 20 ++---
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +
 .../collectionGrid/CollectionGridView.tsx          |  1 +
 .../collectionLinear/CollectionLinearView.tsx      |  1 +
 .../CollectionMulticolumnView.tsx                  |  1 +
 .../CollectionMultirowView.tsx                     |  1 +
 src/client/views/nodes/FieldView.tsx               |  2 +-
 src/client/views/nodes/FontIconBox/FontIconBox.tsx |  4 +-
 src/client/views/nodes/ImageBox.tsx                |  5 +-
 src/client/views/nodes/scrapbook/ScrapbookBox.tsx  | 87 ++++++++++++----------
 16 files changed, 104 insertions(+), 96 deletions(-)

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

diff --git a/src/client/documents/DocUtils.ts b/src/client/documents/DocUtils.ts
index db0770b12..df14dce5a 100644
--- a/src/client/documents/DocUtils.ts
+++ b/src/client/documents/DocUtils.ts
@@ -308,13 +308,7 @@ export namespace DocUtils {
      */
     export async function DocumentFromType(type: string, path: string, options: DocumentOptions, overwriteDoc?: Doc): Promise<Opt<Doc>> {
         let ctor: ((path: string, options: DocumentOptions, overwriteDoc?: Doc) => Doc | Promise<Doc | undefined>) | undefined;
-       
-        if (type.indexOf('scrapbook') !== -1) { 
-            ctor = Docs.Create.ScrapbookDocument;
-            if (!options._width) options._width = 400;
-            if (!options._title) options._title = 'New Scrapbook';
-        }
-        
+
         if (type.indexOf('image') !== -1) {
             ctor = Docs.Create.ImageDocument;
             if (!options._width) options._width = 300;
@@ -718,33 +712,31 @@ export namespace DocUtils {
                       nativeWidth: 40,
                       nativeHeight: 40,
                   })
-                : (defaultTextTemplate?.type === DocumentType.JOURNAL ? Docs.Create.DailyJournalDocument:Docs.Create.TextDocument)(
-                    '', 
-                    {
-                        annotationOn,
-                        backgroundColor,
-                        x,
-                        y,
-                        title,
-                        ...(defaultTextTemplate
-                            ? {} // if the new doc will inherit from a template, don't set any layout fields since that would block the inheritance
-                            : {
-                                  _width: width || BoolCast(Doc.UserDoc().fitBox) ? Number(StrCast(Doc.UserDoc().fontSize).replace('px', '')) * 1.5 * 6 : 200,
-                                  _height: BoolCast(Doc.UserDoc().fitBox) ? Number(StrCast(Doc.UserDoc().fontSize).replace('px', '')) * 1.5 : 35,
-                                  _layout_autoHeight: true,
-                                  backgroundColor: StrCast(Doc.UserDoc().textBackgroundColor),
-                                  borderColor: Doc.UserDoc().borderColor as string,
-                                  borderWidth: Doc.UserDoc().borderWidth as number,
-                                  text_centered: BoolCast(Doc.UserDoc().textCentered),
-                                  text_fitBox: BoolCast(Doc.UserDoc().fitBox),
-                                  text_align: StrCast(Doc.UserDoc().textAlign),
-                                  text_fontColor: StrCast(Doc.UserDoc().fontColor),
-                                  text_fontFamily: StrCast(Doc.UserDoc().fontFamily),
-                                  text_fontWeight: StrCast(Doc.UserDoc().fontWeight),
-                                  text_fontStyle: StrCast(Doc.UserDoc().fontStyle),
-                                  text_fontDecoration: StrCast(Doc.UserDoc().fontDecoration),
-                              }),
-                    });
+                : (defaultTextTemplate?.type === DocumentType.JOURNAL ? Docs.Create.DailyJournalDocument : Docs.Create.TextDocument)('', {
+                      annotationOn,
+                      backgroundColor,
+                      x,
+                      y,
+                      title,
+                      ...(defaultTextTemplate
+                          ? {} // if the new doc will inherit from a template, don't set any layout fields since that would block the inheritance
+                          : {
+                                _width: width || BoolCast(Doc.UserDoc().fitBox) ? Number(StrCast(Doc.UserDoc().fontSize).replace('px', '')) * 1.5 * 6 : 200,
+                                _height: BoolCast(Doc.UserDoc().fitBox) ? Number(StrCast(Doc.UserDoc().fontSize).replace('px', '')) * 1.5 : 35,
+                                _layout_autoHeight: true,
+                                backgroundColor: StrCast(Doc.UserDoc().textBackgroundColor),
+                                borderColor: Doc.UserDoc().borderColor as string,
+                                borderWidth: Doc.UserDoc().borderWidth as number,
+                                text_centered: BoolCast(Doc.UserDoc().textCentered),
+                                text_fitBox: BoolCast(Doc.UserDoc().fitBox),
+                                text_align: StrCast(Doc.UserDoc().textAlign),
+                                text_fontColor: StrCast(Doc.UserDoc().fontColor),
+                                text_fontFamily: StrCast(Doc.UserDoc().fontFamily),
+                                text_fontWeight: StrCast(Doc.UserDoc().fontWeight),
+                                text_fontStyle: StrCast(Doc.UserDoc().fontStyle),
+                                text_fontDecoration: StrCast(Doc.UserDoc().fontDecoration),
+                            }),
+                  });
 
         if (defaultTextTemplate) {
             tbox.layout_fieldKey = 'layout_' + StrCast(defaultTextTemplate.title);
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 7424aaf2c..3f11a4713 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -240,7 +240,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
                         } else {
                             // if Doc is in the sticker palette, remove the flag indicating that it's saved
                             const dragFactory = DocCast(iconView.Document.dragFactory);
-                            if (dragFactory && DocCast(dragFactory.cloneOf).savedAsSticker) DocCast(dragFactory.cloneOf).savedAsSticker = undefined;
+                            if (dragFactory && DocCast(dragFactory.cloneOf)?.savedAsSticker) DocCast(dragFactory.cloneOf)!.savedAsSticker = undefined;
 
                             // if this is a face Annotation doc, then just hide it.
                             if (iconView.Document.annotationOn && iconView.Document.face) iconView.Document.hidden = true;
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index f555808ef..0a49ea28e 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -245,7 +245,7 @@ export class InkingStroke extends ViewBoxAnnotatableComponent<FieldViewProps>()
      * factor for converting between ink and screen space.
      */
     inkScaledData = () => {
-        const inkData = Cast(this.dataDoc[this.fieldKey], InkField, Cast(this.layoutDoc[this.fieldKey], InkField, null))?.inkData ?? [];
+        const inkData = Cast(this.dataDoc[this.fieldKey], InkField, Cast(this.layoutDoc[this.fieldKey], InkField, null) ?? null)?.inkData ?? [];
         const inkStrokeWidth = NumCast(this.layoutDoc.stroke_width, 1);
         const inkTop = Math.min(...inkData.map(p => p.Y)) - inkStrokeWidth / 2;
         const inkBottom = Math.max(...inkData.map(p => p.Y)) + inkStrokeWidth / 2;
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 7992ed412..b884eb8c8 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -53,7 +53,6 @@ import { WebBox } from './nodes/WebBox';
 import { CalendarBox } from './nodes/calendarBox/CalendarBox';
 import { ChatBox } from './nodes/chatbot/chatboxcomponents/ChatBox';
 import { DailyJournal } from './nodes/formattedText/DailyJournal';
-import { ScrapbookVersionTwo } from './nodes/scrapbook/ScrapbookVersionTwo';
 import { DashDocCommentView } from './nodes/formattedText/DashDocCommentView';
 import { DashDocView } from './nodes/formattedText/DashDocView';
 import { DashFieldView } from './nodes/formattedText/DashFieldView';
@@ -66,6 +65,7 @@ import { PresBox, PresSlideBox } from './nodes/trails';
 import { FaceRecognitionHandler } from './search/FaceRecognitionHandler';
 import { SearchBox } from './search/SearchBox';
 import { StickerPalette } from './smartdraw/StickerPalette';
+import { ScrapbookBox } from './nodes/scrapbook/ScrapbookBox';
 
 dotenv.config();
 
@@ -121,7 +121,6 @@ FieldLoader.ServerLoadStatus = { requested: 0, retrieved: 0, message: 'cache' };
             FormattedTextBox,
             DailyJournal, // AARAV
             ImageBox,
-            ScrapbookVersionTwo,
             FontIconBox,
             LabelBox,
             EquationBox,
@@ -136,6 +135,7 @@ FieldLoader.ServerLoadStatus = { requested: 0, retrieved: 0, message: 'cache' };
             VideoBox,
             AudioBox,
             RecordingBox,
+            ScrapbookBox,
             PresBox,
             PresSlideBox,
             SearchBox,
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 9155227dd..f11e646cc 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -343,7 +343,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
     childFitWidth = (doc: Doc) => Cast(this.Document.childLayoutFitWidth, 'boolean', this._props.childLayoutFitWidth?.(doc) ?? Cast(doc.layout_fitWidth, 'boolean', null) ?? null);
     // this is what renders the document that you see on the screen
     // called in Children: this actually adds a document to our children list
-    getDisplayDoc(doc: Doc, trans: () => string, count: number) {
+    getDisplayDoc = (doc: Doc, trans: () => string, count: number) => {
         const dataDoc = doc.isTemplateDoc || doc.isTemplateForField ? this._props.TemplateDataDocument : undefined;
         this._docXfs.push({ stackedDocTransform: this.getDocTransform(doc), width: this.getDocWidth(doc), height: this.getDocHeight(doc) });
         return count > this._renderCount ? null : (
@@ -384,6 +384,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                 searchFilterDocs={this.searchFilterDocs}
                 xPadding={NumCast(this.layoutDoc._childXPadding, this._props.childXPadding)}
                 yPadding={NumCast(this.layoutDoc._childYPadding, this._props.childYPadding)}
+                rejectDrop={this._props.childRejectDrop}
                 addDocument={this._props.addDocument}
                 moveDocument={this._props.moveDocument}
                 removeDocument={this._props.removeDocument}
@@ -393,7 +394,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                 pinToPres={this._props.pinToPres}
             />
         );
-    }
+    };
 
     getDocTransform = computedFn((doc: Doc) => () => {
         // these must be referenced for document decorations to update when the text box container is scrolled
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index bc7d6f897..9a0fda3f8 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -58,6 +58,7 @@ export interface CollectionViewProps extends React.PropsWithChildren<FieldViewPr
     childOpacity?: () => number;
     childContextMenuItems?: () => { script: ScriptField; label: string }[];
     childLayoutTemplate?: () => Doc | undefined; // specify a layout Doc template to use for children of the collection
+    childRejectDrop?: (draggedDoc: Doc[] | undefined, subView?: DocumentView) => boolean; // whether a child document can be dropped on this document
     childHideDecorationTitle?: boolean;
     childHideResizeHandles?: boolean;
     childHideDecorations?: boolean;
@@ -323,7 +324,7 @@ export function CollectionSubView<X>() {
 
         protected onInternalDrop(e: Event, de: DragManager.DropEvent): boolean {
             const { docDragData } = de.complete;
-            if (docDragData && !docDragData.draggedDocuments.includes(this.Document)) {
+            if (docDragData && !docDragData.draggedDocuments.includes(this.Document) && !this._props.rejectDrop?.(docDragData.draggedDocuments, this.DocumentView?.())) {
                 let added;
                 const dropAction = docDragData.dropAction || docDragData.userDropAction;
                 const targetDocments = DocListCast(this.dataDoc[this._props.fieldKey]);
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index c9af92a1b..eb9caf29d 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -183,15 +183,17 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr
                         },
                     })
                 );
-                DocListCast(Cast(Doc.UserDoc()['clickFuncs-child'], Doc, null)?.data).forEach(childClick =>
-                    onClicks.push({
-                        description: `Set child ${childClick.title}`,
-                        icon: 'edit',
-                        event: () => {
-                            this.dataDoc[StrCast(childClick.targetScriptKey)] = ObjectField.MakeCopy(ScriptCast(childClick.data));
-                        },
-                    })
-                );
+                DocListCast(Cast(Doc.UserDoc()['clickFuncs-child'], Doc, null)?.data)
+                    .filter(childClick => ScriptCast(childClick.data))
+                    .forEach(childClick =>
+                        onClicks.push({
+                            description: `Set child ${childClick.title}`,
+                            icon: 'edit',
+                            event: () => {
+                                this.dataDoc[StrCast(childClick.targetScriptKey)] = ObjectField.MakeCopy(ScriptCast(childClick.data)!);
+                            },
+                        })
+                    );
                 !Doc.IsSystem(this.Document) && !existingOnClick && cm.addItem({ description: 'OnClick...', noexpand: true, subitems: onClicks, icon: 'mouse-pointer' });
             }
 
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 842293358..08126d4fe 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -496,6 +496,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     }, 'link drop');
 
     onInternalDrop = (e: Event, de: DragManager.DropEvent): boolean => {
+        if (this._props.rejectDrop?.(de.complete.docDragData?.draggedDocuments, this._props.DocumentView?.())) return false;
         if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de, de.complete.annoDragData);
         if (de.complete.linkDragData) return this.internalLinkDrop(e, de, de.complete.linkDragData);
         if (de.complete.docDragData?.droppedDocuments.length) return this.internalDocDrop(e, de, de.complete.docDragData);
@@ -1562,6 +1563,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 pinToPres={this._props.pinToPres}
                 whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
                 dragAction={(this.Document.childDragAction ?? this._props.childDragAction) as dropActionType}
+                rejectDrop={this._props.childRejectDrop}
                 showTitle={this._props.childlayout_showTitle}
                 dontRegisterView={this._props.dontRegisterView}
                 pointerEvents={this.childPointerEventsFunc}
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index a7603a45b..1d5e70be7 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -201,6 +201,7 @@ export class CollectionGridView extends CollectionSubView() {
             isContentActive={this.isChildContentActive}
             PanelWidth={width}
             PanelHeight={height}
+            rejectDrop={this._props.childRejectDrop}
             ScreenToLocalTransform={dxf}
             setContentViewBox={emptyFunction}
             whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index 8d3947653..3c2a99b1e 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -192,6 +192,7 @@ export class CollectionLinearView extends CollectionSubView() {
                     renderDepth={this._props.renderDepth + 1}
                     dontRegisterView={BoolCast(this.Document.childDontRegisterViews)}
                     focus={emptyFunction}
+                    rejectDrop={this._props.childRejectDrop}
                     styleProvider={this._props.styleProvider}
                     containerViewPath={this.childContainerViewPath}
                     whenChildContentsActiveChanged={emptyFunction}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index adaceb053..caad1c7e0 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -281,6 +281,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
             PanelWidth={this.childWidth(childLayout)}
             PanelHeight={this.childHeight}
             rootSelected={this.rootSelected}
+            rejectDrop={this._props.childRejectDrop}
             dragAction={StrCast(this.Document.childDragAction, this._props.childDragAction) as dropActionType}
             onClickScript={this.onChildClickHandler}
             onDoubleClickScript={this.onChildDoubleClickHandler}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 27c41583c..593598479 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -263,6 +263,7 @@ export class CollectionMultirowView extends CollectionSubView() {
             PanelWidth={this.childWidth}
             PanelHeight={this.childHeight(childLayout)}
             rootSelected={this.rootSelected}
+            rejectDrop={this._props.childRejectDrop}
             dragAction={StrCast(this.Document.childDragAction, this._props.childDragAction) as dropActionType}
             onClickScript={this.onChildClickHandler}
             onDoubleClickScript={this.onChildDoubleClickHandler}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index de49f502f..b4473d9dc 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -68,7 +68,7 @@ export interface FieldViewSharedProps {
     isGroupActive?: () => string | undefined; // is this document part of a group that is active
     // eslint-disable-next-line no-use-before-define
     setContentViewBox?: (view: ViewBoxInterface<FieldViewProps>) => void; // called by rendered field's viewBox so that DocumentView can make direct calls to the viewBox
-
+    rejectDrop?: (draggedDoc: Doc[] | undefined, subView?: DocumentView) => boolean; // whether a document drop is rejected
     PanelWidth: () => number;
     PanelHeight: () => number;
     isDocumentActive?: () => boolean | undefined; // whether a document should handle pointer events
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index 3190757e2..5941e1669 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -117,10 +117,10 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
             default:                               type = 'slider';
                 break;
         } // prettier-ignore
-        const numScript = (value?: number) => ScriptCast(this.Document.script).script.run({ this: this.Document, value, _readOnly_: value === undefined });
+        const numScript = (value?: number) => ScriptCast(this.Document.script)?.script.run({ this: this.Document, value, _readOnly_: value === undefined });
         const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color) as string;
         // Script for checking the outcome of the toggle
-        const checkResult = Number(Number(numScript().result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3)));
+        const checkResult = Number(Number(numScript()?.result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3)));
 
         return (
             <NumberDropdown
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 7ad3ee2b6..c59c38fd8 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -2,7 +2,7 @@ import { Button, Colors, EditableText, Size, Type } from '@dash/components';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Slider, Tooltip } from '@mui/material';
 import axios from 'axios';
-import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import { extname } from 'path';
 import * as React from 'react';
@@ -213,7 +213,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
 
     drop = undoable(
         action((e: Event, de: DragManager.DropEvent) => {
-            if (de.complete.docDragData) {
+            if (de.complete.docDragData && this._props.rejectDrop?.(de.complete.docDragData?.draggedDocuments, this.DocumentView?.())) {
                 let added: boolean | undefined;
                 const hitDropTarget = (ele: HTMLElement, dropTarget: HTMLDivElement | null): boolean => {
                     if (!ele) return false;
@@ -938,6 +938,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
                         ScreenToLocalTransform={this.screenToLocalTransform}
                         select={emptyFunction}
                         focus={this.focus}
+                        rejectDrop={this._props.rejectDrop}
                         getScrollHeight={this.getScrollHeight}
                         NativeDimScaling={returnOne}
                         isAnyChildContentActive={returnFalse}
diff --git a/src/client/views/nodes/scrapbook/ScrapbookBox.tsx b/src/client/views/nodes/scrapbook/ScrapbookBox.tsx
index 56cfcda70..b02976067 100644
--- a/src/client/views/nodes/scrapbook/ScrapbookBox.tsx
+++ b/src/client/views/nodes/scrapbook/ScrapbookBox.tsx
@@ -1,24 +1,25 @@
-import { makeObservable } from 'mobx';
+import { action, makeObservable, observable } from 'mobx';
 import * as React from 'react';
-import { ViewBoxAnnotatableComponent } from '../../DocComponent';
-import { FieldView, FieldViewProps } from '../FieldView';
+import { Doc } from '../../../../fields/Doc';
+import { List } from '../../../../fields/List';
+import { emptyFunction } from '../../../../Utils';
 import { Docs } from '../../../documents/Documents';
 import { DocumentType } from '../../../documents/DocumentTypes';
-import { action, observable } from 'mobx';
-import { DocListCast } from '../../../../fields/Doc';
-import { Doc } from '../../../../fields/Doc';
+import { CollectionView } from '../../collections/CollectionView';
+import { ViewBoxAnnotatableComponent } from '../../DocComponent';
 import { DocumentView } from '../DocumentView';
-import { FormattedTextBox } from '../formattedText/FormattedTextBox';
-import { List } from '../../../../fields/List';
+import { FieldView, FieldViewProps } from '../FieldView';
+import { DragManager } from '../../../util/DragManager';
 // Scrapbook view: a container that lays out its child items in a grid/template
 export class ScrapbookBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     @observable createdDate: string;
+    private _dropDisposer?: DragManager.DragDropDisposer;
 
     constructor(props: FieldViewProps) {
         super(props);
         makeObservable(this);
         this.createdDate = this.getFormattedDate();
-  
+
         // ensure we always have a List<Doc> in dataDoc['items']
         if (!this.dataDoc[this.fieldKey]) {
             this.dataDoc[this.fieldKey] = new List<Doc>();
@@ -31,12 +32,6 @@ export class ScrapbookBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
         return FieldView.LayoutString(ScrapbookBox, fieldStr);
     }
 
-
-    
-    
-     
-    
-
     getFormattedDate(): string {
         return new Date().toLocaleDateString(undefined, {
             year: 'numeric',
@@ -55,40 +50,50 @@ export class ScrapbookBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
 
     componentDidMount() {
         this.setTitle();
-        if (!this.dataDoc[this.fieldKey]) {
-            this.dataDoc[this.fieldKey] = new List<Doc>();
-        }
     }
 
+    childRejectDrop = (draggedDoc: Doc[] | undefined, subView?: DocumentView) => {
+        if (draggedDoc?.length === 1 && subView) {
+            if (subView.Document.type === DocumentType.IMG && draggedDoc[0].$type !== DocumentType.IMG) {
+                return true;
+            }
+        }
+        return false;
+    };
+    rejectDrop = (draggedDoc: Doc[] | undefined, subView?: DocumentView) => {
+        if (draggedDoc?.length === 1 && draggedDoc[0].$type !== DocumentType.IMG) {
+            return true;
+        }
+        return false;
+    };
+    onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
+        if (de.complete.docDragData?.draggedDocuments[0]?.$type === DocumentType.IMG) {
+            return true;
+        }
+        return false;
+    };
+
+    protected createDashEventsTarget = (ele: HTMLDivElement | null) => {
+        this._dropDisposer?.();
+        if (ele) {
+            this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
+        }
+    };
+
     render() {
-        // cast into an array even if empty
-        const items: Doc[] = DocListCast(this.dataDoc[this.fieldKey]);
-    
         return (
-            <div style={{ background: 'beige', width: '100%', height: '100%' }}>
-                        
+            <div style={{ background: 'beige', width: '100%', height: '100%' }} ref={r => r && this.createDashEventsTarget(r)}>
+                <CollectionView
+                    {...this._props} //
+                    setContentViewBox={emptyFunction}
+                    rejectDrop={this.rejectDrop}
+                    childRejectDrop={this.childRejectDrop}
+                />
             </div>
-        
-        //   <div
-        //     style={{
-        //     }}
-        //   >
-        //     {items.length === 0
-        //       ? <div style={{color:'#888',fontStyle:'italic'}}>Drop docs here</div>
-        //       : items.map((childDoc, idx) => (
-        //           <DocumentView
-        //             key={String(childDoc.id ?? idx)}
-        //             {...this._props}
-        //             Document={childDoc}
-        //           />
-        //         ))
-        //     }
-        //   </div>
         );
-      }
+    }
 }
 
-
 // Register scrapbook
 Docs.Prototypes.TemplateMap.set(DocumentType.SCRAPBOOK, {
     layout: { view: ScrapbookBox, dataField: 'items' },
-- 
cgit v1.2.3-70-g09d2

