aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ClientUtils.ts2
-rw-r--r--src/client/documents/DocUtils.ts22
-rw-r--r--src/client/documents/Documents.ts21
-rw-r--r--src/client/util/CurrentUserUtils.ts55
-rw-r--r--src/client/util/DocumentManager.ts2
-rw-r--r--src/client/util/DropConverter.ts4
-rw-r--r--src/client/util/LinkManager.ts4
-rw-r--r--src/client/views/InkingStroke.tsx4
-rw-r--r--src/client/views/MainView.tsx2
-rw-r--r--src/client/views/MarqueeAnnotator.tsx1
-rw-r--r--src/client/views/ObservableReactComponent.tsx8
-rw-r--r--src/client/views/PropertiesButtons.tsx2
-rw-r--r--src/client/views/ViewBoxInterface.ts1
-rw-r--r--src/client/views/collections/CollectionCardDeckView.tsx2
-rw-r--r--src/client/views/collections/CollectionCarouselView.tsx2
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx6
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx6
-rw-r--r--src/client/views/collections/CollectionSubView.tsx32
-rw-r--r--src/client/views/collections/TabDocView.tsx4
-rw-r--r--src/client/views/collections/TreeView.tsx8
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx60
-rw-r--r--src/client/views/collections/collectionGrid/CollectionGridView.tsx4
-rw-r--r--src/client/views/collections/collectionLinear/CollectionLinearView.tsx16
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.tsx2
-rw-r--r--src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx7
-rw-r--r--src/client/views/global/globalScripts.ts5
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx14
-rw-r--r--src/client/views/nodes/DocumentView.scss3
-rw-r--r--src/client/views/nodes/DocumentView.tsx107
-rw-r--r--src/client/views/nodes/FieldView.tsx4
-rw-r--r--src/client/views/nodes/IconTagBox.tsx24
-rw-r--r--src/client/views/nodes/ImageBox.scss2
-rw-r--r--src/client/views/nodes/ImageBox.tsx107
-rw-r--r--src/client/views/nodes/LabelBox.tsx35
-rw-r--r--src/client/views/nodes/PDFBox.tsx4
-rw-r--r--src/client/views/nodes/ScreenshotBox.tsx4
-rw-r--r--src/client/views/nodes/WebBox.tsx22
-rw-r--r--src/client/views/nodes/WebBoxRenderer.js2
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx22
-rw-r--r--src/client/views/nodes/imageEditor/ImageEditor.tsx5
-rw-r--r--src/client/views/nodes/imageEditor/imageEditorUtils/ImageHandler.ts6
-rw-r--r--src/client/views/nodes/trails/PresSlideBox.tsx8
-rw-r--r--src/client/views/pdf/GPTPopup/GPTPopup.scss7
-rw-r--r--src/client/views/pdf/GPTPopup/GPTPopup.tsx153
-rw-r--r--src/client/views/smartdraw/DrawingFillHandler.tsx6
-rw-r--r--src/client/views/smartdraw/SmartDrawHandler.scss1
-rw-r--r--src/client/views/smartdraw/SmartDrawHandler.tsx65
-rw-r--r--src/client/views/smartdraw/StickerPalette.tsx12
-rw-r--r--src/fields/Doc.ts19
-rw-r--r--src/fields/documentSchemas.ts2
-rw-r--r--src/server/SharedMediaTypes.ts3
-rw-r--r--src/server/server_Initialization.ts220
52 files changed, 533 insertions, 606 deletions
diff --git a/src/ClientUtils.ts b/src/ClientUtils.ts
index 03ff13924..cc8b715b4 100644
--- a/src/ClientUtils.ts
+++ b/src/ClientUtils.ts
@@ -194,7 +194,7 @@ export namespace ClientUtils {
}
export function CorsProxy(url: string): string {
- return prepend('/corsProxy/') + encodeURIComponent(url);
+ return prepend('/corsproxy/') + encodeURIComponent(url);
}
export function CopyText(text: string) {
diff --git a/src/client/documents/DocUtils.ts b/src/client/documents/DocUtils.ts
index df14dce5a..36e03daed 100644
--- a/src/client/documents/DocUtils.ts
+++ b/src/client/documents/DocUtils.ts
@@ -603,7 +603,7 @@ export namespace DocUtils {
layout_hideAllLinks: true,
_width: 15,
_height: 15,
- _xPadding: 0,
+ _xMargin: 0,
onClick: FollowLinkScript(),
_timecodeToShow: Cast(doc._timecodeToShow, 'number', null),
});
@@ -642,8 +642,15 @@ export namespace DocUtils {
return dd;
}
- export function assignImageInfo(result: Upload.FileInformation, protoIn: Doc) {
+ export function assignUploadInfo(result: Upload.FileInformation, protoIn: Doc) {
const proto = protoIn;
+
+ if (Upload.isTextInformation(result)) {
+ proto.text = result.rawText;
+ }
+ if (Upload.isVideoInformation(result)) {
+ proto.data_duration = result.duration;
+ }
if (Upload.isImageInformation(result)) {
const maxNativeDim = Math.max(result.nativeHeight, result.nativeWidth);
const exifRotation = StrCast(result.exifData?.data?.Orientation).toLowerCase();
@@ -677,15 +684,8 @@ export namespace DocUtils {
const pathname = result.accessPaths.agnostic.client;
const doc = await DocUtils.DocumentFromType(type, pathname, full, overwriteDoc);
if (doc) {
- const proto = Doc.GetProto(doc);
- proto.text = result.rawText;
- !(result instanceof Error) && DocUtils.assignImageInfo(result, proto);
- if (Upload.isVideoInformation(result)) {
- proto.data_duration = result.duration;
- }
- if (overwriteDoc) {
- Doc.removeCurrentlyLoading(overwriteDoc);
- }
+ DocUtils.assignUploadInfo(result, Doc.GetProto(doc));
+ overwriteDoc && Doc.removeCurrentlyLoading(overwriteDoc);
generatedDocuments.push(doc);
}
return doc;
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index bb6f5d637..1ca298dd7 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -199,7 +199,6 @@ export class DocumentOptions {
_height?: NUMt = new NumInfo("height of document in container's coordiantes");
data_nativeWidth?: NUMt = new NumInfo('native width of data field contents (e.g., the pixel width of an image)', false);
data_nativeHeight?: NUMt = new NumInfo('native height of data field contents (e.g., the pixel height of an image)', false);
- linearBtnWidth?: NUMt = new NumInfo('unexpanded width of a linear menu button (button "width" changes when it expands)', false);
_nativeWidth?: NUMt = new NumInfo('Deprecated: use nativeWidth. native width of document contents (e.g., the pixel width of an image)', false);
_nativeHeight?: NUMt = new NumInfo('Deprecated: use nativeHeight. native height of document contents (e.g., the pixel height of an image)', false);
nativeWidth?: NUMt = new NumInfo('native width of document contents (e.g., the pixel width of an image)', false);
@@ -295,10 +294,8 @@ export class DocumentOptions {
_chromeHidden?: BOOLt = new BoolInfo('whether the editing chrome for a document is hidden');
hideClickBehaviors?: BOOLt = new BoolInfo('whether to hide click behaviors in context menu');
_gridGap?: NUMt = new NumInfo('gap between items in masonry view', false);
- _xMargin?: NUMt = new NumInfo('gap between left edge of document and start of masonry/stacking layouts', false);
- _yMargin?: NUMt = new NumInfo('gap between top edge of dcoument and start of masonry/stacking layouts', false);
- _xPadding?: NUMt = new NumInfo('x padding', false);
- _yPadding?: NUMt = new NumInfo('y padding', false);
+ _xMargin?: NUMt = new NumInfo('gap between left edge of document and contents of freeform/masonry/stacking layouts', false);
+ _yMargin?: NUMt = new NumInfo('gap between top edge of dcoument and contents offreeform/masonry/stacking layouts', false);
_createDocOnCR?: boolean; // whether carriage returns and tabs create new text documents
_columnsHideIfEmpty?: BOOLt = new BoolInfo('whether stacking view column headings should be hidden');
_caption_xMargin?: NUMt = new NumInfo('x margin of caption inside of a carousel collection', false, true);
@@ -428,10 +425,9 @@ export class DocumentOptions {
badgeValue?: ScriptField;
// LINEAR VIEW
- linearView_IsOpen?: BOOLt = new BoolInfo('is linear view open');
- linearView_Expandable?: BOOLt = new BoolInfo('can linear view be expanded');
- linearView_Dropdown?: BOOLt = new BoolInfo('can linear view be opened as a dropdown');
- linearView_SubMenu?: BOOLt = new BoolInfo('is doc a sub menu of more linear views');
+ linearView_btnWidth?: NUMt = new NumInfo('unexpanded width of a linear menu button (button "width" changes when it expands)', false);
+ linearView_isOpen?: BOOLt = new BoolInfo('is linear view open');
+ linearView_expandable?: BOOLt = new BoolInfo('can linear view be expanded');
flexGap?: NUMt = new NumInfo('Linear view flex gap');
flexDirection?: 'unset' | 'row' | 'column' | 'row-reverse' | 'column-reverse';
@@ -524,8 +520,9 @@ export class DocumentOptions {
card_sort_isDesc?: BOOLt = new BoolInfo('whether the cards are sorted ascending or descending');
ai?: string; // to mark items as ai generated
- ai_firefly_seed?: number;
- ai_firefly_prompt?: string;
+ ai_prompt_seed?: NUMt = new NumInfo('seed to GAI engine to make results deterministic');
+ ai_prompt?: STRt = new StrInfo('input prompt to GAI engine');
+ ai_generatedDocs?: List<Doc>; // list of documents generated by GAI engine
/**
* JSON‐stringified slot configuration for ScrapbookBox
@@ -1033,7 +1030,7 @@ export namespace Docs {
const nwid = options._nativeWidth || undefined;
const nhght = options._nativeHeight || undefined;
if (!nhght && width && height && nwid) options._nativeHeight = (Number(nwid) * Number(height)) / Number(width);
- return InstanceFromProto(Prototypes.get(DocumentType.WEB), new WebField(url || 'https://www.wikipedia.org/'), options);
+ return InstanceFromProto(Prototypes.get(DocumentType.WEB), new WebField(url || 'https://wikipedia.org/'), options);
}
export function HtmlDocument(html: string, options: DocumentOptions = {}) {
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 22084d519..3896ac6fc 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -54,7 +54,7 @@ export interface Button {
numBtnMax?: number;
switchToggle?: boolean;
width?: number;
- linearBtnWidth?: number;
+ linearView_btnWidth?: number;
toolType?: string; // type of pen tool
expertMode?: boolean;// available only in expert mode
btnList?: List<string>;
@@ -207,7 +207,7 @@ export class CurrentUserUtils {
const templateIconsDoc = DocUtils.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts));
const labelBox = (opts: DocumentOptions, fieldKey:string) => Docs.Create.LabelDocument({
- layout: LabelBox.LayoutString(fieldKey), letterSpacing: "unset", _label_minFontSize: 14, _label_maxFontSize: 14, layout_borderRounding: "5px", _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...opts
+ layout: LabelBox.LayoutString(fieldKey), letterSpacing: "unset", _label_minFontSize: 14, _label_maxFontSize: 14, layout_borderRounding: "5px", _width: 150, _height: 70, _xMargin: 10, _yMargin: 10, ...opts
});
const imageBox = (opts: DocumentOptions, fieldKey:string) => Docs.Create.ImageDocument( "http://www.cs.brown.edu/~bcz/noImage.png", { layout:ImageBox.LayoutString(fieldKey), "icon_nativeWidth": 360 / 4, "icon_nativeHeight": 270 / 4, iconTemplate:DocumentType.IMG, _width: 360 / 4, _height: 270 / 4, _layout_showTitle: "title", ...opts });
const fontBox = (opts:DocumentOptions, fieldKey:string) => Docs.Create.FontIconDocument({ layout:FontIconBox.LayoutString(fieldKey), _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, ...opts });
@@ -404,7 +404,7 @@ pie title Minerals in my tap water
{key: "Map", creator: opts => Docs.Create.MapDocument([], opts), opts: { _width: 800, _height: 600, _layout_fitWidth: true, }},
{key: "Screengrab", creator: Docs.Create.ScreenshotDocument, opts: { _width: 400, _height: 200 }},
{key: "WebCam", creator: opts => Docs.Create.WebCamDocument("", opts), opts: { _width: 400, _height: 200, recording:true, isSystem: true, cloneFieldFilter: new List<string>(["isSystem"]) }},
- {key: "Button", creator: Docs.Create.ButtonDocument, opts: { _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, title_custom: true, waitForDoubleClickToClick: 'never'}, scripts: {onClick: FollowLinkScript()?.script.originalScript ?? ""}},
+ {key: "Button", creator: Docs.Create.ButtonDocument, opts: { _width: 150, _height: 50, _xMargin: 10, _yMargin: 10, title_custom: true, waitForDoubleClickToClick: 'never'}, scripts: {onClick: FollowLinkScript()?.script.originalScript ?? ""}},
{key: "Script", creator: opts => Docs.Create.ScriptingDocument(null, opts), opts: { _width: 200, _height: 250, }},
{key: "DataViz", creator: opts => Docs.Create.DataVizDocument("", opts), opts: { _width: 300, _height: 300, }},
// AARAV ADD //
@@ -665,7 +665,7 @@ pie title Minerals in my tap water
}
static linearButtonList = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.LinearDocument(docs, {
- ...opts, _gridGap: 0, _xMargin: 5, _yMargin: 5, layout_boxShadow: "0 0", _forceActive: true,
+ ...opts, _gridGap: 0, _xMargin: 5, _yMargin: 5, layout_boxShadow: "0 0", _forceActive: true, linearView: true,
dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
_lockedPosition: true, isSystem: true, flexDirection: "row"
})
@@ -699,7 +699,7 @@ pie title Minerals in my tap water
const btns = btnDescs.map(desc => dockBtn({_width: 30, _height: 30, defaultDoubleClick: 'ignore', undoIgnoreFields: new List<string>(['opacity']), _dragOnlyWithinContainer: true, ...desc.opts}, desc.scripts));
const dockBtnsReqdOpts:DocumentOptions = {
title: "docked buttons", _height: 40, flexGap: 0, layout_boxShadow: "standard", childDragAction: dropActionType.move,
- childDontRegisterViews: true, linearView_IsOpen: true, linearView_Expandable: true, ignoreClick: true
+ childDontRegisterViews: true, linearView_isOpen: true, linearView_expandable: true, ignoreClick: true
};
reaction(() => UndoManager.redoStack.slice(), () => { Doc.GetProto(btns.find(btn => btn.title === "Redo")!).opacity = UndoManager.CanRedo() ? 1 : 0.4; }, { fireImmediately: true });
reaction(() => UndoManager.undoStack.slice(), () => { Doc.GetProto(btns.find(btn => btn.title === "Undo")!).opacity = UndoManager.CanUndo() ? 1 : 0.4; }, { fireImmediately: true });
@@ -791,24 +791,24 @@ pie title Minerals in my tap water
{ title: "Circle", toolTip: "Circle (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "circle", toolType: Gestures.Circle, scripts: {onClick:`{ return setActiveTool(this.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(this.toolType, true, _readOnly_);}`} },
{ title: "Square", toolTip: "Square (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "square", toolType: Gestures.Rectangle, scripts: {onClick:`{ return setActiveTool(this.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(this.toolType, true, _readOnly_);}`} },
{ title: "Line", toolTip: "Line (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "minus", toolType: Gestures.Line, scripts: {onClick:`{ return setActiveTool(this.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(this.toolType, true, _readOnly_);}`} },
- { title: "Ink", toolTip: "Ink", btnType: ButtonType.MultiToggleButton, toolType: InkTool.Ink, showUntilToggle: true, scripts: {onClick:'{ return setActiveTool(this.toolType, true, _readOnly_);}' },
+ { title: "Ink", toolTip: "Ink", btnType: ButtonType.MultiToggleButton, toolType: InkTool.Ink, showUntilToggle: true, scripts: {onClick:'{ return setActiveTool(this.toolType, true, _readOnly_);}' },
subMenu: [
{ title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen-nib", toolType: InkInkTool.Pen, ignoreClick: true, scripts: {onClick:'{ return setActiveTool(this.toolType, true, _readOnly_);}' }},
{ title: "Highlight",toolTip: "Highlight (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", toolType: InkInkTool.Highlight, ignoreClick: true, scripts: {onClick:'{ return setActiveTool(this.toolType, true, _readOnly_);}' }},
{ title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", toolType: InkInkTool.Write, ignoreClick: true, scripts: {onClick:'{ return setActiveTool(this.toolType, true, _readOnly_);}' }, funcs: {hidden:"IsNoviceMode()" }},
]},
- { title: "Width", toolTip: "Stroke width", btnType: ButtonType.NumberSliderButton, toolType: InkProperty.StrokeWidth,ignoreClick: true, scripts: {script: '{ return setInkProperty(this.toolType, value, _readOnly_);}'}, funcs: {hidden:"!activeInkTool()"}, numBtnMin: 1, linearBtnWidth:40},
+ { title: "Width", toolTip: "Stroke width", btnType: ButtonType.NumberSliderButton, toolType: InkProperty.StrokeWidth,ignoreClick: true, scripts: {script: '{ return setInkProperty(this.toolType, value, _readOnly_);}'}, funcs: {hidden:"!activeInkTool()"}, numBtnMin: 1, linearView_btnWidth:40},
{ title: "Color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", toolType: InkProperty.StrokeColor,ignoreClick: true, scripts: {script: '{ return setInkProperty(this.toolType, value, _readOnly_);}'}, funcs: {hidden:"!activeInkTool()"}},
- { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.MultiToggleButton, toolType: InkTool.Eraser, showUntilToggle: true, scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}' },
+ { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.MultiToggleButton, toolType: InkTool.Eraser, showUntilToggle: true, scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}' },
subMenu: [
{ title: "Stroke", toolTip: "Eraser complete strokes",btnType: ButtonType.ToggleButton, icon: "eraser", toolType:InkEraserTool.Stroke, ignoreClick: true, scripts: {onClick: '{ return setActiveTool(this.toolType, false, _readOnly_);}'}},
{ title: "Segment", toolTip: "Erase between intersections",btnType:ButtonType.ToggleButton,icon:"xmark", toolType:InkEraserTool.Segment, ignoreClick: true, scripts: {onClick: '{ return setActiveTool(this.toolType, false, _readOnly_);}'}},
{ title: "Area", toolTip: "Erase like a pencil", btnType: ButtonType.ToggleButton, icon: "circle-xmark",toolType:InkEraserTool.Radius, ignoreClick: true, scripts: {onClick: '{ return setActiveTool(this.toolType, false, _readOnly_);}'}},
]},
- { title: " Size", toolTip: "Size of area pencil eraser", btnType: ButtonType.NumberSliderButton, toolType: InkProperty.EraserWidth,ignoreClick: true, scripts: {script: '{ return setInkProperty(this.toolType, value, _readOnly_);}'}, funcs: {hidden:"NotRadiusEraser()"}, numBtnMin: 1, linearBtnWidth:40},
+ { title: " Size", toolTip: "Size of area pencil eraser", btnType: ButtonType.NumberSliderButton, toolType: InkProperty.EraserWidth,ignoreClick: true, scripts: {script: '{ return setInkProperty(this.toolType, value, _readOnly_);}'}, funcs: {hidden:"NotRadiusEraser()"}, numBtnMin: 1, linearView_btnWidth:40},
{ title: "Mask", toolTip: "Make Stroke a Stencil Mask", btnType: ButtonType.ToggleButton, icon: "user-circle", toolType: InkProperty.Mask, scripts: {onClick:'{ return setInkProperty(this.toolType, value, _readOnly_);}'}, funcs: {hidden:"IsNoviceMode()" } },
{ title: "Labels", toolTip: "Show Labels Inside Shapes", btnType: ButtonType.ToggleButton, icon: "text-width", toolType: InkProperty.Labels, scripts: {onClick:'{ return setInkProperty(this.toolType, value, _readOnly_);}'}},
- { title: "Smart Draw", toolTip: "Draw with AI", btnType: ButtonType.ToggleButton, icon: "user-pen", toolType: InkTool.SmartDraw, scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}'}, funcs: {hidden: "IsNoviceMode()"}},
+ { title: "Smart Draw", toolTip: "Draw with AI", btnType: ButtonType.ToggleButton, icon: "user-pen", toolType: InkTool.SmartDraw, scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}'}, funcs: {hidden: "IsNoviceMode()"}},
];
}
@@ -843,9 +843,10 @@ pie title Minerals in my tap water
{ title: "Pin", icon: "map-pin", toolTip: "Pin View to Trail", btnType: ButtonType.ClickButton, expertMode: false, width: 30, scripts: { onClick: 'pinWithView(altKey)'}, funcs: {hidden: "IsNoneSelected()"}},
{ title: "Header", icon: "heading", toolTip: "Doc Titlebar Color", btnType: ButtonType.ColorButton, expertMode: false, ignoreClick: true, scripts: { script: 'return setHeaderColor(value, _readOnly_)'} },
{ title: "Template",icon: "scroll", toolTip: "Default Note Template",btnType: ButtonType.ToggleButton, expertMode: false, toolType:DocumentType.RTF, scripts: { onClick: '{ return setDefaultTemplate(_readOnly_); }'} },
+ { title: "Img Temp",icon: "portrait", toolTip: "Default Image Template",btnType:ButtonType.ToggleButton, expertMode: false, toolType:DocumentType.IMG, scripts: { onClick: '{ return setDefaultImageTemplate(_readOnly_); }'} },
{ title: "Fill", icon: "fill-drip", toolTip: "Fill/Background Color",btnType: ButtonType.ColorButton, expertMode: false, ignoreClick: true, width: 30, scripts: { script: 'return setBackgroundColor(value, _readOnly_)'} }, // Only when a document is selected
{ title: "Border", icon: "border-style", toolTip: "Border Color", btnType: ButtonType.ColorButton, expertMode: false, ignoreClick: true, width: 30, scripts: { script: 'return setBorderColor(value, _readOnly_)'} }, // Only when a document is selected
- { title: "B.Width", toolTip: "Border width", btnType: ButtonType.NumberSliderButton, ignoreClick: true, scripts: {script: '{ return setBorderWidth(value, _readOnly_);}'}, numBtnMin: 0, linearBtnWidth:40},
+ { title: "B.Width", toolTip: "Border width", btnType: ButtonType.NumberSliderButton, ignoreClick: true, scripts: {script: '{ return setBorderWidth(value, _readOnly_);}'}, numBtnMin: 0, linearView_btnWidth:40},
{ title: "Overlay", icon: "layer-group", toolTip: "Overlay", btnType: ButtonType.ToggleButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectedDocType(this.toolType, this.expertMode, true)'}, scripts: { onClick: '{ return toggleOverlay(_readOnly_); }'}}, // Only when floating document is selected in freeform
{ title: "Back", icon: "chevron-left", toolTip: "Prev Animation Frame", btnType: ButtonType.ClickButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectedDocType(this.toolType, this.expertMode)'}, width: 30, scripts: { onClick: 'prevKeyFrame(_readOnly_)'}},
{ title: "Num", icon:"", toolTip: "Frame # (click to toggle edit mode)",btnType: ButtonType.TextButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectedDocType(this.toolType, this.expertMode)', buttonText: 'selectedDocs()?.lastElement()?.currentFrame?.toString()'}, width: 20, scripts: { onClick: '{ return curKeyFrame(_readOnly_);}'}},
@@ -853,17 +854,17 @@ pie title Minerals in my tap water
{ title: "Chat", icon: "lightbulb", toolTip: "Toggle Chat Assistant",btnType: ButtonType.ToggleButton, expertMode: false, toolType:"toggle-chat", funcs: {}, width: 30, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'} },
{ title: "Filter", icon: "=", toolTip: "Filter cards by tags", btnType: ButtonType.MultiToggleButton,
subMenu: this.filterTools(), expertMode: false, toolType:DocumentType.COL, funcs: {hidden: '!SelectedDocType(this.toolType, this.expertMode)'},ignoreClick:true, width: 30},
- { title: "Sort", icon: "Sort", toolTip: "Sort Documents", subMenu: this.sortTools(), expertMode: false, toolType:DocumentType.COL, funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Always available
- { title: "Text", icon: "Text", toolTip: "Text functions", subMenu: this.textTools(), expertMode: false, toolType:DocumentType.RTF, funcs: { linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Always available
- { title: "Ink", icon: "Ink", toolTip: "Ink functions", subMenu: this.inkTools(), expertMode: false, toolType:DocumentType.INK, funcs: {hidden: `IsExploreMode()`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`}, scripts: { onClick: 'setInkToolDefaults()'} }, // Always available
- { title: "Doc", icon: "Doc", toolTip: "Freeform Doc tools", subMenu: this.freeTools(), expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode, true)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Always available
- { title: "View", icon: "View", toolTip: "View tools", subMenu: this.viewTools(), expertMode: false, toolType:DocumentType.COL, funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Always available
- { title: "Stack", icon: "View", toolTip: "Stacking tools", subMenu: this.stackTools(), expertMode: false, toolType:CollectionViewType.Stacking, funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Always available
+ { title: "Sort", icon: "Sort", toolTip: "Sort Documents", subMenu: this.sortTools(), expertMode: false, toolType:DocumentType.COL, funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_isOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Always available
+ { title: "Text", icon: "Text", toolTip: "Text functions", subMenu: this.textTools(), expertMode: false, toolType:DocumentType.RTF, funcs: { linearView_isOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Always available
+ { title: "Ink", icon: "Ink", toolTip: "Ink functions", subMenu: this.inkTools(), expertMode: false, toolType:DocumentType.INK, funcs: {hidden: `IsExploreMode()`, linearView_isOpen: `SelectedDocType(this.toolType, this.expertMode)`}, scripts: { onClick: 'setInkToolDefaults()'} }, // Always available
+ { title: "Doc", icon: "Doc", toolTip: "Freeform Doc tools", subMenu: this.freeTools(), expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode, true)`, linearView_isOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Always available
+ { title: "View", icon: "View", toolTip: "View tools", subMenu: this.viewTools(), expertMode: false, toolType:DocumentType.COL, funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_isOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Always available
+ { title: "Stack", icon: "View", toolTip: "Stacking tools", subMenu: this.stackTools(), expertMode: false, toolType:CollectionViewType.Stacking, funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_isOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Always available
- { title: "Web", icon: "Web", toolTip: "Web functions", subMenu: this.webTools(), expertMode: false, toolType:DocumentType.WEB, funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Only when Web is selected
- { title: "Video", icon: "Video", toolTip: "Video functions", subMenu: this.videoTools(), expertMode: false, toolType:DocumentType.VID, funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Only when video is selected
- { title: "Image", icon: "Image", toolTip: "Image functions", subMenu: this.imageTools(), expertMode: false, toolType:DocumentType.IMG, funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Only when image is selected
- { title: "Schema", icon: "Schema", toolTip: "Schema functions", subMenu: this.schemaTools(), expertMode: false, toolType:CollectionViewType.Schema, funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`}, linearBtnWidth:58 }, // Only when Schema is selected
+ { title: "Web", icon: "Web", toolTip: "Web functions", subMenu: this.webTools(), expertMode: false, toolType:DocumentType.WEB, funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_isOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Only when Web is selected
+ { title: "Video", icon: "Video", toolTip: "Video functions", subMenu: this.videoTools(), expertMode: false, toolType:DocumentType.VID, funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_isOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Only when video is selected
+ { title: "Image", icon: "Image", toolTip: "Image functions", subMenu: this.imageTools(), expertMode: false, toolType:DocumentType.IMG, funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_isOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Only when image is selected
+ { title: "Schema", icon: "Schema", toolTip: "Schema functions", subMenu: this.schemaTools(), expertMode: false, toolType:CollectionViewType.Schema, funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode)`, linearView_isOpen: `SelectedDocType(this.toolType, this.expertMode)`}, linearView_btnWidth:58 }, // Only when Schema is selected
];
}
@@ -874,7 +875,7 @@ pie title Minerals in my tap water
...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit,
color: Colors.WHITE,
_nativeWidth: params.width ?? 30, _width: params.width ?? 30,
- _height: 30, _nativeHeight: 30, linearBtnWidth: params.linearBtnWidth,
+ _height: 30, _nativeHeight: 30, linearView_btnWidth: params.linearView_btnWidth,
toolType: params.toolType, expertMode: params.expertMode,
_dragOnlyWithinContainer: true, _lockedPosition: true,
embedContainer: btnContainer
@@ -893,9 +894,9 @@ pie title Minerals in my tap water
return this.setupContextMenuButton(params, menuBtnDoc, menuDoc);
}
// linear view
- const reqdSubMenuOpts = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, undoIgnoreFields: new List<string>(['width', "linearView_IsOpen"]),
+ const reqdSubMenuOpts = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, undoIgnoreFields: new List<string>(['width', "linearView_isOpen"]),
childDontRegisterViews: true, flexGap: 0, _height: 30, _width: 30, ignoreClick: !params.scripts?.onClick,
- linearView_SubMenu: true, linearView_Expandable: true, embedContainer: menuDoc};
+ linearView_expandable: true, embedContainer: menuDoc};
const items = (menuBtn?:Doc) => !menuBtn ? [] : subMenu.map(sub => this.setupContextMenuBtn(sub, menuBtn) );
const creator = params.btnType === ButtonType.MultiToggleButton ? this.multiToggleList : this.linearButtonList;
@@ -907,7 +908,7 @@ pie title Minerals in my tap water
/// Initializes all the default buttons for the top bar context menu
static setupContextMenuButtons(doc: Doc, field="myContextMenuBtns") {
- const reqdCtxtOpts:DocumentOptions = { title: "context menu buttons", undoIgnoreFields:new List<string>(['width', "linearView_IsOpen"]), flexGap: 0, childDragAction: dropActionType.embed, childDontRegisterViews: true, linearView_IsOpen: true, ignoreClick: true, linearView_Expandable: false, _height: 35 };
+ const reqdCtxtOpts:DocumentOptions = { title: "context menu buttons", undoIgnoreFields:new List<string>(['width', "linearView_isOpen"]), flexGap: 0, childDragAction: dropActionType.embed, childDontRegisterViews: true, linearView_isOpen: true, ignoreClick: true, linearView_expandable: false, _height: 35 };
const ctxtMenuBtnsDoc = DocUtils.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), reqdCtxtOpts, undefined);
const ctxtMenuBtns = CurrentUserUtils.contextMenuTools().map(params => this.setupContextMenuBtn(params, ctxtMenuBtnsDoc) );
return DocUtils.AssignOpts(ctxtMenuBtnsDoc, reqdCtxtOpts, ctxtMenuBtns);
@@ -934,7 +935,7 @@ pie title Minerals in my tap water
const btns = btnDescs.map(desc => dockBtn({_width: desc.opts.width??30, _height: 30, defaultDoubleClick: 'ignore', undoIgnoreFields: new List<string>(['opacity']), _dragOnlyWithinContainer: true, ...desc.opts}, desc.scripts, desc.funcs));
const dockBtnsReqdOpts:DocumentOptions = {
title: "docked buttons", _height: 40, flexGap: 0, layout_boxShadow: "standard", childDragAction: dropActionType.move,
- childDontRegisterViews: true, linearView_IsOpen: true, linearView_Expandable: false, ignoreClick: true
+ childDontRegisterViews: true, linearView_isOpen: true, linearView_expandable: false, ignoreClick: true
};
return DocUtils.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), dockBtnsReqdOpts, btns);
}
@@ -1074,7 +1075,7 @@ pie title Minerals in my tap water
Doc.MyRecentlyClosed && Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyRecentlyClosed);
}
- DocCast(Doc.UserDoc().emptyWebpage) && (Doc.GetProto(DocCast(Doc.UserDoc().emptyWebpage)!).data = new WebField("https://www.wikipedia.org"));
+ DocCast(Doc.UserDoc().emptyWebpage) && (Doc.GetProto(DocCast(Doc.UserDoc().emptyWebpage)!).data = new WebField("https://wikipedia.org"));
DocServer.CacheNeedsUpdate() && setTimeout(UPDATE_SERVER_CACHE, 2500);
setInterval(UPDATE_SERVER_CACHE, 120000);
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 3bae2881e..fc8f22cf3 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -349,7 +349,7 @@ export class DocumentManager {
// bcz: should this delay be an options parameter?
setTimeout(() => {
Doc.linkFollowHighlight(viewSpec ? [docView.Document, viewSpec] : docView.Document, undefined, options.effect);
- const zoomableText = StrCast(targetDoc.text_html, StrCast(targetDoc.ai_firefly_prompt));
+ const zoomableText = StrCast(targetDoc.text_html, StrCast(targetDoc.ai_prompt));
if (options.zoomTextSelections && Doc.IsUnhighlightTimerSet() && contextView && zoomableText) {
// if the docView is a text anchor, the contextView is the PDF/Web/Text doc
contextView.setTextHtmlOverlay(zoomableText, options.effect);
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index b6b111930..1d4779d8a 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -41,7 +41,7 @@ function makeTemplate(doc: Doc, first: boolean = true): boolean {
if (first && !docs.length) {
// bcz: feels hacky : if the root level document has items, it's not a field template
isTemplate = Doc.MakeMetadataFieldTemplate(doc, layoutDoc[DocData], true) || isTemplate;
- } else if (docData instanceof RichTextField || docData instanceof ImageField) {
+ } else if (docData instanceof RichTextField || docData instanceof ImageField || (docData === undefined && doc.type === DocumentType.IMG)) {
if (!StrCast(layoutDoc.title).startsWith('-')) {
isTemplate = Doc.MakeMetadataFieldTemplate(layoutDoc, layoutDoc[DocData], true);
}
@@ -86,6 +86,8 @@ export function makeUserTemplateButtonOrImage(doc: Doc, image?: string) {
dbox.dragFactory = layoutDoc;
dbox.dropPropertiesToRemove = doc.dropPropertiesToRemove instanceof ObjectField ? ObjectField.MakeCopy(doc.dropPropertiesToRemove) : undefined;
dbox.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory)');
+ const userTemplatesDoc = DocCast(Doc.UserDoc().template_user);
+ userTemplatesDoc && Doc.AddDocToList(userTemplatesDoc, 'data', layoutDoc);
return dbox;
}
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index d8e0c4cbe..6461605a6 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -265,10 +265,10 @@ export function UPDATE_SERVER_CACHE() {
cacheDocumentIds = newCacheUpdate;
// print out cached docs
- Doc.MyDockedBtns?.linearView_IsOpen && console.log('Set cached docs = ');
+ Doc.MyDockedBtns?.linearView_isOpen && console.log('Set cached docs = ');
const isFiltered = filtered.filter(doc => !Doc.IsSystem(doc));
const strings = isFiltered.map(doc => StrCast(doc.title) + ' ' + (Doc.IsDataProto(doc) ? '(data)' : '(embedding)'));
- Doc.MyDockedBtns?.linearView_IsOpen && strings.sort().forEach((str, i) => console.log(i.toString() + ' ' + str));
+ Doc.MyDockedBtns?.linearView_isOpen && strings.sort().forEach((str, i) => console.log(i.toString() + ' ' + str));
rp.post(ClientUtils.prepend('/setCacheDocumentIds'), {
body: {
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 0136f6abe..253db08de 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -497,8 +497,8 @@ export class InkingStroke extends ViewBoxAnnotatableComponent<FieldViewProps>()
{...this._props}
setHeight={undefined}
setContentViewBox={this.setSubContentView} // this makes the inkingStroke the "dominant" component - ie, it will show the inking UI when selected (not text)
- yPadding={10}
- xPadding={10}
+ yMargin={10}
+ xMargin={10}
fieldKey="text"
// dontRegisterView={true}
noSidebar
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index ad6bb09c7..c49b7e6de 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -173,7 +173,7 @@ export class MainView extends ObservableReactComponent<object> {
views => views.length > 1 && document.activeElement instanceof HTMLElement && document.activeElement?.blur()
);
reaction(
- () => Doc.MyDockedBtns?.linearView_IsOpen,
+ () => Doc.MyDockedBtns?.linearView_isOpen,
open => SnappingManager.SetPrintToConsole(!!open)
);
const scriptTag = document.createElement('script');
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index e4811a902..b2e42652d 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -198,6 +198,7 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
const targetCreator = (annotationOn: Doc | undefined) => {
const target = DocUtils.GetNewTextDoc('Note linked to ' + this.props.Document.title, 0, 0, 100, 100, annotationOn, 'yellow');
+ target.layout_fitWidth = true;
DocumentView.SetSelectOnLoad(target);
return target;
};
diff --git a/src/client/views/ObservableReactComponent.tsx b/src/client/views/ObservableReactComponent.tsx
index 2290516dc..cf4bf991e 100644
--- a/src/client/views/ObservableReactComponent.tsx
+++ b/src/client/views/ObservableReactComponent.tsx
@@ -16,15 +16,13 @@ export abstract class ObservableReactComponent<T> extends React.Component<T, obj
makeObservable(this);
}
__passiveWheel: HTMLElement | null = null;
- _isContentActive: () => boolean | undefined = () => false;
+ __isContentActive: () => boolean | undefined = () => false;
/**
* default method to stop wheel events from bubbling up to parent components.
* @param e
*/
- onPassiveWheel = (e: WheelEvent) => {
- if (this._isContentActive?.()) e.stopPropagation();
- };
+ onPassiveWheel = (e: WheelEvent) => this.__isContentActive?.() && e.stopPropagation();
/**
* This fixes the problem where a component uses wheel events to scroll, but is nested inside another component that
@@ -36,7 +34,7 @@ export abstract class ObservableReactComponent<T> extends React.Component<T, obj
* @param onPassiveWheel an optional function to call to handle the wheel event (and block its propagation. If omitted, the event won't propagate.
*/
fixWheelEvents = (ele: HTMLElement | null, isContentActive: () => boolean | undefined, onPassiveWheel?: (e: WheelEvent) => void) => {
- this._isContentActive = isContentActive;
+ this.__isContentActive = isContentActive;
this.__passiveWheel?.removeEventListener('wheel', onPassiveWheel ?? this.onPassiveWheel);
this.__passiveWheel = ele;
ele?.addEventListener('wheel', onPassiveWheel ?? this.onPassiveWheel, { passive: false });
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 28566ba1d..ded342df0 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -165,7 +165,7 @@ export class PropertiesButtons extends React.Component {
// // containerDoc._forceActive =
// //containerDoc._freeform_fitContentsToBox =
// containerDoc._isLightbox = !containerDoc._isLightbox;
- // //containerDoc._xPadding = containerDoc._yPadding = containerDoc._isLightbox ? 10 : undefined;
+ // //containerDoc._xMargin = containerDoc._yMargin = containerDoc._isLightbox ? 10 : undefined;
// const containerContents = DocListCast(dv.dataDoc[dv.props.fieldKey ?? Doc.LayoutFieldKey(containerDoc)]);
// //dv.Document.onClick = ScriptField.MakeScript('{this.data = undefined; documentView.select(false)}', { documentView: 'any' });
// containerContents.forEach(doc => LinkManager.Links(doc).forEach(link => (link.layout_linkDisplay = false)));
diff --git a/src/client/views/ViewBoxInterface.ts b/src/client/views/ViewBoxInterface.ts
index 0ddac8914..d8dab8e89 100644
--- a/src/client/views/ViewBoxInterface.ts
+++ b/src/client/views/ViewBoxInterface.ts
@@ -64,5 +64,4 @@ export abstract class ViewBoxInterface<P> extends ObservableReactComponent<React
dontRegisterView?: () => boolean; // KeyValueBox's don't want to register their views
isUnstyledView?: () => boolean; // SchemaView and KeyValue are unstyled -- not titles, no opacity, no animations
componentAIView?: () => JSX.Element;
- componentAIViewHistory?: () => JSX.Element;
}
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index d5edc3e0b..3dc7bc515 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -133,7 +133,7 @@ export class CollectionCardView extends CollectionSubView() {
}
@computed get yMargin() {
- return this._props.yPadding || NumCast(this.layoutDoc._yMargin, Math.min(5, 0.05 * this._props.PanelWidth()));
+ return this._props.yMargin || NumCast(this.layoutDoc._yMargin, Math.min(5, 0.05 * this._props.PanelWidth()));
}
@computed get cardDeckWidth() {
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index ac1981012..3c5bc10de 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -206,7 +206,7 @@ export class CollectionCarouselView extends CollectionSubView() {
marginLeft: this.captionMarginX,
width: `calc(100% - ${this.captionMarginX * 2}px)`,
}}>
- <FormattedTextBox xPadding={10} yPadding={10} {...captionProps} fieldKey={carouselShowsCaptions} styleProvider={this.captionStyleProvider} Document={this.curDoc()} TemplateDataDocument={undefined} />
+ <FormattedTextBox xMargin={10} yMargin={10} {...captionProps} fieldKey={carouselShowsCaptions} styleProvider={this.captionStyleProvider} Document={this.curDoc()} TemplateDataDocument={undefined} />
</div>
)}
</>
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index be570564b..25a222cbb 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -89,7 +89,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
return NumCast(this.layoutDoc._xMargin, Math.max(3, 0.05 * this._props.PanelWidth()));
}
@computed get yMargin() {
- return this._props.yPadding || NumCast(this.layoutDoc._yMargin, Math.min(5, 0.05 * this._props.PanelWidth()));
+ return this._props.yMargin || NumCast(this.layoutDoc._yMargin, Math.min(5, 0.05 * this._props.PanelWidth()));
}
@computed get gridGap() {
@@ -381,8 +381,8 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
hideDecorations={this._props.childHideDecorations}
childFiltersByRanges={this.childDocRangeFilters}
searchFilterDocs={this.searchFilterDocs}
- xPadding={NumCast(this.layoutDoc._childXPadding, this._props.childXPadding)}
- yPadding={NumCast(this.layoutDoc._childYPadding, this._props.childYPadding)}
+ xMargin={NumCast(this.layoutDoc._childXPadding, this._props.childXPadding)}
+ yMargin={NumCast(this.layoutDoc._childYPadding, this._props.childYPadding)}
rejectDrop={this._props.childRejectDrop}
addDocument={this._props.addDocument}
moveDocument={this._props.moveDocument}
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 994669734..345f60e75 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -71,6 +71,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
}
_ele: HTMLElement | null = null;
+ _eleMasonrySingle = React.createRef<HTMLDivElement>();
protected onInternalPreDrop = (e: Event, de: DragManager.DropEvent, targetDropAction: dropActionType) => {
const dragData = de.complete.docDragData;
@@ -92,13 +93,13 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
createColumnDropRef = (ele: HTMLDivElement | null) => {
this.dropDisposer?.();
if (ele) this.dropDisposer = DragManager.MakeDropTarget(ele, this.columnDrop.bind(this), this._props.Doc, this.onInternalPreDrop.bind(this));
- else if (this._ele) this.props.refList.splice(this.props.refList.indexOf(this._ele), 1);
+ else if (this._eleMasonrySingle.current) this.props.refList.splice(this.props.refList.indexOf(this._eleMasonrySingle.current), 1);
this._ele = ele;
};
@action
componentDidMount() {
- this._ele && this.props.refList.push(this._ele);
+ this._eleMasonrySingle.current && this.props.refList.push(this._eleMasonrySingle.current);
this._disposers.collapser = reaction(
() => this._props.headingObject?.collapsed,
collapsed => { this.collapsed = collapsed !== undefined ? BoolCast(collapsed) : false; }, // prettier-ignore
@@ -363,6 +364,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
}}>
<div
key={`${heading}-stack`}
+ ref={this._eleMasonrySingle}
className="collectionStackingView-masonrySingle"
style={{
padding: `${columnYMargin}px ${0}px ${this._props.yMargin}px ${0}px`,
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index e79d0a76d..01a8da313 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -9,7 +9,7 @@ import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { listSpec } from '../../../fields/Schema';
import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, DateCast, NumCast, ScriptCast, StrCast, toList } from '../../../fields/Types';
+import { BoolCast, Cast, DateCast, DocCast, NumCast, ScriptCast, StrCast, toList } from '../../../fields/Types';
import { WebField } from '../../../fields/URLField';
import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
import { GestureUtils } from '../../../pen-gestures/GestureUtils';
@@ -356,12 +356,16 @@ export function CollectionSubView<X>() {
return !!added;
}
if (de.complete.annoDragData) {
- const dropCreator = de.complete.annoDragData.dropDocCreator;
- de.complete.annoDragData.dropDocCreator = () => {
- const dropped = dropCreator(this._props.isAnnotationOverlay ? this.Document : undefined);
- this.addDocument(dropped);
- return dropped;
- };
+ if (![de.complete.annoDragData.dragDocument.embedContainer, de.complete.annoDragData.dragDocument].includes(this.Document)) {
+ de.complete.annoDragData.dropDocCreator = () => this.getAnchor?.(true) || this.Document;
+ } else {
+ const dropCreator = de.complete.annoDragData.dropDocCreator;
+ de.complete.annoDragData.dropDocCreator = () => {
+ const dropped = dropCreator(this._props.isAnnotationOverlay ? this.Document : undefined);
+ this.addDocument(dropped);
+ return dropped;
+ };
+ }
return true;
}
return false;
@@ -415,7 +419,7 @@ export function CollectionSubView<X>() {
const tags = html.split('<');
if (tags[0] === '') tags.splice(0, 1);
let img = tags[0].startsWith('img') ? tags[0] : tags.length > 1 && tags[1].startsWith('img') ? tags[1] : '';
- const cors = img.includes('corsProxy') ? img.match(/http.*corsProxy\//)![0] : '';
+ const cors = img.includes('corsproxy') ? img.match(/http.*corsproxy\//)![0] : '';
img = cors ? img.replace(cors, '') : img;
if (img) {
const imgSrc = img.split('src="')[1].split('"')[0];
@@ -561,7 +565,17 @@ export function CollectionSubView<X>() {
}
const loading = Docs.Create.LoadingDocument(file, options);
Doc.addCurrentlyLoading(loading);
- DocUtils.uploadFileToDoc(file, {}, loading);
+ DocUtils.uploadFileToDoc(file, {}, loading).then(d => {
+ if (d && d?.type === DocumentType.IMG) {
+ const imgTemplate = DocCast(Doc.UserDoc().defaultImageLayout);
+ if (imgTemplate) {
+ const templateFieldKey = StrCast(imgTemplate.title);
+ d.layout_fieldKey = templateFieldKey;
+ d[templateFieldKey] = imgTemplate;
+ }
+ }
+ });
+
return loading;
})
))
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 4348bc7dc..c4373aaa7 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -163,8 +163,8 @@ export class TabMinimapView extends ObservableReactComponent<TabMinimapViewProps
childFiltersByRanges={CollectionDockingView.Instance?.childDocRangeFilters ?? returnEmptyFilter}
searchFilterDocs={CollectionDockingView.Instance?.searchFilterDocs ?? returnEmptyDoclist}
fitContentsToBox={returnTrue}
- xPadding={this.xPadding}
- yPadding={this.yPadding}
+ xMargin={this.xPadding}
+ yMargin={this.yPadding}
/>
<div className="miniOverlay" onPointerDown={this.miniDown}>
<TabMiniThumb miniLeft={miniLeft} miniTop={miniTop} miniWidth={miniWidth} miniHeight={miniHeight} />
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index fb2d0955f..5b2f1ff81 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -1042,8 +1042,8 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
disableBrushing={this.treeView._props.disableBrushing}
hideLinkButton={BoolCast(this.treeView.Document.childHideLinkButton)}
dontRegisterView={BoolCast(this.treeView.Document.childDontRegisterViews, this._props.dontRegisterView)}
- xPadding={NumCast(this.treeView.Document.childXPadding, this.treeView._props.childXPadding)}
- yPadding={NumCast(this.treeView.Document.childYPadding, this.treeView._props.childYPadding)}
+ xMargin={NumCast(this.treeView.Document.childXPadding, this.treeView._props.childXPadding)}
+ yMargin={NumCast(this.treeView.Document.childYPadding, this.treeView._props.childYPadding)}
childFilters={returnEmptyFilter}
childFiltersByRanges={returnEmptyFilter}
searchFilterDocs={returnEmptyDoclist}
@@ -1148,8 +1148,8 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
moveDocument={this.move}
removeDocument={this._props.removeDoc}
whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
- xPadding={NumCast(this.treeView.Document.childXPadding, this.treeView._props.childXPadding)}
- yPadding={NumCast(this.treeView.Document.childYPadding, this.treeView._props.childYPadding)}
+ xMargin={NumCast(this.treeView.Document.childXPadding, this.treeView._props.childXPadding)}
+ yMargin={NumCast(this.treeView.Document.childYPadding, this.treeView._props.childYPadding)}
addDocTab={this._props.addDocTab}
pinToPres={this.treeView._props.pinToPres}
disableBrushing={this.treeView._props.disableBrushing}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 93314e383..5bbe93a90 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -258,8 +258,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
override contentBounds = () => {
const { x, y, r, b } = aggregateBounds(
this._layoutElements.filter(e => e.bounds?.width && !e.bounds.z).map(e => e.bounds!),
- NumCast(this.layoutDoc._xPadding, NumCast(this.layoutDoc._xMargin, this._props.xPadding ?? 0)),
- NumCast(this.layoutDoc._yPadding, NumCast(this.layoutDoc._yMargin, this._props.yPadding ?? 0))
+ NumCast(this.layoutDoc._xMargin, this._props.xMargin ?? 0),
+ NumCast(this.layoutDoc._yMargin, this._props.yMargin ?? 0)
);
const [width, height] = [r - x, b - y];
return {
@@ -1261,15 +1261,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
/**
* Adds the created drawing to the freeform canvas and sets the metadata.
*/
- addDrawing = (doc: Doc, opts: DrawingOptions, gptRes: string, x?: number, y?: number) => {
- doc.$title = opts.text;
- doc.$width = opts.size;
- doc.$ai_drawing_input = opts.text;
- doc.$ai_drawing_complexity = opts.complexity;
- doc.$ai_drawing_colored = opts.autoColor;
- doc.$ai_drawing_size = opts.size;
- doc.$ai_drawing_data = gptRes;
- doc.$ai = 'gpt';
+ addDrawing = (doc: Doc, opts: DrawingOptions, x?: number, y?: number) => {
+ doc.$ai_prompt = opts.text;
this._drawingContainer = doc;
if (x !== undefined && y !== undefined) {
[doc.x, doc.y] = this.screenToFreeformContentsXf.transformPoint(x, y);
@@ -1815,7 +1808,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
() => {
if (this.Document.isGroup && this.childDocs.length === this.childDocList?.length) {
const clist = this.childDocs.map(cd => ({ x: NumCast(cd.x), y: NumCast(cd.y), width: NumCast(cd._width), height: NumCast(cd._height) }));
- return aggregateBounds(clist, NumCast(this.layoutDoc._xPadding), NumCast(this.layoutDoc._yPadding));
+ return aggregateBounds(clist, NumCast(this.layoutDoc._xMargin), NumCast(this.layoutDoc._yMargin));
}
return undefined;
},
@@ -2017,7 +2010,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
event: action(() => {
SmartDrawHandler.Instance.AddDrawing = this.addDrawing;
SmartDrawHandler.Instance.RemoveDrawing = this.removeDrawing;
- !SmartDrawHandler.Instance.ShowRegenerate ? SmartDrawHandler.Instance.displayRegenerate(this._downX, this._downY - 10) : SmartDrawHandler.Instance.hideRegenerate();
+ !SmartDrawHandler.Instance.ShowRegenerate ? SmartDrawHandler.Instance.displayRegenerate(this._downX, this._downY - 10, NumCast(this.layoutDoc[this.scaleFieldKey])) : SmartDrawHandler.Instance.hideRegenerate();
}),
icon: 'pen-to-square',
});
@@ -2222,9 +2215,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
placeholder={this._drawingFillInput || StrCast(this.Document.title) || 'Describe image'}
type="text"
value={this._drawingFillInput}
- onChange={action(e => {
- this._drawingFillInput = e.target.value;
- })}
+ onChange={action(e => (this._drawingFillInput = e.target.value))}
/>
<div className="collectionFreeFormView-aiView-strength">
<span className="collectionFreeFormView-aiView-similarity">Similarity</span>
@@ -2253,11 +2244,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
onClick={undoable(
action(() => {
this._drawingFillLoading = true;
- DrawingFillHandler.drawingToImage(this.props.Document, this._fireflyRefStrength, this._drawingFillInput || StrCast(this.Document.title))?.then(
- action(() => {
- this._drawingFillLoading = false;
- })
- );
+ DrawingFillHandler.drawingToImage(this.props.Document, this._fireflyRefStrength, this._drawingFillInput || StrCast(this.Document.title))?.then(action(() => (this._drawingFillLoading = false)));
}),
'create image'
)}
@@ -2265,37 +2252,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
</div>
</div>
</div>
- <div className="collectionfreeformview-aiView-regenerate-container">
- <span className="collectionfreeformview-aiView-subtitle">Regenerate</span>
- <div className="collectionfreeformview-aiView-regenerate">
- <input
- className="collectionfreeformview-aiView-input"
- aria-label="Edit instructions input"
- type="text"
- value={this._regenInput}
- onChange={action(e => {
- this._regenInput = e.target.value;
- })}
- placeholder="..under development.."
- />
- <div className="collectionFreeFormView-aiView-regenBtn">
- <Button
- text="Regenerate"
- type={Type.SEC}
- icon={this._regenLoading ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <AiOutlineSend />}
- iconPlacement="right"
- // onClick={action(async () => {
- // this._regenLoading = true;
- // SmartDrawHandler.Instance.CreateDrawingDoc = this.createDrawingDoc;
- // SmartDrawHandler.Instance.AddDrawing = this.addDrawing;
- // SmartDrawHandler.Instance.RemoveDrawing = this.removeDrawing;
- // await SmartDrawHandler.Instance.regenerate([this.Document], undefined, undefined, this._regenInput, true);
- // this._regenLoading = false;
- // })}
- />
- </div>
- </div>
- </div>
</div>
);
};
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index 1d5e70be7..b837b3a86 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -58,7 +58,7 @@ export class CollectionGridView extends CollectionSubView() {
return NumCast(this.layoutDoc._xMargin, Math.max(3, 0.05 * this._props.PanelWidth()));
}
@computed get yMargin() {
- return this._props.yPadding || NumCast(this.layoutDoc._yMargin, Math.min(5, 0.05 * this._props.PanelWidth()));
+ return this._props.yMargin || NumCast(this.layoutDoc._yMargin, Math.min(5, 0.05 * this._props.PanelWidth()));
}
@computed get gridGap() {
return NumCast(this.Document._gridGap, 10);
@@ -206,7 +206,7 @@ export class CollectionGridView extends CollectionSubView() {
setContentViewBox={emptyFunction}
whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
onClickScript={this.onChildClickHandler}
- dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as 'x' | 'y' | 'xy'}
+ dontCenter={StrCast(this.layoutDoc.layout_dontCenter, StrCast(childLayout.layout_dontCenter)) as 'x' | 'y' | 'xy'}
showTags={BoolCast(this.layoutDoc.showChildTags) || BoolCast(this.Document._layout_showTags)}
/>
);
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index 3c2a99b1e..d0a1e6f0d 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -27,7 +27,7 @@ import './CollectionLinearView.scss';
/**
* CollectionLinearView is the class for rendering the horizontal collection
* of documents, it useful for horizontal menus. It can either be expandable
- * or not using the linearView_Expandable field.
+ * or not using the linearView_expandable field.
* It is used in the following locations:
* - It is used in the popup menu on the bottom left (see docButtons() in MainView.tsx)
* - It is used for the context sensitive toolbar at the top (see contMenuButtons() in CollectionMenu.tsx)
@@ -52,7 +52,7 @@ export class CollectionLinearView extends CollectionSubView() {
componentDidMount() {
this._widthDisposer = reaction(
- () => 5 + NumCast(this.dataDoc.linearBtnWidth, this.dimension()) + (this.layoutDoc.linearView_IsOpen ? this.childDocs.filter(doc => !doc.hidden).reduce((tot, doc) => (NumCast(doc._width) || this.dimension()) + tot + 4, 0) : 0),
+ () => 5 + NumCast(this.dataDoc.linearView_btnWidth, this.dimension()) + (this.layoutDoc.linearView_isOpen ? this.childDocs.filter(doc => !doc.hidden).reduce((tot, doc) => (NumCast(doc._width) || this.dimension()) + tot + 4, 0) : 0),
width => {
this.childDocs.length && (this.layoutDoc._width = width);
},
@@ -208,7 +208,7 @@ export class CollectionLinearView extends CollectionSubView() {
render() {
const flexDir = StrCast(this.Document.flexDirection); // Specify direction of linear view content
const flexGap = NumCast(this.Document.flexGap); // Specify the gap between linear view content
- const isExpanded = BoolCast(this.layoutDoc.linearView_IsOpen);
+ const isExpanded = BoolCast(this.layoutDoc.linearView_isOpen);
const menuOpener = (
<Toggle
@@ -219,9 +219,9 @@ export class CollectionLinearView extends CollectionSubView() {
type={Type.TERT}
onPointerDown={e => e.stopPropagation()}
toggleType={ToggleType.BUTTON}
- toggleStatus={BoolCast(this.layoutDoc.linearView_IsOpen)}
+ toggleStatus={BoolCast(this.layoutDoc.linearView_isOpen)}
onClick={() => {
- this.layoutDoc.linearView_IsOpen = !isExpanded;
+ this.layoutDoc.linearView_isOpen = !isExpanded;
ScriptCast(this.Document.onClick)?.script.run({ this: this.Document }, console.log);
}}
tooltip={isExpanded ? 'Close' : 'Open'}
@@ -231,10 +231,10 @@ export class CollectionLinearView extends CollectionSubView() {
);
return (
- <div className={`collectionLinearView-outer ${this.layoutDoc.linearView_SubMenu}`} style={{ backgroundColor: this.layoutDoc.linearView_IsOpen ? undefined : 'transparent' }}>
+ <div className="collectionLinearView-outer" style={{ backgroundColor: this.layoutDoc.linearView_isOpen ? undefined : 'transparent' }}>
<div className="collectionLinearView" ref={this.createDashEventsTarget} onContextMenu={this.myContextMenu} style={{ minHeight: this.dimension(), pointerEvents: 'all' }}>
- {!this.layoutDoc.linearView_Expandable ? null : menuOpener}
- {!this.layoutDoc.linearView_IsOpen ? null : (
+ {!this.layoutDoc.linearView_expandable ? null : menuOpener}
+ {!this.layoutDoc.linearView_isOpen && this.layoutDoc.linearView_expandable ? null : (
<div
className="collectionLinearView-content"
style={{
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index c06391f35..6442385c0 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -359,7 +359,7 @@ export class CollectionSchemaView extends CollectionSubView() {
@action
addNewKey = (key: string, defaultVal: FieldType | undefined) => {
this.childDocs.forEach(doc => {
- doc[DocData][key] = defaultVal;
+ if (doc[DocData][key] === undefined) doc[DocData][key] = defaultVal;
});
};
diff --git a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
index 16d33eb93..134f2ed31 100644
--- a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
@@ -115,12 +115,11 @@ export class SchemaColumnHeader extends ObservableReactComponent<SchemaColumnHea
};
const readOnly = this.getFinfo(fieldKey)?.readOnly ?? false;
const cursor = !readOnly ? 'text' : 'default';
- const pointerEvents: 'all' | 'none' = 'all';
- return { color, fieldProps, cursor, pointerEvents };
+ return { color, fieldProps, cursor };
};
@computed get editableView() {
- const { color, fieldProps, pointerEvents } = this.renderProps(this._props);
+ const { color, fieldProps } = this.renderProps(this._props);
return (
<div
@@ -132,7 +131,6 @@ export class SchemaColumnHeader extends ObservableReactComponent<SchemaColumnHea
style={{
color,
width: '100%',
- pointerEvents,
}}>
<EditableView
ref={r => {
@@ -232,6 +230,7 @@ export class SchemaColumnHeader extends ObservableReactComponent<SchemaColumnHea
className="schema-column-header"
style={{
width: this._props.columnWidths[this._props.columnIndex],
+ pointerEvents: this.props.isContentActive() ? undefined : 'none',
}}
onPointerEnter={() => {
this.handlePointerEnter();
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index f4beb1004..cb3adae10 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -156,6 +156,11 @@ ScriptingGlobals.add(function setDefaultTemplate(checkResult?: boolean) {
});
// toggle: Set overlay status of selected document
// eslint-disable-next-line prefer-arrow-callback
+ScriptingGlobals.add(function setDefaultImageTemplate(checkResult?: boolean) {
+ return DocumentView.setDefaultImageTemplate(checkResult);
+});
+// toggle: Set overlay status of selected document
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boolean) {
if (checkResult) {
return DocumentView.Selected().length ? StrCast(DocumentView.SelectedDocs().lastElement().layout_headingColor) : Doc.SharingDoc()?.headingColor;
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 940c4cb99..3805b0dca 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -148,7 +148,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
public static getValues(doc: Doc, time: number, fillIn: boolean = true) {
return CollectionFreeFormDocumentView.animFields.reduce(
(p, val) => {
- p[val.key] = Cast(doc[`${val.key}_indexed`], listSpec('number'), fillIn ? [NumCast(doc[val.key], val.val)] : []).reduce(
+ p[val.key] = Cast(doc[`${val.key}_indexed`], listSpec('number'), fillIn ? [NumCast(doc[val.key], val.val)] : [])!.reduce(
(prev, v, i) => ((i <= Math.round(time) && v !== undefined) || prev === undefined ? v : prev),
undefined as unknown as number
);
@@ -161,7 +161,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
public static getStringValues(doc: Doc, time: number) {
return CollectionFreeFormDocumentView.animStringFields.reduce(
(p, val) => {
- p[val] = Cast(doc[`${val}_indexed`], listSpec('string'), [StrCast(doc[val])]).reduce((prev, v, i) => ((i <= Math.round(time) && v !== undefined) || prev === undefined ? v : prev), undefined as unknown as string);
+ p[val] = Cast(doc[`${val}_indexed`], listSpec('string'), [StrCast(doc[val])])!.reduce((prev, v, i) => ((i <= Math.round(time) && v !== undefined) || prev === undefined ? v : prev), undefined as unknown as string);
return p;
},
{} as { [val: string]: Opt<string> }
@@ -171,7 +171,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
public static setStringValues(time: number, d: Doc, vals: { [val: string]: Opt<string> }) {
const timecode = Math.round(time);
Object.keys(vals).forEach(val => {
- const findexed = Cast(d[`${val}_indexed`], listSpec('string'), []).slice();
+ const findexed = Cast(d[`${val}_indexed`], listSpec('string'), [])!.slice();
findexed[timecode] = vals[val] || '';
d[`${val}_indexed`] = new List<string>(findexed);
});
@@ -180,7 +180,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
public static setValues(time: number, d: Doc, vals: { [val: string]: Opt<number> }) {
const timecode = Math.round(time);
Object.keys(vals).forEach(val => {
- const findexed = Cast(d[`${val}_indexed`], listSpec('number'), []).slice();
+ const findexed = Cast(d[`${val}_indexed`], listSpec('number'), [])!.slice();
findexed[timecode] = vals[val] as unknown as number;
d[`${val}_indexed`] = new List<number>(findexed);
});
@@ -204,15 +204,15 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
docs.forEach(doc => {
this.animFields.forEach(val => {
const findexed = Cast(doc[`${val.key}_indexed`], listSpec('number'), null);
- findexed?.length <= timecode + 1 && findexed.push(undefined as unknown as number);
+ (findexed?.length ?? 0) <= timecode + 1 && findexed?.push(undefined as unknown as number);
});
this.animStringFields.forEach(val => {
const findexed = Cast(doc[`${val}_indexed`], listSpec('string'), null);
- findexed?.length <= timecode + 1 && findexed.push(undefined as unknown as string);
+ (findexed?.length ?? 0) <= timecode + 1 && findexed?.push(undefined as unknown as string);
});
this.animDataFields(doc).forEach(val => {
const findexed = Cast(doc[`${val}_indexed`], listSpec(InkField), null);
- findexed?.length <= timecode + 1 && findexed.push(undefined as unknown as InkField);
+ (findexed?.length ?? 0) <= timecode + 1 && findexed?.push(undefined as unknown as InkField);
});
});
return newTimer;
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 5ac66f2cd..c4351a200 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -119,6 +119,7 @@
display: flex;
justify-content: center;
align-items: center;
+ margin: auto;
position: relative; // allows contents to be positioned relative/below title
> .formattedTextBox {
position: absolute; // position a child text box
@@ -302,6 +303,6 @@
background: transparent;
.documentView-editorView-resizer {
- height: 5px;
+ height: 2px;
}
}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 284014e54..05706fe6b 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -648,11 +648,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
rootSelected = () => this._rootSelected;
panelHeight = () => this._props.PanelHeight() - this.headerMargin - 2 * NumCast(this.Document.borderWidth);
- screenToLocalContent = () =>
- this._props
- .ScreenToLocalTransform()
- .translate(-NumCast(this.Document.borderWidth), -this.headerMargin - NumCast(this.Document.borderWidth))
- .scale(this.viewingAiEditor() ? (this._props.PanelHeight() || 1) / this.aiContentsHeight() : 1);
+ aiShift = () => (!this.viewingAiEditor() ? 0 : (this._props.PanelWidth() - this.aiContentsWidth()) / 2);
+ aiScale = () => (this.viewingAiEditor() ? (this._props.PanelHeight() || 1) / this.aiContentsHeight() : 1);
onClickFunc = () => (this.disableClickScriptFunc ? undefined : this.onClickHdlr);
setHeight = (height: number) => { !this._props.suppressSetHeight && (this.layoutDoc._height = Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), height + 2 * NumCast(this.Document.borderWidth))); } // prettier-ignore
setContentView = action((view: ViewBoxInterface<FieldViewProps>) => (this._componentView = view));
@@ -678,7 +675,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
return this._props.styleProvider?.(doc, props, property);
};
- @observable _aiWinHeight = 88;
+ @observable _aiWinHeight = 32;
TagsBtnHeight = 22;
@computed get currentScale() {
@@ -703,33 +700,21 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
@computed get uiBtnScaling() { return Math.max(this.maxWidgetSize / this.TagsBtnHeight, 1) * Math.min(1, this.viewScaling); } // prettier-ignore
aiContentsWidth = () => (this.aiContentsHeight() * (this._props.NativeWidth?.() || 1)) / (this._props.NativeHeight?.() || 1);
- aiContentsHeight = () => Math.max(10, this._props.PanelHeight() - this._aiWinHeight * this.uiBtnScaling);
+ aiContentsHeight = () => Math.max(10, this._props.PanelHeight() - (this._aiWinHeight + (this.tagsOverlayFunc() ? 22 : 0)) * this.uiBtnScaling);
@computed get aiEditor() {
return (
- <>
- <div
- className="documentView-editorView-history"
- ref={r => this.historyRef(this._oldAiWheel, (this._oldAiWheel = r))}
- style={{
- transform: `scale(${this.uiBtnScaling})`,
- height: this.aiContentsHeight() / this.uiBtnScaling,
- width: ((this._props.PanelWidth() - this.aiContentsWidth()) * 0.95) / this.uiBtnScaling,
- }}>
- {this._componentView?.componentAIViewHistory?.() ?? null}
- </div>
- <div
- className="documentView-editorView"
- style={{
- background: SnappingManager.userVariantColor,
- width: `${100 / this.uiBtnScaling}%`, //
- transform: `scale(${this.uiBtnScaling})`,
- }}
- ref={r => this.historyRef(this._oldHistoryWheel, (this._oldHistoryWheel = r))}>
- <div className="documentView-editorView-resizer" />
- {this._componentView?.componentAIView?.() ?? null}
- {this._props.DocumentView?.() ? <TagsView background={this.backgroundBoxColor} Views={[this._props.DocumentView?.()]} /> : null}
- </div>
- </>
+ <div
+ className="documentView-editorView"
+ style={{
+ background: SnappingManager.userVariantColor,
+ width: `${100 / this.uiBtnScaling}%`, //
+ transform: `scale(${this.uiBtnScaling})`,
+ }}
+ ref={r => this.historyRef(this._oldHistoryWheel, (this._oldHistoryWheel = r))}>
+ <div className="documentView-editorView-resizer" />
+ {this._componentView?.componentAIView?.() ?? null}
+ {this._props.DocumentView?.() ? <TagsView background={this.backgroundBoxColor} Views={[this._props.DocumentView?.()]} /> : null}
+ </div>
);
}
@computed get tagsOverlay() {
@@ -755,7 +740,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
}
widgetOverlayFunc = () => (this.widgetDecorations ? this.widgetOverlay : null);
viewingAiEditor = () => (this._props.showAIEditor && this._componentView?.componentAIView?.() !== undefined ? this.aiEditor : null);
- _contentsRef = React.createRef<DocumentContentsView>();
+ @observable _contentsRef: DocumentContentsView | undefined = undefined;
@computed get viewBoxContents() {
TraceMobx();
const isInk = this.layoutDoc._layout_isSvg && !this._props.LayoutTemplateString;
@@ -771,7 +756,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
}}>
<DocumentContentsView
{...this._props}
- ref={this._contentsRef}
+ ref={action((r: DocumentContentsView) => (this._contentsRef = r))}
layoutFieldKey={StrCast(this.Document.layout_fieldKey, 'layout')}
pointerEvents={this.contentPointerEvents}
setContentViewBox={this.setContentView}
@@ -780,7 +765,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
PanelHeight={this.viewingAiEditor() ? this.aiContentsHeight : this.panelHeight}
setHeight={this.setHeight}
isContentActive={this.isContentActive}
- ScreenToLocalTransform={this.screenToLocalContent}
rootSelected={this.rootSelected}
onClickScript={this.onClickFunc}
setTitleFocus={this.setTitleFocus}
@@ -913,8 +897,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
}}>
<FormattedTextBox
{...this._props}
- yPadding={10}
- xPadding={10}
+ yMargin={10}
+ xMargin={10}
fieldKey={this.showCaption}
styleProvider={this.captionStyleProvider}
dontRegisterView
@@ -1184,7 +1168,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
* @returns boolean whether sub-component Doc is in synch with the layoutDoc that this view thinks its rendering
*/
IsInvalid = (renderDoc?: Doc): boolean => {
- const docContents = this._docViewInternal?._contentsRef.current;
+ const docContents = this._docViewInternal?._contentsRef;
return !(
(!renderDoc ||
(docContents?.layoutDoc === renderDoc && //
@@ -1291,7 +1275,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
if (this.ComponentView?.screenBounds?.()) {
return this.ComponentView.screenBounds();
}
- const xf = this.screenToContentsTransform().scale(this.nativeScaling).inverse();
+ const xf = this.screenToContentBoundsTransform().inverse();
const [[left, top], [right, bottom]] = [xf.transformPoint(0, 0), xf.transformPoint(this.panelWidth, this.panelHeight)];
// transition is returned so that the bounds will 'update' at the end of an animated transition. This is needed by xAnchor in LinkBox
@@ -1405,28 +1389,36 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
custom && DocUtils.makeCustomViewClicked(this.Document, Docs.Create.StackingDocument, layout, undefined);
}, 'set custom view');
- public static setDefaultTemplate(checkResult?: boolean) {
- if (checkResult) {
- return Doc.UserDoc().defaultTextLayout;
+ private static getTemplate(view: DocumentView | undefined) {
+ if (view) {
+ if (!view.layoutDoc.isTemplateDoc) {
+ MakeTemplate(view.Document);
+ Doc.AddDocToList(Doc.UserDoc(), 'template_user', view.Document);
+ Doc.AddDocToList(DocListCast(Doc.MyTools?.data)[1], 'data', makeUserTemplateButtonOrImage(view.Document));
+ DocCast(Doc.UserDoc().template_user) && view.Document && Doc.AddDocToList(DocCast(Doc.UserDoc().template_user)!, 'data', view.Document);
+ return view.Document;
+ }
+ return DocCast(Doc.LayoutField(view.Document)) ?? view.Document;
}
+ }
+ public static setDefaultTemplate(checkResult?: boolean) {
+ if (checkResult) return Doc.UserDoc().defaultTextLayout;
const view = DocumentView.Selected()[0]?._props.renderDepth > 0 ? DocumentView.Selected()[0] : undefined;
undoable(() => {
- let tempDoc: Opt<Doc>;
- if (view) {
- if (!view.layoutDoc.isTemplateDoc) {
- tempDoc = view.Document;
- MakeTemplate(tempDoc);
- Doc.AddDocToList(Doc.UserDoc(), 'template_user', tempDoc);
- Doc.AddDocToList(DocListCast(Doc.MyTools?.data)[1], 'data', makeUserTemplateButtonOrImage(tempDoc));
- DocCast(Doc.UserDoc().template_user) && tempDoc && Doc.AddDocToList(DocCast(Doc.UserDoc().template_user)!, 'data', tempDoc);
- } else {
- tempDoc = DocCast(Doc.LayoutField(view.Document));
- }
- }
+ const tempDoc = DocumentView.getTemplate(view);
Doc.UserDoc().defaultTextLayout = tempDoc ? new PrefetchProxy(tempDoc) : undefined;
}, 'set default template')();
return undefined;
}
+ public static setDefaultImageTemplate(checkResult?: boolean) {
+ if (checkResult) return Doc.UserDoc().defaultImageLayout;
+ const view = DocumentView.Selected()[0]?._props.renderDepth > 0 ? DocumentView.Selected()[0] : undefined;
+ undoable(() => {
+ const tempDoc = DocumentView.getTemplate(view);
+ Doc.UserDoc().defaultImageLayout = tempDoc ? new PrefetchProxy(tempDoc) : undefined;
+ }, 'set default image template')();
+ return undefined;
+ }
/**
* This switches between the current view of a Doc and a specified alternate layout view.
@@ -1498,17 +1490,22 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
isHovering = () => this._isHovering;
selfView = () => this;
/**
- * @returns Transform to the document view (in the coordinate system of whatever contains the DocumentView)
+ * @returns Transform to the document view's available panel space (in the coordinate system of whatever contains the DocumentView)
*/
screenToViewTransform = () => this._props.ScreenToLocalTransform();
/**
+ * @returns Transform to the document view after centering in available panel space(in the coordinate system of whatever contains the DocumentView)
+ */
+ private screenToContentBoundsTransform = () => this.screenToViewTransform().translate(-this.centeringX, -this.centeringY);
+ /**
* @returns Transform to the coordinate system of the contents of the document view (includes native dimension scaling and centering)
*/
screenToContentsTransform = () =>
this._props
.ScreenToLocalTransform()
.translate(-this.centeringX, -this.centeringY)
- .scale(1 / this.nativeScaling);
+ .translate(-(this._docViewInternal?.aiShift() ?? 0), 0)
+ .scale((this._docViewInternal?.aiScale() ?? 1) / this.nativeScaling);
htmlOverlay = () => {
const effect = StrCast(this._htmlOverlayEffect?.presentation_effect, StrCast(this._htmlOverlayEffect?.followLinkAnimEffect));
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index a6872f8dc..f6b405a43 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -52,8 +52,8 @@ export interface FieldViewSharedProps {
renderDepth: number;
scriptContext?: unknown; // can be assigned anything and will be passed as 'scriptContext' to any OnClick script that executes on this document
screenXPadding?: (view: DocumentView | undefined) => number; // padding in screen space coordinates (used by text box to reflow around UI buttons in carouselView)
- xPadding?: number;
- yPadding?: number;
+ xMargin?: number;
+ yMargin?: number;
dontRegisterView?: boolean;
dropAction?: dropActionType;
dragAction?: dropActionType;
diff --git a/src/client/views/nodes/IconTagBox.tsx b/src/client/views/nodes/IconTagBox.tsx
index 0bbd6a0d3..d04ec3a10 100644
--- a/src/client/views/nodes/IconTagBox.tsx
+++ b/src/client/views/nodes/IconTagBox.tsx
@@ -58,16 +58,20 @@ export class IconTagBox extends ObservableReactComponent<IconTagProps> {
const tag = StrCast(key.toolType);
const color = dv._props.styleProvider?.(dv.layoutDoc, dv.ComponentView?._props, StyleProp.FontColor) as string;
return (
- <Toggle
- tooltip={`Click to add/remove the tag ${tag}`}
- toggleStatus={TagItem.docHasTag(dv.Document, tag)}
- toggleType={ToggleType.BUTTON}
- icon={<FontAwesomeIcon className={`fontIconBox-icon-${ToggleType.BUTTON}`} icon={icon} color={color} />}
- size={Size.XSMALL}
- type={Type.PRIM}
- onClick={() => this.setIconTag(tag, !TagItem.docHasTag(this.View.Document, tag))}
- color={color}
- />
+ <div>
+ {' '}
+ {/* tooltips require the wrapped item to be an element ref */}
+ <Toggle
+ tooltip={`Click to add/remove the tag ${tag}`}
+ toggleStatus={TagItem.docHasTag(dv.Document, tag)}
+ toggleType={ToggleType.BUTTON}
+ icon={<FontAwesomeIcon className={`fontIconBox-icon-${ToggleType.BUTTON}`} icon={icon} color={color} />}
+ size={Size.XSMALL}
+ type={Type.PRIM}
+ onClick={() => this.setIconTag(tag, !TagItem.docHasTag(this.View.Document, tag))}
+ color={color}
+ />
+ </div>
);
};
diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss
index 9f7a5d03f..5a6292fab 100644
--- a/src/client/views/nodes/ImageBox.scss
+++ b/src/client/views/nodes/ImageBox.scss
@@ -236,7 +236,7 @@
.imageBox-aiView-input {
overflow: hidden;
text-overflow: ellipsis;
- max-width: 65%;
+ max-width: 80%;
width: 100%;
color: black;
}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index d16baada6..f7ad5c7e2 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -1,6 +1,6 @@
-import { Button, Colors, EditableText, IconButton, Size, Toggle, ToggleType, Type } from '@dash/components';
+import { Button, Colors, EditableText, IconButton, NumberDropdown, Size, Toggle, ToggleType, Type } from '@dash/components';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Slider, Tooltip } from '@mui/material';
+import { Tooltip } from '@mui/material';
import axios from 'axios';
import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
@@ -37,7 +37,6 @@ import { OverlayView } from '../OverlayView';
import { AnchorMenu } from '../pdf/AnchorMenu';
import { PinDocView, PinProps } from '../PinFuncs';
import { DrawingFillHandler } from '../smartdraw/DrawingFillHandler';
-import { FireflyImageData, isFireflyImageData } from '../smartdraw/FireflyConstants';
import { SmartDrawHandler } from '../smartdraw/SmartDrawHandler';
import { StickerPalette } from '../smartdraw/StickerPalette';
import { StyleProp } from '../StyleProp';
@@ -102,7 +101,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@observable private _regenInput = '';
@observable private _canInteract = true;
@observable private _regenerateLoading = false;
- @observable private _prevImgs: FireflyImageData[] = StrCast(this.Document.ai_firefly_history) ? JSON.parse(StrCast(this.Document.ai_firefly_history)) : [];
// Add these observable properties to the ImageBox class
@observable private _outpaintingInProgress = false;
@@ -154,7 +152,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
this._disposers.path = reaction(
() => ({ nativeSize: this.nativeSize, width: NumCast(this.layoutDoc._width), height: this.layoutDoc._height }),
({ nativeSize, width, height }) => {
- if (!this.layoutDoc._layout_nativeDimEditable || !height) {
+ if (!this.layoutDoc._layout_nativeDimEditable || !height || this.layoutDoc.layout_resetNativeDim) {
+ this.layoutDoc.layout_resetNativeDim = undefined; // template images need to reset their dimensions when they are rendered with content. afterwards, remove this flag.
this.layoutDoc._height = (width * nativeSize.nativeHeight) / nativeSize.nativeWidth;
}
},
@@ -227,7 +226,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
const drag = de.complete.docDragData.draggedDocuments.lastElement();
const dragField = drag[Doc.LayoutDataKey(drag)];
const descText = RTFCast(dragField)?.Text || StrCast(dragField) || RTFCast(drag.text)?.Text || StrCast(drag.text) || StrCast(this.Document.title);
- const oldPrompt = StrCast(this.Document.ai_firefly_prompt, StrCast(this.Document.title));
+ const oldPrompt = StrCast(this.Document.ai_prompt, StrCast(this.Document.title));
const newPrompt = (text: string) => (oldPrompt ? `${oldPrompt} ~~~ ${text}` : text);
DrawingFillHandler.drawingToImage(this.Document, 90, newPrompt(descText), drag)?.then(action(() => (this._regenerateLoading = false)));
added = false;
@@ -737,9 +736,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
<div
className="imageBox-regenerateDropTarget"
ref={this._regenerateIconRef}
- onClick={() => DocCast(this.Document.ai_firefly_generatedDocs) && DocumentView.showDocument(DocCast(this.Document.ai_firefly_generatedDocs)!, { openLocation: OpenWhere.addRight })}
+ onClick={() => DocCast(this.Document.ai_generatedDocs) && DocumentView.showDocument(DocCast(this.Document.ai_generatedDocs)!, { openLocation: OpenWhere.addRight })}
style={{
- display: (this._props.isContentActive() && (SnappingManager.CanEmbed || this.Document.ai_firefly_generatedDocs)) || this._regenerateLoading ? 'block' : 'none',
+ display: (this._props.isContentActive() && (SnappingManager.CanEmbed || this.Document.ai_generatedDocs)) || this._regenerateLoading ? 'block' : 'none',
transform: `scale(${this.uiBtnScaling})`,
width: this._sideBtnWidth,
height: this._sideBtnWidth,
@@ -845,34 +844,10 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@observable _filterFunc: ((doc: Doc) => boolean) | undefined = undefined;
@observable private _fireflyRefStrength = 0;
- componentAIViewHistory = () => (
- <div className="imageBox-aiView-history">
- <Button text="Clear History" type={Type.SEC} size={Size.XSMALL} />
- {this._prevImgs.map(img => (
- <div key={img.pathname}>
- <img
- className="imageBox-aiView-img"
- src={ClientUtils.prepend(img.pathname.replace(extname(img.pathname), '_s' + extname(img.pathname)))}
- onClick={() => {
- this.dataDoc[this.fieldKey] = new ImageField(img.pathname);
- this.dataDoc.ai_firefly_prompt = img.prompt;
- this.dataDoc.ai_firefly_seed = img.seed;
- }}
- />
- <span>{img.prompt}</span>
- </div>
- ))}
- </div>
- );
-
componentAIView = () => {
- const field = this.dataDoc[this.fieldKey] instanceof ImageField ? Cast(this.dataDoc[this.fieldKey], ImageField, null) : new ImageField(String(this.dataDoc[this.fieldKey]));
return (
<div className="imageBox-aiView">
<div className="imageBox-aiView-regenerate">
- <span className="imageBox-aiView-firefly" style={{ color: SnappingManager.userColor }}>
- Firefly:
- </span>
<input
style={{ color: SnappingManager.userColor, background: SnappingManager.userBackgroundColor }}
className="imageBox-aiView-input"
@@ -886,57 +861,39 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
<Button
text="Create"
type={Type.TERT}
+ size={Size.XSMALL}
color={SnappingManager.userColor}
background={SnappingManager.userBackgroundColor}
// style={{ alignSelf: 'flex-end' }}
icon={this._regenerateLoading ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <AiOutlineSend />}
iconPlacement="right"
- onClick={action(async () => {
+ onClick={action(() => {
this._regenerateLoading = true;
- if (this._fireflyRefStrength) {
- DrawingFillHandler.drawingToImage(this.Document, this._fireflyRefStrength, this._regenInput || StrCast(this.Document.title))?.then(action(() => (this._regenerateLoading = false)));
- } else {
- SmartDrawHandler.Instance.regenerate([this.Document], undefined, undefined, this._regenInput || StrCast(this.Document.title), true).then(
- action(newImgs => {
- const firstImg = newImgs[0];
- if (isFireflyImageData(firstImg)) {
- const url = firstImg.pathname;
- const imgField = new ImageField(url);
- this._prevImgs.length === 0 &&
- this._prevImgs.push({ prompt: StrCast(this.dataDoc.ai_firefly_prompt), seed: this.dataDoc.ai_firefly_seed as number, href: this.paths.lastElement(), pathname: field?.url.pathname ?? '' });
- this._prevImgs.unshift({ prompt: firstImg.prompt, seed: firstImg.seed, pathname: url });
- this.dataDoc.ai_firefly_history = JSON.stringify(this._prevImgs);
- this.dataDoc.ai_firefly_prompt = firstImg.prompt;
- this.dataDoc[this.fieldKey] = imgField;
- this._regenerateLoading = false;
- this._regenInput = '';
- }
- })
- );
- }
+ DrawingFillHandler.drawingToImage(this.Document, this._fireflyRefStrength, this._regenInput || StrCast(this.Document.title))?.then(action(() => (this._regenerateLoading = false)));
})}
/>
</div>
- </div>
- <div className="imageBox-aiView-strength">
- <span className="imageBox-aiView-similarity" style={{ color: SnappingManager.userColor }}>
- Similarity
- </span>
- <Slider
- className="imageBox-aiView-slider"
- sx={{
- '& .MuiSlider-track': { color: SettingsManager.userColor },
- '& .MuiSlider-rail': { color: SettingsManager.userBackgroundColor },
- '& .MuiSlider-thumb': { color: SettingsManager.userColor, '&.Mui-focusVisible, &:hover, &.Mui-active': { boxShadow: `0px 0px 0px 8px${SettingsManager.userColor.slice(0, 7)}10` } },
- }}
- min={0}
- max={100}
- step={1}
- size="small"
- value={this._fireflyRefStrength}
- onChange={action((e, val) => this._canInteract && (this._fireflyRefStrength = val as number))}
- valueLabelDisplay="auto"
- />
+ <div>
+ <NumberDropdown
+ color={SnappingManager.userColor}
+ background={SnappingManager.userBackgroundColor}
+ numberDropdownType="slider"
+ showPlusMinus={false}
+ formLabel="similarity"
+ tooltip="structure similarity of created images to current image"
+ type={Type.PRIM}
+ width={75}
+ min={0}
+ max={100}
+ number={this._fireflyRefStrength}
+ size={Size.XXSMALL}
+ setNumber={undoable(
+ action(val => this._canInteract && (this._fireflyRefStrength = val as number)),
+ `${this.Document.title} button set from list`
+ )}
+ fillWidth
+ />
+ </div>
</div>
</div>
);
@@ -1092,7 +1049,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
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);
+ !(result instanceof Error) && DocUtils.assignUploadInfo(result, this.dataDoc);
}
disposer();
} else {
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index b08ed84b7..4cbe01b82 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -27,7 +27,7 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
private dropDisposer?: DragManager.DragDropDisposer;
private _timeout: NodeJS.Timeout | undefined;
private _divRef: HTMLDivElement | null = null;
- private _reaction: IReactionDisposer | undefined;
+ private _disposers: { [key: string]: IReactionDisposer } = {};
constructor(props: FieldViewProps) {
super(props);
@@ -43,7 +43,7 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
componentDidMount() {
this._props.setContentViewBox?.(this);
- this._reaction = reaction(
+ this._disposers.active = reaction(
() => this.Title,
() => document.activeElement !== this._divRef && this._forceRerender++
);
@@ -51,7 +51,7 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
componentWillUnMount() {
this._timeout && clearTimeout(this._timeout);
this.setText(this._divRef?.innerText ?? '');
- this._reaction?.();
+ Object.values(this._disposers).forEach(disposer => disposer());
}
@observable _forceRerender = 0;
@@ -171,20 +171,21 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
render() {
TraceMobx();
const boxParams = this.fitTextToBox(undefined); // this causes mobx to trigger re-render when data changes
+ const [xmargin, ymargin] = [NumCast(this.layoutDoc._xMargin), NumCast(this.layoutDoc._uMargin)];
return (
<div className="labelBox-outerDiv" ref={this.createDropTarget} style={{ boxShadow: this.boxShadow }}>
<div
className="labelBox-mainButton"
style={{
backgroundColor: this.backgroundColor,
- color: StrCast(this.layoutDoc._text_fontColor, StrCast(this.layoutDoc._color)),
- fontFamily: StrCast(this.layoutDoc._text_fontFamily, StrCast(Doc.UserDoc().fontFamily)) || 'inherit',
+ color: StrCast(this.layoutDoc[`${this.fieldKey}_fontColor`], StrCast(this.layoutDoc._color)),
+ fontFamily: StrCast(this.layoutDoc[`${this.fieldKey}_fontFamily`], StrCast(Doc.UserDoc().fontFamily)) || 'inherit',
letterSpacing: StrCast(this.layoutDoc.letterSpacing),
- textTransform: StrCast(this.layoutDoc[this.fieldKey + '_transform']) as Property.TextTransform,
- paddingLeft: NumCast(this.layoutDoc._xPadding),
- paddingRight: NumCast(this.layoutDoc._xPadding),
- paddingTop: NumCast(this.layoutDoc._yPadding),
- paddingBottom: NumCast(this.layoutDoc._yPadding),
+ textTransform: StrCast(this.layoutDoc[`${this.fieldKey}_transform`]) as Property.TextTransform,
+ paddingLeft: xmargin,
+ paddingRight: xmargin,
+ paddingTop: ymargin,
+ paddingBottom: ymargin,
width: this._props.PanelWidth(),
height: this._props.PanelHeight(),
whiteSpace: boxParams.multiLine ? 'pre-wrap' : 'pre',
@@ -192,8 +193,8 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
<div
key={this._forceRerender}
style={{
- width: this._props.PanelWidth() - 2 * NumCast(this.layoutDoc._xPadding),
- height: this._props.PanelHeight() - 2 * NumCast(this.layoutDoc._yPadding),
+ width: this._props.PanelWidth() - 2 * xmargin,
+ height: this._props.PanelHeight() - 2 * ymargin,
outline: 'unset !important',
}}
onKeyDown={e => {
@@ -214,12 +215,14 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
this._divRef?.removeEventListener('focusout', this.keepFocus);
this._divRef?.addEventListener('focusout', this.keepFocus);
}}
- onBlur={() => {
+ onBlur={e => {
this._divRef?.removeEventListener('focusout', this.keepFocus);
this.setText(this._divRef?.innerText ?? '');
- RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined, undefined);
- FormattedTextBox.LiveTextUndo?.end();
- FormattedTextBox.LiveTextUndo = undefined;
+ if (!FormattedTextBox.tryKeepingFocus(e.relatedTarget, () => this._divRef?.focus())) {
+ RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined, undefined);
+ FormattedTextBox.LiveTextUndo?.end();
+ FormattedTextBox.LiveTextUndo = undefined;
+ }
}}
dangerouslySetInnerHTML={{
__html: `<span class="textFitted textFitAlignVert" style="display: inline-block; text-align: center; font-size: 100px; height: 0px;">${this.Title?.startsWith('#') ? '' : (this.Title ?? '')}</span>`,
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 83c44c80f..55e6d5596 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -557,8 +557,8 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
NativeHeight={this.sidebarNativeHeightFunc}
PanelHeight={this._props.PanelHeight}
PanelWidth={this.sidebarWidth}
- xPadding={0}
- yPadding={0}
+ xMargin={0}
+ yMargin={0}
viewField={this.SidebarKey}
isAnnotationOverlay={false}
originTopLeft
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 4f02d68d6..603dcad5c 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -335,8 +335,8 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
select={emptyFunction}
isContentActive={emptyFunction}
NativeDimScaling={returnOne}
- xPadding={25}
- yPadding={10}
+ xMargin={25}
+ yMargin={10}
whenChildContentsActiveChanged={emptyFunction}
removeDocument={returnFalse}
moveDocument={returnFalse}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 5603786f0..838dbea9d 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -454,7 +454,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
iframeDown = (e: PointerEvent) => {
this._textAnnotationCreator = undefined;
const sel = this._url ? this._iframe?.contentDocument?.getSelection() : window.document.getSelection();
- if (sel?.empty)
+ if (sel?.empty && !(e.target as any).textContent)
sel.empty(); // Chrome
else if (sel?.removeAllRanges) sel.removeAllRanges(); // Firefox
@@ -509,10 +509,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
try {
href = iframe?.contentWindow?.location.href;
} catch {
- runInAction(() => this._warning++);
+ // runInAction(() => this._warning++);
href = undefined;
}
- let requrlraw = decodeURIComponent(href?.replace(ClientUtils.prepend('') + '/corsProxy/', '') ?? this._url.toString());
+ let requrlraw = decodeURIComponent(href?.replace(ClientUtils.prepend('') + '/corsproxy/', '') ?? this._url.toString());
if (requrlraw !== this._url.toString()) {
if (requrlraw.match(/q=.*&/)?.length && this._url.toString().match(/q=.*&/)?.length) {
const matches = requrlraw.match(/[^a-zA-z]q=[^&]*/g);
@@ -565,9 +565,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
'click',
undoable(
action((e: MouseEvent) => {
- let eleHref = '';
+ let eleHref = (e.target as any)?.outerHTML?.split('"="')[1]?.split('"')[0];
for (let ele = e.target as HTMLElement | Element | null; ele; ele = ele.parentElement) {
- if (ele instanceof HTMLAnchorElement) {
+ if ('href' in ele) {
eleHref = (typeof ele.href === 'string' ? ele.href : eleHref) || (ele.parentElement && 'href' in ele.parentElement ? (ele.parentElement.href as string) : eleHref);
}
}
@@ -576,7 +576,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
const batch = UndoManager.StartBatch('webclick');
e.stopPropagation();
setTimeout(() => {
- this.setData(eleHref.replace(ClientUtils.prepend(''), origin));
+ const url = eleHref.replace(ClientUtils.prepend(''), origin);
+ this.setData(url);
batch.end();
});
if (this._outerRef.current) {
@@ -858,7 +859,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
);
}
if (field instanceof WebField) {
- const url = this.layoutDoc[this.fieldKey + '_useCors'] ? ClientUtils.CorsProxy(this._webUrl) : this._webUrl;
+ const url = this.layoutDoc[this.fieldKey + '_useCors'] ? '/corsproxy/' + this._webUrl : this._webUrl;
const scripts = this.dataDoc[this.fieldKey + '_allowScripts'] || this._webUrl.includes('wikipedia.org') || this._webUrl.includes('google.com') || this._webUrl.startsWith('https://bing');
// if (!scripts) console.log('No scripts for: ' + url);
return (
@@ -1074,15 +1075,15 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
childPointerEvents = () => (this._props.isContentActive() ? 'all' : undefined);
@computed get webpage() {
TraceMobx();
- const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
+ // const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this._props.pointerEvents?.() as Property.PointerEvents | undefined);
- const scale = previewScale * (this._props.NativeDimScaling?.() || 1);
+ // const scale = previewScale * (this._props.NativeDimScaling?.() || 1);
return (
<div
className="webBox-outerContent"
ref={this._outerRef}
style={{
- height: `${100 / scale}%`,
+ height: '100%', //`${100 / scale}%`,
pointerEvents,
}}
// when active, block wheel events from propagating since they're handled by the iframe
@@ -1175,6 +1176,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
className="webBox-container"
style={{
width: `calc(${100 / scale}% - ${!this.SidebarShown ? 0 : ((this.sidebarWidth() - WebBox.sidebarResizerWidth) / scale) * (this._previewWidth ? scale : 1)}px)`,
+ height: `${100 / scale}%`,
transform: `scale(${scale})`,
pointerEvents,
}}
diff --git a/src/client/views/nodes/WebBoxRenderer.js b/src/client/views/nodes/WebBoxRenderer.js
index b727107a9..ef465c453 100644
--- a/src/client/views/nodes/WebBoxRenderer.js
+++ b/src/client/views/nodes/WebBoxRenderer.js
@@ -21,7 +21,7 @@ const ForeignHtmlRenderer = function (styleSheets) {
return window.location.origin + extension;
}
function CorsProxy(url) {
- return prepend('/corsProxy/') + encodeURIComponent(url);
+ return prepend('/corsproxy/') + encodeURIComponent(url);
}
/**
*
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index c51f6c38b..57720baae 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1164,7 +1164,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
};
@computed get tagsHeight() {
- return this.DocumentView?.().showTags ? Math.max(0, 20 - Math.max(this._props.yPadding ?? 0, NumCast(this.layoutDoc._yMargin))) * this.ScreenToLocalBoxXf().Scale : 0;
+ return this.DocumentView?.().showTags ? Math.max(0, 20 - Math.max(this._props.yMargin ?? 0, NumCast(this.layoutDoc._yMargin))) * this.ScreenToLocalBoxXf().Scale : 0;
}
@computed get contentScaling() {
@@ -1224,13 +1224,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
incomingValue => {
if (this.EditorView && this.ApplyingChange !== this.fieldKey) {
if (incomingValue?.data) {
- const updatedState = JSON.parse(incomingValue.data.Data);
+ const updatedState = JSON.parse(incomingValue.data.Data.replace(/\n/g, ''));
if (JSON.stringify(this.EditorView.state.toJSON()) !== JSON.stringify(updatedState)) {
this.EditorView.updateState(EditorState.fromJSON(this.config, updatedState));
this.tryUpdateScrollHeight();
}
} else if (this.EditorView.state.doc.textContent !== (incomingValue?.str ?? '')) {
selectAll(this.EditorView.state, tx => this.EditorView?.dispatch(tx.insertText(incomingValue?.str ?? '')));
+ this.tryUpdateScrollHeight();
}
}
},
@@ -1750,19 +1751,22 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
* When a text box loses focus, it might be because a text button was clicked (eg, bold, italics) or color picker.
* In these cases, force focus back onto the text box.
* @param target
+ * @returns true if focus was kept on the text box, false otherwise
*/
- tryKeepingFocus = (target: Element | null) => {
+ public static tryKeepingFocus(target: Element | null, refocusFunc?: () => void) {
for (let newFocusEle = target instanceof HTMLElement ? target : null; newFocusEle; newFocusEle = newFocusEle?.parentElement) {
// bcz: HACK!! test if parent of new focused element is a UI button (should be more specific than testing className)
if (['fonticonbox', 'antimodeMenu-cont', 'popup-container'].includes(newFocusEle?.className ?? '')) {
- return this.EditorView?.focus(); // keep focus on text box
+ refocusFunc?.(); // keep focus on text box
+ return true;
}
}
- };
+ return false;
+ }
@action
onBlur = (e: React.FocusEvent) => {
- this.tryKeepingFocus(e.relatedTarget);
+ FormattedTextBox.tryKeepingFocus(e.relatedTarget, () => this.EditorView?.focus());
if (this.ProseRef?.children[0] !== e.nativeEvent.target) return;
if (!(this.EditorView?.state.selection instanceof NodeSelection) || this.EditorView.state.selection.node.type !== this.EditorView.state.schema.nodes.footnote) {
const stordMarks = this.EditorView?.state.storedMarks?.slice();
@@ -1880,7 +1884,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
}
};
tryUpdateScrollHeight = () => {
- const margins = 2 * NumCast(this.layoutDoc._yMargin, this._props.yPadding || 0);
+ const margins = 2 * NumCast(this.layoutDoc._yMargin, this._props.yMargin || 0);
const children = this.ProseRef?.children.length ? Array.from(this.ProseRef.children[0].children) : undefined;
if (this.EditorView && children && !SnappingManager.IsDragging) {
const getChildrenHeights = (kids: Element[] | undefined) => kids?.reduce((p, child) => p + toHgt(child), margins) ?? 0;
@@ -2112,8 +2116,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
const rounded = StrCast(this.layoutDoc._layout_borderRounding) === '100%' ? '-rounded' : '';
setTimeout(() => !this._props.isContentActive() && FormattedTextBoxComment.textBox === this && FormattedTextBoxComment.Hide);
- const paddingX = Math.max(NumCast(this.layoutDoc._xMargin), 0, this._props.xPadding ?? 0, this._props.screenXPadding?.(this._props.DocumentView?.()) ?? 0);
- const paddingY = Math.max(NumCast(this.layoutDoc._yMargin), 0, this._props.yPadding ?? 0); // prettier-ignore
+ const paddingX = Math.max(NumCast(this.layoutDoc._xMargin), 0, this._props.xMargin ?? 0, this._props.screenXPadding?.(this._props.DocumentView?.()) ?? 0);
+ const paddingY = Math.max(NumCast(this.layoutDoc._yMargin), 0, this._props.yMargin ?? 0); // prettier-ignore
const styleFromLayout = styleFromLayoutString(this.Document, this._props, scale); // this converts any expressions in the format string to style props. e.g., <FormattedTextBox height='{this._header_height}px' >
return this.isLabel ? (
<LabelBox {...this._props} />
diff --git a/src/client/views/nodes/imageEditor/ImageEditor.tsx b/src/client/views/nodes/imageEditor/ImageEditor.tsx
index 85bd95d15..198b8e713 100644
--- a/src/client/views/nodes/imageEditor/ImageEditor.tsx
+++ b/src/client/views/nodes/imageEditor/ImageEditor.tsx
@@ -281,11 +281,14 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
try {
const canvasOriginalImg = ImageUtility.getCanvasImg(img);
if (!canvasOriginalImg) return;
- const canvasMask = ImageUtility.getCanvasMask(canvas, canvasOriginalImg);
+ const canvasMask = ImageUtility.getCanvasMask(canvas, canvas);
if (!canvasMask) return;
const maskBlob = await ImageUtility.canvasToBlob(canvasMask);
const imgBlob = await ImageUtility.canvasToBlob(canvasOriginalImg);
const res = await ImageUtility.getEdit(imgBlob, maskBlob, input || 'Fill in the image in the same style', 2);
+ if ((res as any).status == 'error') {
+ alert((res as any).message);
+ }
// create first image
if (!newCollectionRef.current) {
diff --git a/src/client/views/nodes/imageEditor/imageEditorUtils/ImageHandler.ts b/src/client/views/nodes/imageEditor/imageEditorUtils/ImageHandler.ts
index 1c6a38a24..d6093c6eb 100644
--- a/src/client/views/nodes/imageEditor/imageEditorUtils/ImageHandler.ts
+++ b/src/client/views/nodes/imageEditor/imageEditorUtils/ImageHandler.ts
@@ -75,7 +75,7 @@ export class ImageUtility {
fd.append('mask', maskBlob, 'mask.png');
fd.append('prompt', prompt);
fd.append('size', '1024x1024');
- fd.append('n', n ? JSON.stringify(n) : '1');
+ fd.append('n', n ? n + '' : '1');
fd.append('response_format', 'b64_json');
try {
@@ -268,14 +268,14 @@ export class ImageUtility {
ctx.drawImage(img, xOffset, 0, width, height);
// draw reflected image padding
- this.drawHorizontalReflection(ctx, canvas, xOffset);
+ // this.drawHorizontalReflection(ctx, canvas, xOffset);
} else {
// vertical padding, y offset
const yOffset = Math.floor((canvasSize - height) / 2);
ctx.drawImage(img, 0, yOffset, width, height);
// draw reflected image padding
- this.drawVerticalReflection(ctx, canvas, yOffset);
+ // this.drawVerticalReflection(ctx, canvas, yOffset);
}
return canvas;
};
diff --git a/src/client/views/nodes/trails/PresSlideBox.tsx b/src/client/views/nodes/trails/PresSlideBox.tsx
index 3dbb3da88..55a655c7a 100644
--- a/src/client/views/nodes/trails/PresSlideBox.tsx
+++ b/src/client/views/nodes/trails/PresSlideBox.tsx
@@ -559,10 +559,10 @@ export class PresSlideBox extends ViewBoxBaseComponent<FieldViewProps>() {
style={{
backgroundColor: presColorBool ? (isSelected ? 'rgba(250,250,250,0.3)' : 'transparent') : isSelected ? Colors.LIGHT_BLUE : 'transparent',
opacity: this._dragging ? 0.3 : 1,
- paddingLeft: NumCast(this.layoutDoc._xPadding, this._props.xPadding),
- paddingRight: NumCast(this.layoutDoc._xPadding, this._props.xPadding),
- paddingTop: NumCast(this.layoutDoc._yPadding, this._props.yPadding),
- paddingBottom: NumCast(this.layoutDoc._yPadding, this._props.yPadding),
+ paddingLeft: NumCast(this.layoutDoc._xMargin, this._props.xMargin),
+ paddingRight: NumCast(this.layoutDoc._xMargin, this._props.xMargin),
+ paddingTop: NumCast(this.layoutDoc._yPadding, this._props.yMargin),
+ paddingBottom: NumCast(this.layoutDoc._yPadding, this._props.yMargin),
}}
onDoubleClick={action(() => {
this.toggleProperties();
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.scss b/src/client/views/pdf/GPTPopup/GPTPopup.scss
index bb43291ee..f6fa45221 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.scss
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.scss
@@ -7,6 +7,11 @@ $highlightedText: #82e0ff;
$inputHeight: 60px;
$headingHeight: 32px;
+.gptPopup-sortBox {
+ display: block;
+ max-height: calc(100% - 45px); // leave room for input
+}
+
.gptPopup-summary-box {
position: fixed;
padding-left: 10px;
@@ -87,6 +92,7 @@ $headingHeight: 32px;
}
.btns-wrapper-gpt {
height: 100%;
+ width: 100%;
display: flex;
justify-content: center;
align-items: center;
@@ -97,7 +103,6 @@ $headingHeight: 32px;
flex-direction: column;
width: 100%;
height: 100%;
- overflow-y: auto;
padding-right: 5px;
}
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index c45d8e052..568e48edf 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -93,7 +93,7 @@ export class GPTPopup extends ObservableReactComponent<object> {
this.onGptResponse = (sortResult: string, questionType: GPTDocCommand, args?: string) => this.processGptResponse(selDoc, this._textToDocMap, sortResult, questionType, args);
this.onQuizRandom = () => this.randomlyChooseDoc(selDoc.Document, hasChildDocs());
this._documentDescriptions = Promise.all(hasChildDocs().map(doc =>
- Doc.getDescription(doc).then(text => this._textToDocMap.set(text.trim(), doc) && `${DescriptionSeperator}${text}${DescriptionSeperator}`)
+ Doc.getDescription(doc).then(text => this._textToDocMap.set(text.replace(/\n/g, ' ').trim(), doc) && `${DescriptionSeperator}${text}${DescriptionSeperator}`)
)).then(docDescriptions => docDescriptions.join()); // prettier-ignore
}
},
@@ -211,7 +211,7 @@ export class GPTPopup extends ObservableReactComponent<object> {
const selView = DocumentView.Selected().lastElement();
const selDoc = selView?.Document;
if (selDoc && (selView._props.renderDepth > 1 || selDoc[Doc.LayoutDataKey(selDoc)] instanceof ImageField)) {
- const oldPrompt = StrCast(selDoc.ai_firefly_prompt, StrCast(selDoc.title));
+ const oldPrompt = StrCast(selDoc.ai_prompt, StrCast(selDoc.title));
const newPrompt = oldPrompt ? `${oldPrompt} ~~~ ${imgDesc}` : imgDesc;
return DrawingFillHandler.drawingToImage(selDoc, 100, newPrompt, selDoc)
.then(action(() => (this._userPrompt = '')))
@@ -406,73 +406,80 @@ export class GPTPopup extends ObservableReactComponent<object> {
scrollToBottom = () => setTimeout(() => this._messagesEndRef.current?.scrollIntoView({ behavior: 'smooth', block: 'end' }), 50);
gptMenu = () => (
- <div className="btns-wrapper-gpt">
- <Button
- tooltip="Ask Firefly to create images"
- text="Ask Firefly"
- onClick={() => this.setMode(GPTPopupMode.FIREFLY)}
- color={SettingsManager.userColor}
- background={SettingsManager.userVariantColor}
- type={Type.TERT}
- style={{
- width: '100%',
- height: '40%',
- textAlign: 'center',
- color: '#ffffff',
- fontSize: '16px',
- marginBottom: '10px',
- }}
- />
- <Button
- tooltip="Ask GPT to sort, tag, define, or filter your Docs!"
- text="Ask GPT"
- onClick={() => this.setMode(GPTPopupMode.USER_PROMPT)}
- color={SettingsManager.userColor}
- background={SettingsManager.userVariantColor}
- type={Type.TERT}
- style={{
- width: '100%',
- height: '40%',
- textAlign: 'center',
- color: '#ffffff',
- fontSize: '16px',
- marginBottom: '10px',
- }}
- />
- <Button
- tooltip="Test your knowledge by verifying answers with ChatGPT"
- text="Take Quiz"
- onClick={() => {
- this._conversationArray = ['Define the selected card!'];
- this.setMode(GPTPopupMode.QUIZ_RESPONSE);
- this.onQuizRandom?.();
- }}
- color={SettingsManager.userColor}
- background={SettingsManager.userVariantColor}
- type={Type.TERT}
- style={{
- width: '100%',
- height: '40%',
- textAlign: 'center',
- color: '#ffffff',
- fontSize: '16px',
- }}
- />
+ <div style={{ display: 'flex', maxHeight: 'calc(100% - 32px)', overflow: 'auto' }}>
+ <div className="btns-wrapper-gpt">
+ <Button
+ tooltip="Ask Firefly to create images"
+ text="Ask Firefly"
+ onClick={() => this.setMode(GPTPopupMode.FIREFLY)}
+ color={SettingsManager.userColor}
+ background={SettingsManager.userVariantColor}
+ type={Type.TERT}
+ style={{
+ width: '100%',
+ height: '40%',
+ textAlign: 'center',
+ color: '#ffffff',
+ fontSize: '16px',
+ marginBottom: '10px',
+ }}
+ />
+ <Button
+ tooltip="Ask GPT to sort, tag, define, or filter your Docs!"
+ text="Ask GPT"
+ onClick={() => this.setMode(GPTPopupMode.USER_PROMPT)}
+ color={SettingsManager.userColor}
+ background={SettingsManager.userVariantColor}
+ type={Type.TERT}
+ style={{
+ width: '100%',
+ height: '40%',
+ textAlign: 'center',
+ color: '#ffffff',
+ fontSize: '16px',
+ marginBottom: '10px',
+ }}
+ />
+ <Button
+ tooltip="Test your knowledge by verifying answers with ChatGPT"
+ text="Take Quiz"
+ onClick={() => {
+ this._conversationArray = ['Define the selected card!'];
+ this.setMode(GPTPopupMode.QUIZ_RESPONSE);
+ this.onQuizRandom?.();
+ }}
+ color={SettingsManager.userColor}
+ background={SettingsManager.userVariantColor}
+ type={Type.TERT}
+ style={{
+ width: '100%',
+ height: '40%',
+ textAlign: 'center',
+ color: '#ffffff',
+ fontSize: '16px',
+ }}
+ />
+ </div>
</div>
);
callGpt = action((mode: GPTPopupMode) => {
this.setGptProcessing(true);
+ const reset = action(() => {
+ this.setGptProcessing(false);
+ this._userPrompt = '';
+ this._quizAnswer = '';
+ });
switch (mode) {
case GPTPopupMode.FIREFLY:
this._fireflyArray.push(this._userPrompt);
- return this.generateFireflyImage(this._userPrompt).then(action(() => (this._userPrompt = '')));
+ return this.generateFireflyImage(this._userPrompt).then(reset);
case GPTPopupMode.USER_PROMPT:
this._conversationArray.push(this._userPrompt);
- return this.generateUserPromptResponse(this._userPrompt).then(action(() => (this._userPrompt = '')));
+ return this.generateUserPromptResponse(this._userPrompt).then(reset);
case GPTPopupMode.QUIZ_RESPONSE:
this._conversationArray.push(this._quizAnswer);
- return this.generateQuizAnswerAnalysis(DocumentView.SelectedDocs().lastElement(), this._quizAnswer).then(action(() => (this._quizAnswer = '')));
+ return this.generateQuizAnswerAnalysis(DocumentView.SelectedDocs().lastElement(), this._quizAnswer).then(reset);
}
});
@@ -490,18 +497,20 @@ export class GPTPopup extends ObservableReactComponent<object> {
};
gptUserInput = () => (
- <div className="btns-wrapper-gpt">
- <div className="chat-wrapper">
- <div className="chat-bubbles">
- {(this._mode === GPTPopupMode.FIREFLY ? this._fireflyArray : this._conversationArray).map((message, index) => (
- <div key={index} className={`chat-bubble ${index % 2 === 1 ? 'user-message' : 'chat-message'}`}>
- {message}
- </div>
- ))}
- {this._gptProcessing && <div className="chat-bubble chat-message">...</div>}
- </div>
+ <div style={{ display: 'flex', maxHeight: 'calc(100% - 32px)', overflow: 'auto' }}>
+ <div className="btns-wrapper-gpt">
+ <div className="chat-wrapper">
+ <div className="chat-bubbles">
+ {(this._mode === GPTPopupMode.FIREFLY ? this._fireflyArray : this._conversationArray).map((message, index) => (
+ <div key={index} className={`chat-bubble ${index % 2 === 1 ? 'user-message' : 'chat-message'}`}>
+ {message}
+ </div>
+ ))}
+ {this._gptProcessing && <div className="chat-bubble chat-message">...</div>}
+ </div>
- <div ref={this._messagesEndRef} style={{ height: '100px' }} />
+ <div ref={this._messagesEndRef} style={{ height: '40px' }} />
+ </div>
</div>
</div>
);
@@ -520,7 +529,7 @@ export class GPTPopup extends ObservableReactComponent<object> {
onChange={e => onChange(e.target.value)}
onKeyDown={e => this.handleKeyPress(e, this._mode)}
type="text"
- style={{ color: SnappingManager.userColor }}
+ style={{ color: 'black' }}
placeholder={placeholder}
/>
<Button //
@@ -744,7 +753,7 @@ export class GPTPopup extends ObservableReactComponent<object> {
onClick={() => this._collectionContext && Doc.setDocFilter(this._collectionContext, 'tags', GPTPopup.ChatTag, 'remove')}
/>
{[GPTPopupMode.USER_PROMPT, GPTPopupMode.QUIZ_RESPONSE, GPTPopupMode.FIREFLY].includes(this._mode) && (
- <IconButton color={SettingsManager.userVariantColor} background={SettingsManager.userColor} tooltip="back" icon={<CgCornerUpLeft size="16px" />} onClick={() => (this._mode = GPTPopupMode.GPT_MENU)} />
+ <IconButton color={SettingsManager.userVariantColor} background={SettingsManager.userColor} tooltip="back" icon={<CgCornerUpLeft size="16px" />} onClick={action(() => (this._mode = GPTPopupMode.GPT_MENU))} />
)}
</>
)}
@@ -753,12 +762,12 @@ export class GPTPopup extends ObservableReactComponent<object> {
render() {
return (
- <div className="gptPopup-summary-box" style={{ background: SnappingManager.userColor, color: SnappingManager.userBackgroundColor, display: SnappingManager.ChatVisible ? 'flex' : 'none', overflow: 'auto' }}>
+ <div className="gptPopup-summary-box" style={{ background: SnappingManager.userColor, color: SnappingManager.userBackgroundColor, display: SnappingManager.ChatVisible ? 'flex' : 'none' }}>
{(() => {
//prettier-ignore
switch (this._mode) {
case GPTPopupMode.USER_PROMPT: return this.promptBox("ASK", this._userPrompt, this.setUserPrompt, 'Ask GPT to sort, tag, define, or filter your documents for you!');
- case GPTPopupMode.FIREFLY: return this.promptBox("CREATE", this._userPrompt, this.setUserPrompt, StrCast(DocumentView.Selected().lastElement()?.Document.ai_firefly_prompt, 'Ask Firefly to generate images'));
+ case GPTPopupMode.FIREFLY: return this.promptBox("CREATE", this._userPrompt, this.setUserPrompt, StrCast(DocumentView.Selected().lastElement()?.Document.ai_prompt, 'Ask Firefly to generate images'));
case GPTPopupMode.QUIZ_RESPONSE: return this.promptBox("QUIZ", this._quizAnswer, this.setQuizAnswer, 'Describe/answer the selected document!');
case GPTPopupMode.GPT_MENU: return this.menuBox();
case GPTPopupMode.SUMMARY: return this.summaryBox();
diff --git a/src/client/views/smartdraw/DrawingFillHandler.tsx b/src/client/views/smartdraw/DrawingFillHandler.tsx
index 2c69284db..f773957e7 100644
--- a/src/client/views/smartdraw/DrawingFillHandler.tsx
+++ b/src/client/views/smartdraw/DrawingFillHandler.tsx
@@ -51,19 +51,19 @@ export class DrawingFillHandler {
if (error.includes('Dropbox') && confirm('Create image failed. Try authorizing DropBox?\r\n' + error.replace(/^[^"]*/, ''))) {
return DrawingFillHandler.authorizeDropbox();
}
- const genratedDocs = DocCast(drawing.ai_firefly_generatedDocs) ?? Docs.Create.MasonryDocument([], { title: StrCast(drawing.title) + ' AI Images', _width: 400, _height: 400 });
- drawing.$ai_firefly_generatedDocs = genratedDocs;
+ const genratedDocs = DocCast(drawing.ai_generatedDocs) ?? Docs.Create.MasonryDocument([], { title: StrCast(drawing.title) + ' AI Images', _width: 400, _height: 400 });
+ drawing.$ai_generatedDocs = genratedDocs;
(res as Upload.ImageInformation[]).map(info =>
Doc.AddDocToList(
genratedDocs,
undefined,
Docs.Create.ImageDocument(info.accessPaths.agnostic.client, {
ai: 'firefly',
+ ai_prompt: newPrompt,
tags: new List<string>(['@ai']),
title: newPrompt,
_data_usePath: 'alternate:hover',
data_alternates: new List<Doc>([drawing]),
- ai_firefly_prompt: newPrompt,
_width: 500,
data_nativeWidth: info.nativeWidth,
data_nativeHeight: info.nativeHeight,
diff --git a/src/client/views/smartdraw/SmartDrawHandler.scss b/src/client/views/smartdraw/SmartDrawHandler.scss
index cca7d77c7..e80f1122b 100644
--- a/src/client/views/smartdraw/SmartDrawHandler.scss
+++ b/src/client/views/smartdraw/SmartDrawHandler.scss
@@ -73,5 +73,6 @@
.edit-box {
display: flex;
flex-direction: row;
+ color: black;
}
}
diff --git a/src/client/views/smartdraw/SmartDrawHandler.tsx b/src/client/views/smartdraw/SmartDrawHandler.tsx
index 4f0cd3978..3976ec39e 100644
--- a/src/client/views/smartdraw/SmartDrawHandler.tsx
+++ b/src/client/views/smartdraw/SmartDrawHandler.tsx
@@ -61,7 +61,6 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
static Instance: SmartDrawHandler;
private _lastInput: DrawingOptions = { text: '', complexity: 5, size: 350, autoColor: true, x: 0, y: 0 };
- private _lastResponse: string = '';
private _selectedDocs: Doc[] = [];
@observable private _display: boolean = false;
@@ -97,7 +96,7 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
CollectionFreeForm, FormattedTextBox, StickerPalette) to define how a drawing document should be added
or removed in their respective locations (to the freeform canvas, to the sticker palette's preview, etc.)
*/
- public AddDrawing: (doc: Doc, opts: DrawingOptions, gptRes: string, x?: number, y?: number) => void = unimplementedFunction;
+ public AddDrawing: (doc: Doc, opts: DrawingOptions, x?: number, y?: number) => void = unimplementedFunction;
public RemoveDrawing: (useLastContainer: boolean, doc?: Doc) => void = unimplementedFunction;
/**
* This creates the ink document that represents a drawing, so it goes through the strokes that make up the drawing,
@@ -105,7 +104,7 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
* classes to customize the way the drawing docs get created. For example, the freeform canvas has a different way of
* defining document bounds, so CreateDrawingDoc is redefined when that class calls gpt draw functions.
*/
- public static CreateDrawingDoc: (strokeList: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => Doc | undefined = (strokeList: [InkData, string, string][], opts: DrawingOptions) => {
+ public static CreateDrawingDoc: (strokeList: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => Doc | undefined = (strokeList: [InkData, string, string][], opts: DrawingOptions, gptRes: string) => {
const drawing: Doc[] = [];
strokeList.forEach((stroke: [InkData, string, string]) => {
const bounds = InkField.getBounds(stroke[0]);
@@ -130,7 +129,14 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
drawing.push(inkDoc);
});
- return MarqueeView.getCollection(drawing, undefined, true, { left: 1, top: 1, width: 1, height: 1 });
+ const drawn = MarqueeView.getCollection(drawing, undefined, true, { left: 1, top: 1, width: 1, height: 1 });
+
+ drawn.$ai_drawing = true;
+ drawn.$ai_drawing_complexity = opts.complexity;
+ drawn.$ai_drawing_colored = opts.autoColor;
+ drawn.$ai_drawing_size = opts.size;
+ drawn.$ai_drawing_data = gptRes;
+ return drawn;
};
@action
@@ -146,15 +152,16 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
* the regenerate popup show by user command.
*/
@action
- displayRegenerate = (x: number, y: number) => {
+ displayRegenerate = (x: number, y: number, scale: number) => {
this._selectedDocs = [DocumentView.SelectedDocs()?.lastElement()];
[this._pageX, this._pageY] = [x, y];
+ this._scale = scale;
this._display = false;
this.ShowRegenerate = true;
this._showEditBox = false;
const docData = this._selectedDocs[0];
- this._lastResponse = StrCast(docData.$drawingData);
- this._lastInput = { text: StrCast(docData.$ai_drawing_input), complexity: NumCast(docData.$ai_drawing_complexity), size: NumCast(docData.$ai_drawing_size), autoColor: BoolCast(docData.$ai_drawing_colored), x: this._pageX, y: this._pageY };
+ this._regenInput = StrCast(docData.$ai_prompt, StrCast(docData.title));
+ this._lastInput = { text: StrCast(docData.$ai_prompt), complexity: NumCast(docData.$ai_drawing_complexity), size: NumCast(docData.$ai_drawing_size), autoColor: BoolCast(docData.$ai_drawing_colored), x: this._pageX, y: this._pageY };
};
/**
@@ -168,9 +175,6 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
this._isLoading = false;
this._showOptions = false;
this._userInput = '';
- this._complexity = 5;
- this._size = 350;
- this._autoColor = true;
Doc.ActiveTool = InkTool.None;
}
};
@@ -184,7 +188,6 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
this.ShowRegenerate = false;
this._isLoading = false;
this._regenInput = '';
- this._lastInput = { text: '', complexity: 5, size: 350, autoColor: true, x: 0, y: 0 };
}
};
@@ -208,7 +211,9 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
this._isLoading = true;
this._canInteract = false;
if (this.ShowRegenerate) {
- await this.regenerate(this._selectedDocs, undefined, undefined, this._regenInput).then(action(() => (this._showEditBox = false)));
+ this._lastInput.x = X;
+ this._lastInput.y = Y;
+ await this.regenerate(this._selectedDocs).then(action(() => (this._showEditBox = false)));
} else {
this._showOptions = false;
try {
@@ -234,13 +239,15 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
*/
drawWithGPT = async (screenPt: { X: number; Y: number }, input: string, complexity: number, size: number, autoColor: boolean) => {
if (input) {
- this._lastInput = { text: input, complexity: complexity, size: size, autoColor: autoColor, x: screenPt.X, y: screenPt.Y };
+ this._lastInput = { text: input, complexity, size, autoColor, x: screenPt.X, y: screenPt.Y };
const res = await gptAPICall(`"${input}", "${complexity}", "${size}"`, GPTCallType.DRAW, undefined, true);
if (res) {
const strokeData = await this.parseSvg(res, { X: 0, Y: 0 }, false, autoColor);
const drawingDoc = strokeData && SmartDrawHandler.CreateDrawingDoc(strokeData.data, strokeData.lastInput, strokeData.lastRes);
- drawingDoc && this.AddDrawing(drawingDoc, this._lastInput, res, screenPt.X, screenPt.Y);
- drawingDoc && this._selectedDocs.push(drawingDoc);
+ if (drawingDoc) {
+ this.AddDrawing(drawingDoc, this._lastInput, screenPt.X, screenPt.Y);
+ this._selectedDocs.push(drawingDoc);
+ }
return strokeData;
} else {
console.error('GPT call failed');
@@ -255,7 +262,7 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
createImageWithFirefly = (input: string, seed?: number): Promise<FireflyImageData | Doc | undefined> => {
this._lastInput.text = input;
return SmartDrawHandler.CreateWithFirefly(input, this._imgDims, seed).then(doc => {
- doc instanceof Doc && this.AddDrawing(doc, this._lastInput, input, this._pageX, this._pageY);
+ doc instanceof Doc && this.AddDrawing(doc, this._lastInput, this._pageX, this._pageY);
return doc;
});
}; /**
@@ -301,8 +308,8 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
_width: Math.min(400, dims.width),
_height: (Math.min(400, dims.width) * dims.height) / dims.width,
ai: 'firefly',
- ai_firefly_seed: +(newseed ?? 0),
- ai_firefly_prompt: input,
+ ai_prompt_seed: +(newseed ?? 0),
+ ai_prompt: input,
});
})
.catch(e => {
@@ -316,33 +323,30 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
* @param doc the drawing Docs to regenerate
*/
@action
- regenerate = (drawingDocs: Doc[], lastInput?: DrawingOptions, lastResponse?: string, regenInput?: string, changeInPlace?: boolean) => {
- if (lastInput) this._lastInput = lastInput;
- if (lastResponse) this._lastResponse = lastResponse;
+ regenerate = (drawingDocs: Doc[], regenInput?: string, changeInPlace?: boolean) => {
if (regenInput) this._regenInput = regenInput;
return Promise.all(
drawingDocs.map(async doc => {
switch (doc.type) {
case DocumentType.IMG: {
const func = changeInPlace ? this.recreateImageWithFirefly : this.createImageWithFirefly;
- const newPrompt = doc.ai_firefly_prompt ? `${doc.ai_firefly_prompt} ~~~ ${this._regenInput}` : this._regenInput;
- return this._regenInput ? func(newPrompt, NumCast(doc?.ai_firefly_seed)) : func(this._lastInput.text || StrCast(doc.ai_firefly_prompt));
+ const newPrompt = doc.ai_prompt && doc.ai_prompt !== this._regenInput ? `${doc.ai_prompt} ~~~ ${this._regenInput}` : this._regenInput;
+ return this._regenInput ? func(newPrompt, NumCast(doc?.ai_prompt_seed)) : func(this._lastInput.text || StrCast(doc.ai_prompt));
}
case DocumentType.COL: {
try {
const res = await (async () => {
if (this._regenInput) {
- const prompt = `This is your previously generated svg code: ${this._lastResponse} for the user input "${this._lastInput.text}". Please regenerate it with the provided specifications.`;
+ const prompt = `This is your previously generated svg code: ${doc.$ai_drawing_data} for the user input "${doc.ai_prompt}". Please regenerate it with the provided specifications.`;
this._lastInput.text = `${this._lastInput.text} ~~~ ${this._regenInput}`;
return gptAPICall(`"${this._regenInput}"`, GPTCallType.DRAW, prompt, true);
}
- return gptAPICall(`"${this._lastInput.text}", "${this._lastInput.complexity}", "${this._lastInput.size}"`, GPTCallType.DRAW, undefined, true);
+ return gptAPICall(`"${doc.$ai_prompt}", "${doc.$ai_drawing_complexity}", "${doc.$ai_drawing_size}"`, GPTCallType.DRAW, undefined, true);
})();
if (res) {
- const strokeData = await this.parseSvg(res, { X: this._lastInput.x ?? 0, Y: this._lastInput.y ?? 0 }, true, lastInput?.autoColor || this._autoColor);
- this.RemoveDrawing !== unimplementedFunction && this.RemoveDrawing(true, doc);
+ const strokeData = await this.parseSvg(res, { X: 0, Y: 0 }, true, this._autoColor);
const drawingDoc = strokeData && SmartDrawHandler.CreateDrawingDoc(strokeData.data, strokeData.lastInput, strokeData.lastRes);
- drawingDoc && this.AddDrawing(drawingDoc, this._lastInput, res);
+ drawingDoc && this.AddDrawing(drawingDoc, this._lastInput, this._lastInput.x, this._lastInput.y);
} else {
console.error('GPT call failed');
}
@@ -363,7 +367,6 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
const svg = res.match(/<svg[^>]*>([\s\S]*?)<\/svg>/g);
if (svg) {
- this._lastResponse = svg[0];
const svgObject = await parse(svg[0]);
console.log(res, svgObject);
const svgStrokes: INode[] = svgObject.children;
@@ -398,7 +401,7 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
*/
colorWithGPT = async (drawing: Doc) => {
const img = await DocumentView.GetDocImage(drawing);
- const { href } = ImageCast(img).url;
+ const { href } = ImageCast(img)?.url ?? { href: '' };
const hrefParts = href.split('.');
const hrefComplete = `${hrefParts[0]}_o.${hrefParts[1]}`;
try {
@@ -664,7 +667,7 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
<div className="regenerate-box">
<IconButton
tooltip="Regenerate"
- icon={this._isLoading && this._regenInput === '' ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <FontAwesomeIcon icon={'rotate'} />}
+ icon={this._isLoading ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <FontAwesomeIcon icon={'rotate'} />}
color={SettingsManager.userColor}
onClick={() => this.handleSendClick(this._pageX, this._pageY)}
/>
diff --git a/src/client/views/smartdraw/StickerPalette.tsx b/src/client/views/smartdraw/StickerPalette.tsx
index 0e234e966..6ef3d26ad 100644
--- a/src/client/views/smartdraw/StickerPalette.tsx
+++ b/src/client/views/smartdraw/StickerPalette.tsx
@@ -9,7 +9,7 @@ import ReactLoading from 'react-loading';
import { returnEmptyFilter, returnFalse, returnTrue } from '../../../ClientUtils';
import { emptyFunction, numberRange } from '../../../Utils';
import { Doc, DocListCast, returnEmptyDoclist } from '../../../fields/Doc';
-import { ImageCast, NumCast } from '../../../fields/Types';
+import { ImageCast, NumCast, StrCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
import { DocumentType } from '../../documents/DocumentTypes';
import { Docs } from '../../documents/Documents';
@@ -147,9 +147,9 @@ export class StickerPalette extends ObservableReactComponent<StickerPaletteProps
SmartDrawHandler.Instance.AddDrawing = this.addDrawing;
this._canInteract = false;
Promise.all(
- numberRange(3).map(i => {
+ numberRange(3).map(() => {
return this._showRegenerate
- ? SmartDrawHandler.Instance.regenerate(prevDrawings, this._opts, this._gptRes[i], this._userInput)
+ ? SmartDrawHandler.Instance.regenerate(prevDrawings, this._userInput)
: SmartDrawHandler.Instance.drawWithGPT({ X: 0, Y: 0 }, this._userInput, this._opts.complexity || 0, this._opts.size || 0, !!this._opts.autoColor);
})
).then(() => {
@@ -161,8 +161,8 @@ export class StickerPalette extends ObservableReactComponent<StickerPaletteProps
});
@action
- addDrawing = (drawing: Doc, opts: DrawingOptions, gptRes: string) => {
- this._gptRes.push(gptRes);
+ addDrawing = (drawing: Doc) => {
+ this._gptRes.push(StrCast(drawing.$ai_drawing_data));
drawing.$freeform_fitContentsToBox = true;
Doc.AddDocToList(this._props.Doc, 'data', drawing);
};
@@ -176,7 +176,7 @@ export class StickerPalette extends ObservableReactComponent<StickerPaletteProps
const cIndex = NumCast(this._props.Doc.carousel_index);
const focusedDrawing = DocListCast(this._props.Doc.data)[cIndex];
focusedDrawing.$title = this._opts.text?.match(/^(.*?)~~~.*$/)?.[1] || this._opts.text;
- focusedDrawing.$ai_drawing_input = this._opts.text;
+ focusedDrawing.$ai_prompt = this._opts.text;
focusedDrawing.$ai_drawing_complexity = this._opts.complexity;
focusedDrawing.$ai_drawing_colored = this._opts.autoColor;
focusedDrawing.$ai_drawing_size = this._opts.size;
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index ba94f0504..0d11b9743 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -25,6 +25,7 @@ import { ComputedField, ScriptField } from './ScriptField';
import { BoolCast, Cast, DocCast, FieldValue, ImageCastWithSuffix, NumCast, RTFCast, StrCast, ToConstructor, toList } from './Types';
import { containedFieldChangedHandler, deleteProperty, GetEffectiveAcl, getField, getter, makeEditable, makeReadOnly, setter, SharingPermissions } from './util';
import { gptImageLabel } from '../client/apis/gpt/GPT';
+import { DateField } from './DateField';
export let ObjGetRefField: (id: string, force?: boolean) => Promise<Doc | undefined>;
export let ObjGetRefFields: (ids: string[]) => Promise<Map<string, Doc | undefined>>;
@@ -1109,6 +1110,10 @@ export namespace Doc {
Cast(templateFieldValue, listSpec(Doc), [])?.map(d => d instanceof Doc && MakeMetadataFieldTemplate(d, templateDoc));
Doc.GetProto(templateField)[metadataFieldKey] = ObjectField.MakeCopy(templateFieldValue);
}
+ if (templateField.type === DocumentType.IMG) {
+ // bcz: should be a better way .. but, if the image is a template, then we can't expect to know the aspect ratio. When the image is replaced by data and rendered, we want to recomputed the native dimensions.
+ templateField[DocData].layout_resetNativeDim = true;
+ }
// get the layout string that the template uses to specify its layout
const templateFieldLayoutString = StrCast(Doc.LayoutField(templateField[DocLayout]));
@@ -1184,13 +1189,15 @@ export namespace Doc {
return Doc.NativeWidth(doc, dataDoc, useDim) / (Doc.NativeHeight(doc, dataDoc, useDim) || 1);
}
export function NativeWidth(doc?: Doc, dataDoc?: Doc, useWidth?: boolean) {
- return !doc ? 0 : NumCast(doc._nativeWidth, NumCast((dataDoc || doc)[Doc.LayoutDataKey(doc) + '_nativeWidth'], useWidth ? NumCast(doc._width) : 0));
+ // if this is a field template, then don't use the doc's nativeWidth/height
+ return !doc ? 0 : NumCast(doc.isTemplateForField ? undefined : doc._nativeWidth, NumCast((dataDoc || doc)[Doc.LayoutDataKey(doc) + '_nativeWidth'], !doc.isTemplateForField && useWidth ? NumCast(doc._width) : 0));
}
export function NativeHeight(doc?: Doc, dataDoc?: Doc, useHeight?: boolean) {
if (!doc) return 0;
const nheight = (Doc.NativeWidth(doc, dataDoc, useHeight) / NumCast(doc._width)) * NumCast(doc._height); // divide before multiply to avoid floating point errrorin case nativewidth = width
const dheight = NumCast((dataDoc || doc)[Doc.LayoutDataKey(doc) + '_nativeHeight'], useHeight ? NumCast(doc._height) : 0);
- return NumCast(doc._nativeHeight, nheight || dheight);
+ // if this is a field template, then don't use the doc's nativeWidth/height
+ return NumCast(doc.isTemplateForField ? undefined : doc._nativeHeight, nheight || dheight);
}
export function OutpaintingWidth(doc?: Doc, dataDoc?: Doc, useWidth?: boolean) {
@@ -1504,7 +1511,13 @@ export namespace Doc {
case DocumentType.RTF: return RTFCast(tdoc[Doc.LayoutDataKey(tdoc)])?.Text ?? StrCast(tdoc[Doc.LayoutDataKey(tdoc)]);
default: return StrCast(tdoc.title).startsWith("Untitled") ? "" : StrCast(tdoc.title);
}}); // prettier-ignore
- return docText(doc).then(text => (doc['$' + Doc.LayoutDataKey(doc) + '_description'] = text));
+ return docText(doc).then(
+ action(text => {
+ // set the time when the date changes. This also allows a live textbox view to react to the update, otherwise, it wouldn't take effect until the next time the view is rerendered.
+ doc['$' + Doc.LayoutDataKey(doc) + '_description_modificationDate'] = new DateField();
+ return (doc['$' + Doc.LayoutDataKey(doc) + '_description'] = text);
+ })
+ );
}
// prettier-ignore
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index b27816f55..df832d088 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -35,8 +35,6 @@ export const documentSchema = createSchema({
_nativeHeight: 'number', // "
_width: 'number', // width of document in its container's coordinate system
_height: 'number', // "
- _xPadding: 'number', // pixels of padding on left/right of collectionfreeformview contents when freeform_fitContentsToBox is set
- _yPadding: 'number', // pixels of padding on top/bottom of collectionfreeformview contents when freeform_fitContentsToBox is set
_xMargin: 'number', // margin added on left/right of most documents to add separation from their container
_yMargin: 'number', // margin added on top/bottom of most documents to add separation from their container
_overflow: 'string', // sets overflow behvavior for CollectionFreeForm views
diff --git a/src/server/SharedMediaTypes.ts b/src/server/SharedMediaTypes.ts
index 9aa4b120f..43a9ce963 100644
--- a/src/server/SharedMediaTypes.ts
+++ b/src/server/SharedMediaTypes.ts
@@ -19,6 +19,9 @@ export enum AudioAnnoState {
}
export namespace Upload {
+ export function isTextInformation(uploadResponse: Upload.FileInformation): uploadResponse is Upload.ImageInformation {
+ return 'rawText' in uploadResponse;
+ }
export function isImageInformation(uploadResponse: Upload.FileInformation): uploadResponse is Upload.ImageInformation {
return 'nativeWidth' in uploadResponse;
}
diff --git a/src/server/server_Initialization.ts b/src/server/server_Initialization.ts
index a56ab5d18..514e2ce1e 100644
--- a/src/server/server_Initialization.ts
+++ b/src/server/server_Initialization.ts
@@ -1,19 +1,15 @@
import * as bodyParser from 'body-parser';
-import * as brotli from 'brotli';
import { blue, yellow } from 'colors';
import * as flash from 'connect-flash';
import * as MongoStoreConnect from 'connect-mongo';
-import * as cors from 'cors';
import * as express from 'express';
import * as expressFlash from 'express-flash';
import * as session from 'express-session';
import { createServer } from 'https';
import * as passport from 'passport';
-import * as request from 'request';
import * as webpack from 'webpack';
import * as wdm from 'webpack-dev-middleware';
import * as whm from 'webpack-hot-middleware';
-import * as zlib from 'zlib';
import * as config from '../../webpack.config';
import { logPort } from './ActionUtilities';
import RouteManager from './RouteManager';
@@ -23,6 +19,8 @@ import { SSL } from './apis/google/CredentialsLoader';
import { getForgot, getLogin, getLogout, getReset, getSignup, postForgot, postLogin, postReset, postSignup } from './authentication/AuthenticationManager';
import { Database } from './database';
import { WebSocket } from './websocket';
+import axios from 'axios';
+import { JSDOM } from 'jsdom';
/* RouteSetter is a wrapper around the server that prevents the server
from being exposed. */
@@ -84,142 +82,96 @@ function buildWithMiddleware(server: express.Express) {
return server;
}
-function registerEmbeddedBrowseRelativePathHandler(server: express.Express) {
- server.use('*', (req, res) => {
- // res.setHeader('Access-Control-Allow-Origin', '*');
- // res.header('Access-Control-Allow-Methods', 'GET, PUT, PATCH, POST, DELETE');
- // res.header('Access-Control-Allow-Headers', req.header('access-control-request-headers'));
- const relativeUrl = req.originalUrl;
- if (!res.headersSent && req.headers.referer?.includes('corsProxy')) {
- if (!req.user) res.redirect('/home'); // When no user is logged in, we interpret a relative URL as being a reference to something they don't have access to and redirect to /home
- // a request for something by a proxied referrer means it must be a relative reference. So construct a proxied absolute reference here.
- try {
- const proxiedRefererUrl = decodeURIComponent(req.headers.referer); // (e.g., http://localhost:<port>/corsProxy/https://en.wikipedia.org/wiki/Engelbart)
- const dashServerUrl = proxiedRefererUrl.match(/.*corsProxy\//)![0]; // the dash server url (e.g.: http://localhost:<port>/corsProxy/ )
- const actualReferUrl = proxiedRefererUrl.replace(dashServerUrl, ''); // the url of the referer without the proxy (e.g., : https://en.wikipedia.org/wiki/Engelbart)
- const absoluteTargetBaseUrl = actualReferUrl.match(/https?:\/\/[^/]*/)![0]; // the base of the original url (e.g., https://en.wikipedia.org)
- const redirectedProxiedUrl = dashServerUrl + encodeURIComponent(absoluteTargetBaseUrl + relativeUrl); // the new proxied full url (e.g., http://localhost:<port>/corsProxy/https://en.wikipedia.org/<somethingelse>)
- const redirectUrl = relativeUrl.startsWith('//') ? 'http:' + relativeUrl : redirectedProxiedUrl;
- res.redirect(redirectUrl);
- } catch (e) {
- console.log('Error embed: ', e);
+function registerCorsProxy(server: express.Express) {
+ // .replace('<head>', '<head> <style>[id ^= "google"] { display: none; } </style>')
+ server.use('/corsproxy', async (req, res) => {
+ try {
+ // Extract URL from either query param or path
+ let targetUrl: string;
+
+ if (req.query.url) {
+ // Case 1: URL passed as query parameter (/corsproxy?url=...)
+ targetUrl = req.query.url as string;
+ } else {
+ // Case 2: URL passed as path (/corsproxy/http://example.com)
+ const path = req.originalUrl.replace(/^\/corsproxy\/?/, '');
+ targetUrl = decodeURIComponent(path);
+
+ // Add protocol if missing (assuming https as default)
+ if (!targetUrl.startsWith('http://') && !targetUrl.startsWith('https://')) {
+ targetUrl = `https://${targetUrl}`;
+ }
+ }
+
+ if (!targetUrl) {
+ res.send(`<html><body bgcolor="red" link="006666" alink="8B4513" vlink="006666">
+ <title>Error</title>
+ <div align="center"><h1>Failed to load: ${targetUrl} </h1></div>
+ <p>URL is required</p>
+ </body></html>`);
+ // res.status(400).json({ error: 'URL is required' });
+ return;
}
- } else if (relativeUrl.startsWith('/search') && !req.headers.referer?.includes('corsProxy')) {
- // detect search query and use default search engine
- res.redirect(req.headers.referer + 'corsProxy/' + encodeURIComponent('http://www.google.com' + relativeUrl));
- } else {
- res.status(404).json({ error: 'no such file or endpoint: try /home /logout /login' });
- }
- });
-}
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-function proxyServe(req: any, requrl: string, response: any) {
- // eslint-disable-next-line @typescript-eslint/no-require-imports
- const htmlBodyMemoryStream = new (require('memorystream'))();
- let wasinBrFormat = false;
- const sendModifiedBody = () => {
- const header = response.headers['content-encoding'];
- const refToCors = (match: string, tag: string, sym: string, href: string) => `${tag}=${sym + resolvedServerUrl}/corsProxy/${href + sym}`;
- // const relpathToCors = (match: any, href: string, offset: any, string: any) => `="${resolvedServerUrl + '/corsProxy/' + decodeURIComponent(req.originalUrl.split('/corsProxy/')[1].match(/https?:\/\/[^\/]*/)?.[0] ?? '') + '/' + href}"`;
- if (header) {
+ // Validate URL format
try {
- const bodyStream = htmlBodyMemoryStream.read();
- if (bodyStream) {
- const htmlInputText = wasinBrFormat ? Buffer.from(brotli.decompress(bodyStream)) : header.includes('gzip') ? zlib.gunzipSync(bodyStream) : bodyStream;
- const htmlText = htmlInputText
- .toString('utf8')
- .replace('<head>', '<head> <style>[id ^= "google"] { display: none; } </style>')
- .replace(/(src|href)=(['"])(https?[^\n]*)\1/g, refToCors) // replace src or href='http(s)://...' or href="http(s)://.."
- // .replace(/= *"\/([^"]*)"/g, relpathToCors)
- .replace(/data-srcset="[^"]*"/g, '')
- .replace(/srcset="[^"]*"/g, '')
- .replace(/target="_blank"/g, '');
- response.send(header?.includes('gzip') ? zlib.gzipSync(htmlText) : htmlText);
- } else {
- req.pipe(request(requrl))
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- .on('error', (e: any) => console.log('requrl ', e))
- .pipe(response)
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- .on('error', (e: any) => console.log('response pipe error', e));
- console.log('EMPTY body:' + req.url);
- }
+ new URL(targetUrl);
} catch (e) {
- console.log('ERROR?: ', e);
- }
- } else {
- req.pipe(htmlBodyMemoryStream)
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- .on('error', (e: any) => console.log('html body memorystream error', e))
- .pipe(response)
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- .on('error', (e: any) => console.log('html body memory stream response error', e));
- }
- };
- const retrieveHTTPBody = () => {
- // req.headers.cookie = '';
- req.pipe(request(requrl))
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- .on('error', (e: any) => {
- console.log(`CORS url error: ${requrl}`, e);
- response.send(`<html><body bgcolor="red" link="006666" alink="8B4513" vlink="006666">
+ res.send(`<html><body bgcolor="red" link="006666" alink="8B4513" vlink="006666">
<title>Error</title>
- <div align="center"><h1>Failed to load: ${requrl} </h1></div>
+ <div align="center"><h1>Failed to load: ${targetUrl} </h1></div>
<p>${e}</p>
</body></html>`);
- })
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- .on('response', (res: any) => {
- res.headers;
- const headers = Object.keys(res.headers);
- const headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/;
- headers.forEach(headerName => {
- const header = res.headers[headerName];
- if (Array.isArray(header)) {
- res.headers[headerName] = header.filter(h => !headerCharRegex.test(h));
- } else if (headerCharRegex.test(header || '')) {
- delete res.headers[headerName];
- } else res.headers[headerName] = header;
- if (headerName === 'content-encoding') {
- wasinBrFormat = res.headers[headerName] === 'br';
- res.headers[headerName] = 'gzip';
- }
+ //res.status(400).json({ error: 'Invalid URL format' });
+ return;
+ }
+
+ const response = await axios.get(targetUrl as string, {
+ headers: { 'User-Agent': req.headers['user-agent'] || 'Mozilla/5.0' },
+ responseType: 'text',
+ });
+
+ const baseUrl = new URL(targetUrl as string);
+
+ if (response.headers['content-type']?.includes('text/html')) {
+ const dom = new JSDOM(response.data);
+ const document = dom.window.document;
+
+ // Process all elements with href/src
+ const elements = document.querySelectorAll('[href],[src]');
+ elements.forEach(elem => {
+ const attrs = [];
+ if (elem.hasAttribute('href')) attrs.push('href');
+ if (elem.hasAttribute('src')) attrs.push('src');
+
+ attrs.forEach(attr => {
+ const originalUrl = elem.getAttribute(attr);
+ if (!originalUrl || originalUrl.startsWith('http://') || originalUrl.startsWith('https://') || originalUrl.startsWith('data:') || /^[a-z]+:/.test(originalUrl)) {
+ return;
+ }
+
+ const resolvedUrl = new URL(originalUrl, baseUrl).toString();
+ elem.setAttribute(attr, resolvedUrl);
+ });
});
- res.headers['x-permitted-cross-domain-policies'] = 'all';
- res.headers['x-frame-options'] = '';
- res.headers['content-security-policy'] = '';
- response.headers = response._headers = res.headers;
- })
- .on('end', sendModifiedBody)
- .pipe(htmlBodyMemoryStream)
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- .on('error', (e: any) => console.log('http body pipe error', e));
- };
- retrieveHTTPBody();
-}
-function registerCorsProxy(server: express.Express) {
- server.use('/corsProxy', async (req, res) => {
- res.setHeader('Access-Control-Allow-Origin', '*');
- res.header('Access-Control-Allow-Methods', 'GET, PUT, PATCH, POST, DELETE');
- res.header('Access-Control-Allow-Headers', req.header('access-control-request-headers'));
- const referer = req.headers.referer ? decodeURIComponent(req.headers.referer) : '';
- let requrlraw = decodeURIComponent(req.url.substring(1));
- const qsplit = requrlraw.split('?q=');
- const newqsplit = requrlraw.split('&q=');
- if (qsplit.length > 1 && newqsplit.length > 1) {
- const lastq = newqsplit[newqsplit.length - 1];
- requrlraw = qsplit[0] + '?q=' + lastq.split('&')[0] + '&' + qsplit[1].split('&')[1];
- }
- const requrl = requrlraw.startsWith('/') ? referer + requrlraw : requrlraw;
- // cors weirdness here...
- // if the referer is a cors page and the cors() route (I think) redirected to /corsProxy/<path> and the requested url path was relative,
- // then we redirect again to the cors referer and just add the relative path.
- if (!requrl.startsWith('http') && req.originalUrl.startsWith('/corsProxy') && referer?.includes('corsProxy')) {
- res.redirect(referer + (referer.endsWith('/') ? '' : '/') + requrl);
- } else {
- proxyServe(req, requrl, res);
+ // Handle base tag
+ const baseTags = document.querySelectorAll('base');
+ baseTags.forEach(tag => tag.remove());
+
+ const newBase = document.createElement('base');
+ newBase.setAttribute('href', `${baseUrl}/`);
+ document.head.insertBefore(newBase, document.head.firstChild);
+
+ response.data = dom.serialize();
+ }
+
+ res.set({
+ 'Access-Control-Allow-Origin': '*',
+ 'Content-Type': response.headers['content-type'],
+ }).send(response.data);
+ } catch (error: unknown) {
+ res.status(500).json({ error: 'Proxy error', details: (error as { message: string }).message });
}
});
}
@@ -255,13 +207,11 @@ export default async function InitializeServer(routeSetter: RouteSetter) {
app.use(whm(compiler));
app.get(/^\/+$/, (req, res) => res.redirect(req.user ? '/home' : '/login')); // target urls that consist of one or more '/'s with nothing in between
app.use(express.static(publicDirectory, { setHeaders: res => res.setHeader('Access-Control-Allow-Origin', '*') })); // all urls that start with dash's public directory: /files/ (e.g., /files/images, /files/audio, etc)
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- app.use(cors({ origin: (_origin: any, callback: any) => callback(null, true) }));
+ // app.use(cors({ origin: (_origin: any, callback: any) => callback(null, true) }));
registerAuthenticationRoutes(app); // this adds routes to authenticate a user (login, etc)
- registerCorsProxy(app); // this adds a /corsProxy/ route to allow clients to get to urls that would otherwise be blocked by cors policies
+ registerCorsProxy(app); // this adds a /corsproxy/ route to allow clients to get to urls that would otherwise be blocked by cors policies
isRelease && !SSL.Loaded && SSL.exit();
routeSetter(new RouteManager(app, isRelease)); // this sets up all the regular supervised routes (things like /home, download/upload api's, pdf, search, session, etc)
- registerEmbeddedBrowseRelativePathHandler(app); // this allows renered web pages which internally have relative paths to find their content
isRelease && process.env.serverPort && (resolvedPorts.server = Number(process.env.serverPort));
const server = isRelease ? createServer(SSL.Credentials, app) : app;
await new Promise<void>(resolve => {