diff options
Diffstat (limited to 'src/client/views/nodes')
15 files changed, 186 insertions, 151 deletions
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index cb0831d3c..5315612e1 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -291,7 +291,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() this.askGPTPhonemes(this._inputValue); this._renderSide = this.backKey; this._outputValue = ''; - } else if (this._inputValue) this.askGPT(GPTCallType.QUIZ); + } else if (this._inputValue) this.askGPT(GPTCallType.QUIZDOC); }; onPointerMove = ({ movementX }: PointerEvent) => { @@ -511,7 +511,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() */ askGPT = async (callType: GPTCallType) => { const questionText = this.frontText; - const queryText = questionText + (callType == GPTCallType.QUIZ ? ' UserAnswer: ' + this._inputValue + '. ' + ' Rubric: ' + this.backText : ''); + const queryText = questionText + (callType == GPTCallType.QUIZDOC ? ' UserAnswer: ' + this._inputValue + '. ' + ' Rubric: ' + this.backText : ''); this.loading = true; const res = !this.frontText @@ -522,7 +522,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() case GPTCallType.CHATCARD: DocCast(this.dataDoc[this.backKey])[DocData].text = resp; break; - case GPTCallType.QUIZ: + case GPTCallType.QUIZDOC: this._renderSide = this.backKey; this._outputValue = resp.replace(/UserAnswer/g, "user's answer").replace(/Rubric/g, 'rubric'); break; diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index b874d077b..fa3ab73a7 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -489,7 +489,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { } // Changing which document to add the annotation to (the currently selected PDF) - GPTPopup.Instance.setSidebarId('data_sidebar'); + GPTPopup.Instance.setSidebarFieldKey('data_sidebar'); GPTPopup.Instance.addDoc = this.sidebarAddDocument; }; @@ -523,7 +523,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { }; askGPT = action(async () => { - GPTPopup.Instance.setSidebarId('data_sidebar'); + GPTPopup.Instance.setSidebarFieldKey('data_sidebar'); GPTPopup.Instance.addDoc = this.sidebarAddDocument; GPTPopup.Instance.createFilteredDoc = this.createFilteredDoc; GPTPopup.Instance.setDataJson(''); diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss index fe4f0b1a2..59e093683 100644 --- a/src/client/views/nodes/ImageBox.scss +++ b/src/client/views/nodes/ImageBox.scss @@ -40,7 +40,7 @@ max-height: 100%; pointer-events: inherit; background: transparent; - z-index: -10000; + // z-index: -10000; // bcz: not sure why this was here. it broke dropping images on the image box alternate bullseye icon. img { height: auto; @@ -129,7 +129,12 @@ right: 0; bottom: 0; z-index: 2; + transform-origin: bottom right; cursor: default; + > svg { + width: 100%; + height: 100%; + } } .imageBox-fader img { diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index caefbf542..f76e10a0e 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -1,11 +1,12 @@ +import { Button, Colors, Size, Type } from '@dash/components'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Slider, Tooltip } from '@mui/material'; import axios from 'axios'; -import { Colors, Button, Type, Size } from '@dash/components'; import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction } from 'mobx'; import { observer } from 'mobx-react'; import { extname } from 'path'; import * as React from 'react'; +import { AiOutlineSend } from 'react-icons/ai'; import ReactLoading from 'react-loading'; import { ClientUtils, DashColor, returnEmptyString, returnFalse, returnOne, returnZero, setupMoveUpEvents, UpdateIcon } from '../../../ClientUtils'; import { Doc, DocListCast, Opt } from '../../../fields/Doc'; @@ -16,12 +17,14 @@ import { ObjectField } from '../../../fields/ObjectField'; import { Cast, DocCast, ImageCast, NumCast, RTFCast, StrCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; +import { Upload } from '../../../server/SharedMediaTypes'; import { emptyFunction } from '../../../Utils'; import { Docs } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; import { DocUtils, FollowLinkScript } from '../../documents/DocUtils'; import { Networking } from '../../Network'; import { DragManager } from '../../util/DragManager'; +import { SettingsManager } from '../../util/SettingsManager'; import { SnappingManager } from '../../util/SnappingManager'; import { undoable, undoBatch } from '../../util/UndoManager'; import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; @@ -32,6 +35,9 @@ import { MarqueeAnnotator } from '../MarqueeAnnotator'; import { OverlayView } from '../OverlayView'; import { AnchorMenu } from '../pdf/AnchorMenu'; import { PinDocView, PinProps } from '../PinFuncs'; +import { DrawingFillHandler } from '../smartdraw/DrawingFillHandler'; +import { FireflyImageData } from '../smartdraw/FireflyConstants'; +import { SmartDrawHandler } from '../smartdraw/SmartDrawHandler'; import { StickerPalette } from '../smartdraw/StickerPalette'; import { StyleProp } from '../StyleProp'; import { DocumentView } from './DocumentView'; @@ -39,12 +45,6 @@ import { FieldView, FieldViewProps } from './FieldView'; import { FocusViewOptions } from './FocusViewOptions'; import './ImageBox.scss'; import { OpenWhere } from './OpenWhere'; -import { Upload } from '../../../server/SharedMediaTypes'; -import { SmartDrawHandler } from '../smartdraw/SmartDrawHandler'; -import { SettingsManager } from '../../util/SettingsManager'; -import { AiOutlineSend } from 'react-icons/ai'; -import { FireflyImageData } from '../smartdraw/FireflyConstants'; -import { DrawingFillHandler } from '../smartdraw/DrawingFillHandler'; export class ImageEditorData { // eslint-disable-next-line no-use-before-define @@ -83,7 +83,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { private _disposers: { [name: string]: IReactionDisposer } = {}; private _getAnchor: (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => Opt<Doc> = () => undefined; private _overlayIconRef = React.createRef<HTMLDivElement>(); - private _mainCont: React.RefObject<HTMLDivElement> = React.createRef(); + private _mainCont: HTMLDivElement | null = null; private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef(); imageRef: HTMLImageElement | null = null; // <video> ref marqueeref = React.createRef<MarqueeAnnotator>(); @@ -108,6 +108,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { } protected createDropTarget = (ele: HTMLDivElement) => { + this._mainCont = ele; this._dropDisposer?.(); ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.Document)); }; @@ -147,7 +148,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { this._disposers.path = reaction( () => ({ nativeSize: this.nativeSize, width: NumCast(this.layoutDoc._width) }), ({ nativeSize, width }) => { - if (layoutDoc === this.layoutDoc || !this.layoutDoc._height) { + if ((layoutDoc === this.layoutDoc && !this.layoutDoc._layout_nativeDimEditable) || !this.layoutDoc._height) { this.layoutDoc._height = (width * nativeSize.nativeHeight) / nativeSize.nativeWidth; } }, @@ -157,8 +158,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { () => this.layoutDoc.layout_scrollTop, sTop => { this._forcedScroll = true; - !this._ignoreScroll && this._mainCont.current && (this._mainCont.current.scrollTop = NumCast(sTop)); - this._mainCont.current?.scrollTo({ top: NumCast(sTop) }); + !this._ignoreScroll && this._mainCont && (this._mainCont.scrollTop = NumCast(sTop)); + this._mainCont?.scrollTo({ top: NumCast(sTop) }); this._forcedScroll = false; }, { fireImmediately: true } @@ -315,6 +316,16 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { return cropping; }; + docEditorView = action(() => { + const field = Cast(this.dataDoc[this.fieldKey], ImageField); + if (field) { + ImageEditorData.Open = true; + ImageEditorData.Source = this.choosePath(field.url); + ImageEditorData.AddDoc = this._props.addDocument; + ImageEditorData.RootDoc = this.Document; + } + }); + specificContextMenu = (): void => { const field = Cast(this.dataDoc[this.fieldKey], ImageField); if (field) { @@ -352,16 +363,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { icon: 'expand-arrows-alt', }); funcs.push({ description: 'Copy path', event: () => ClientUtils.CopyText(this.choosePath(field.url)), icon: 'copy' }); - funcs.push({ - description: 'Open Image Editor', - event: action(() => { - ImageEditorData.Open = true; - ImageEditorData.Source = this.choosePath(field.url); - ImageEditorData.AddDoc = this._props.addDocument; - ImageEditorData.RootDoc = this.Document; - }), - icon: 'pencil-alt', - }); + funcs.push({ description: 'Open Image Editor', event: this.docEditorView, icon: 'pencil-alt' }); this.layoutDoc.ai && funcs.push({ description: 'Regenerate AI Image', @@ -381,7 +383,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { // updateIcon = () => new Promise<void>(res => res()); updateIcon = (usePanelDimensions?: boolean) => { - const contentDiv = this._mainCont.current; + const contentDiv = this._mainCont; return !contentDiv ? new Promise<void>(res => res()) : UpdateIcon( @@ -423,6 +425,20 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { const nativeOrientation = NumCast(this.dataDoc[this.fieldKey + '_nativeOrientation'], 1); return { nativeWidth, nativeHeight, nativeOrientation }; } + private _sideBtnWidth = 35; + /** + * How much the content of the view is being scaled based on its nesting and its fit-to-width settings + */ + @computed get viewScaling() { return this.ScreenToLocalBoxXf().Scale * ( this._props.NativeDimScaling?.() || 1); } // prettier-ignore + /** + * The maximum size a UI widget can be scaled so that it won't be bigger in screen pixels than its normal 35 pixel size. + */ + @computed get maxWidgetSize() { return Math.min(this._sideBtnWidth, 0.5 * Math.min(NumCast(this.Document.width)))* this.viewScaling; } // prettier-ignore + /** + * How much to reactively scale a UI element so that it is as big as it can be (up to its normal 35pixel size) without being too big for the Doc content + */ + @computed get uiBtnScaling() { return Math.min(this.maxWidgetSize / this._sideBtnWidth, 1); } // prettier-ignore + @computed get overlayImageIcon() { const usePath = this.layoutDoc[`_${this.fieldKey}_usePath`]; return ( @@ -451,9 +467,10 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { }) } style={{ - display: (this._props.isContentActive() !== false && SnappingManager.CanEmbed) || this.dataDoc[this.fieldKey + '_alternates'] ? 'block' : 'none', - width: 'min(10%, 25px)', - height: 'min(10%, 25px)', + display: this._props.isContentActive() && (SnappingManager.CanEmbed || this.dataDoc[this.fieldKey + '_alternates']) ? 'block' : 'none', + transform: `scale(${this.uiBtnScaling})`, + width: this._sideBtnWidth, + height: this._sideBtnWidth, background: usePath === undefined ? 'white' : usePath === 'alternate' ? 'black' : 'gray', color: usePath === undefined ? 'black' : 'white', }}> @@ -510,7 +527,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { this._isHovering = false; })} key={this.layoutDoc[Id]} - ref={this.createDropTarget} onPointerDown={this.marqueeDown}> <div className="imageBox-fader" style={{ opacity: backAlpha }}> <img @@ -531,7 +547,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { </div> )} </div> - {this.overlayImageIcon} </div> ); } @@ -739,12 +754,12 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { <div className="imageBox" onContextMenu={this.specificContextMenu} - ref={this._mainCont} + ref={this.createDropTarget} onScroll={action(() => { if (!this._forcedScroll) { - if (this.layoutDoc._layout_scrollTop || this._mainCont.current?.scrollTop) { + if (this.layoutDoc._layout_scrollTop || this._mainCont?.scrollTop) { this._ignoreScroll = true; - this.layoutDoc._layout_scrollTop = this._mainCont.current?.scrollTop; + this.layoutDoc._layout_scrollTop = this._mainCont?.scrollTop; this._ignoreScroll = false; } } @@ -786,8 +801,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { <ReactLoading type="spin" height={50} width={50} color={'blue'} /> </div> ) : null} + {this.overlayImageIcon} {this.annotationLayer} - {!this._mainCont.current || !this.DocumentView || !this._annotationLayer.current ? null : ( + {!this._mainCont || !this.DocumentView || !this._annotationLayer.current ? null : ( <MarqueeAnnotator Document={this.Document} ref={this.marqueeref} @@ -802,7 +818,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { savedAnnotations={this.savedAnnotations} selectionText={returnEmptyString} annotationLayer={this._annotationLayer.current} - marqueeContainer={this._mainCont.current} + marqueeContainer={this._mainCont} highlightDragSrcColor="" anchorMenuCrop={this.crop} // anchorMenuFlashcard={() => this.getImageDesc()} @@ -839,5 +855,5 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { Docs.Prototypes.TemplateMap.set(DocumentType.IMG, { layout: { view: ImageBox, dataField: 'data' }, - options: { acl: '', freeform: '', systemIcon: 'BsFileEarmarkImageFill' }, + options: { acl: '', freeform: '', _layout_nativeDimEditable: true, systemIcon: 'BsFileEarmarkImageFill' }, }); diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 6026d9ca7..e7a10cc29 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -383,7 +383,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { this._textAnnotationCreator = () => this.createTextAnnotation(sel, !sel.isCollapsed ? sel.getRangeAt(0) : undefined); AnchorMenu.Instance.jumpTo(e.clientX * scale + mainContBounds.translateX, e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._layout_scrollTop) * scale); // Changing which document to add the annotation to (the currently selected WebBox) - GPTPopup.Instance.setSidebarId(`${this._props.fieldKey}_${this._urlHash ? this._urlHash + '_' : ''}sidebar`); + GPTPopup.Instance.setSidebarFieldKey(`${this._props.fieldKey}_${this._urlHash ? this._urlHash + '_' : ''}sidebar`); GPTPopup.Instance.addDoc = this.sidebarAddDocument; } } else { @@ -446,7 +446,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { this._textAnnotationCreator = () => this.createTextAnnotation(sel, selRange); (!sel.isCollapsed || this.marqueeing) && AnchorMenu.Instance.jumpTo(e.clientX, e.clientY); // Changing which document to add the annotation to (the currently selected WebBox) - GPTPopup.Instance.setSidebarId(`${this._props.fieldKey}_${this._urlHash ? this._urlHash + '_' : ''}sidebar`); + GPTPopup.Instance.setSidebarFieldKey(`${this._props.fieldKey}_${this._urlHash ? this._urlHash + '_' : ''}sidebar`); GPTPopup.Instance.addDoc = this.sidebarAddDocument; } }; diff --git a/src/client/views/nodes/calendarBox/CalendarBox.tsx b/src/client/views/nodes/calendarBox/CalendarBox.tsx index d38cb5423..009eb82cd 100644 --- a/src/client/views/nodes/calendarBox/CalendarBox.tsx +++ b/src/client/views/nodes/calendarBox/CalendarBox.tsx @@ -1,4 +1,4 @@ -import { Calendar, EventClickArg, EventSourceInput } from '@fullcalendar/core'; +import { Calendar, EventClickArg, EventDropArg, EventSourceInput } from '@fullcalendar/core'; import dayGridPlugin from '@fullcalendar/daygrid'; import multiMonthPlugin from '@fullcalendar/multimonth'; import timeGrid from '@fullcalendar/timegrid'; @@ -17,6 +17,7 @@ import { DocumentView } from '../DocumentView'; import { OpenWhere } from '../OpenWhere'; import { DragManager } from '../../../util/DragManager'; import { DocData } from '../../../../fields/DocSymbols'; +import { ContextMenu } from '../../ContextMenu'; type CalendarView = 'multiMonth' | 'dayGridMonth' | 'timeGridWeek' | 'timeGridDay'; @@ -104,32 +105,44 @@ export class CalendarBox extends CollectionSubView() { } // TODO: Return a different color based on the event type - eventToColor(event: Doc): string { + eventToColor = (event: Doc): string => { return 'red'; - } + }; - internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData) { + internalDocDrop = (e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData) => { if (!super.onInternalDrop(e, de)) return false; de.complete.docDragData?.droppedDocuments.forEach(doc => { const today = new Date().toISOString(); if (!doc.date_range) doc[DocData].date_range = `${today}|${today}`; }); return true; - } + }; onInternalDrop = (e: Event, de: DragManager.DropEvent): boolean => { if (de.complete.docDragData?.droppedDocuments.length) return this.internalDocDrop(e, de, de.complete.docDragData); return false; }; + handleEventDrop = (arg: EventDropArg) => { + const doc = DocServer.GetCachedRefField(arg.event._def.groupId ?? ''); + doc && arg.event.start && (doc.date_range = arg.event.start?.toString() + '|' + (arg.event.end ?? arg.event.start).toString()); + }; + handleEventClick = (arg: EventClickArg) => { const doc = DocServer.GetCachedRefField(arg.event._def.groupId ?? ''); - DocumentView.DeselectAll(); if (doc) { DocumentView.showDocument(doc, { openLocation: OpenWhere.lightboxAlways }); arg.jsEvent.stopPropagation(); } }; + handleEventContextMenu = (pageX: number, pageY: number, docid: string) => { + const doc = DocServer.GetCachedRefField(docid ?? ''); + if (doc) { + const cm = ContextMenu.Instance; + cm.addItem({ description: 'Show Metadata', event: () => this._props.addDocTab(doc, OpenWhere.addRightKeyvalue), icon: 'table-columns' }); + cm.displayMenu(pageX - 15, pageY - 15, undefined, undefined); + } + }; // https://fullcalendar.io renderCalendar = () => { @@ -157,6 +170,25 @@ export class CalendarBox extends CollectionSubView() { aspectRatio: NumCast(this.Document.width) / NumCast(this.Document.height), events: this.calendarEvents, eventClick: this.handleEventClick, + eventDrop: this.handleEventDrop, + eventDidMount: arg => { + arg.el.addEventListener('pointerdown', ev => { + ev.button && ev.stopPropagation(); + }); + if (navigator.userAgent.includes('Macintosh')) { + arg.el.addEventListener('pointerup', ev => { + ev.button && ev.stopPropagation(); + ev.button && this.handleEventContextMenu(ev.pageX, ev.pageY, arg.event._def.groupId); + }); + } + arg.el.addEventListener('contextmenu', ev => { + if (!navigator.userAgent.includes('Macintosh')) { + this.handleEventContextMenu(ev.pageX, ev.pageY, arg.event._def.groupId); + } + ev.stopPropagation(); + ev.preventDefault(); + }); + }, })); cal?.render(); setTimeout(() => cal?.view.calendar.select(this.dateSelect.start, this.dateSelect.end)); diff --git a/src/client/views/nodes/chatbot/agentsystem/Agent.ts b/src/client/views/nodes/chatbot/agentsystem/Agent.ts index 19fd6ae36..e93fb87db 100644 --- a/src/client/views/nodes/chatbot/agentsystem/Agent.ts +++ b/src/client/views/nodes/chatbot/agentsystem/Agent.ts @@ -22,6 +22,7 @@ import { ChatCompletionMessageParam } from 'openai/resources'; import { Doc } from '../../../../../fields/Doc'; import { parsedDoc } from '../chatboxcomponents/ChatBox'; import { WebsiteInfoScraperTool } from '../tools/WebsiteInfoScraperTool'; +import { Upload } from '../../../../../server/SharedMediaTypes'; import { RAGTool } from '../tools/RAGTool'; //import { CreateTextDocTool } from '../tools/CreateTextDocumentTool'; @@ -62,7 +63,7 @@ export class Agent { history: () => string, csvData: () => { filename: string; id: string; text: string }[], addLinkedUrlDoc: (url: string, id: string) => void, - createImage: (result: any, options: DocumentOptions) => void, + createImage: (result: Upload.FileInformation & Upload.InspectionResults, options: DocumentOptions) => void, addLinkedDoc: (doc: parsedDoc) => Doc | undefined, // eslint-disable-next-line @typescript-eslint/no-unused-vars createCSVInDash: (url: string, title: string, id: string, data: string) => void diff --git a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx index f8fe531ab..6e9307d37 100644 --- a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx +++ b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx @@ -42,6 +42,7 @@ import './ChatBox.scss'; import MessageComponentBox from './MessageComponent'; import { ProgressBar } from './ProgressBar'; import { OpenWhere } from '../../OpenWhere'; +import { Upload } from '../../../../../server/SharedMediaTypes'; dotenv.config(); @@ -412,7 +413,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { }); @action - createImageInDash = async (result: any, options: DocumentOptions) => { + createImageInDash = async (result: Upload.FileInformation & Upload.InspectionResults, options: DocumentOptions) => { const newImgSrc = result.accessPaths.agnostic.client.indexOf('dashblobstore') === -1 // ? ClientUtils.prepend(result.accessPaths.agnostic.client) @@ -1046,5 +1047,5 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { */ Docs.Prototypes.TemplateMap.set(DocumentType.CHAT, { layout: { view: ChatBox, dataField: 'data' }, - options: { acl: '', chat: '', chat_history: '', chat_thread_id: '', chat_assistant_id: '', chat_vector_store_id: '' }, + options: { acl: '', _layout_fitWidth: true, chat: '', chat_history: '', chat_thread_id: '', chat_assistant_id: '', chat_vector_store_id: '' }, }); diff --git a/src/client/views/nodes/chatbot/tools/CreateDocumentTool.ts b/src/client/views/nodes/chatbot/tools/CreateDocumentTool.ts index 6dc36b0d1..284879a4a 100644 --- a/src/client/views/nodes/chatbot/tools/CreateDocumentTool.ts +++ b/src/client/views/nodes/chatbot/tools/CreateDocumentTool.ts @@ -263,79 +263,79 @@ const standardOptions = ['title', 'backgroundColor']; * Description of document options and data field for each type. */ const documentTypesInfo: { [key in supportedDocTypes]: { options: string[]; dataDescription: string } } = { - [supportedDocTypes.comparison]: { + comparison: { options: [...standardOptions, 'fontColor', 'text_align'], dataDescription: 'an array of two documents of any kind that can be compared.', }, - [supportedDocTypes.deck]: { + deck: { options: [...standardOptions, 'fontColor', 'text_align'], dataDescription: 'an array of flashcard docs', }, - [supportedDocTypes.flashcard]: { + flashcard: { options: [...standardOptions, 'fontColor', 'text_align'], dataDescription: 'an array of two strings. the first string contains a question, and the second string contains an answer', }, - [supportedDocTypes.text]: { + text: { options: [...standardOptions, 'fontColor', 'text_align'], dataDescription: 'The text content of the document.', }, - [supportedDocTypes.web]: { + web: { options: [], dataDescription: 'A URL to a webpage. Example: https://en.wikipedia.org/wiki/Brown_University', }, - [supportedDocTypes.html]: { + html: { options: [], dataDescription: 'The HTML-formatted text content of the document.', }, - [supportedDocTypes.equation]: { + equation: { options: [...standardOptions, 'fontColor'], dataDescription: 'The equation content represented as a MathML string.', }, - [supportedDocTypes.functionplot]: { + functionplot: { options: [...standardOptions, 'function_definition'], dataDescription: 'The function definition(s) for plotting. Provide as a string or array of function definitions.', }, - [supportedDocTypes.dataviz]: { + dataviz: { options: [...standardOptions, 'chartType'], dataDescription: 'A string of comma-separated values representing the CSV data.', }, - [supportedDocTypes.notetaking]: { + notetaking: { options: standardOptions, dataDescription: 'An array of related text documents with small amounts of text.', }, - [supportedDocTypes.rtf]: { + rtf: { options: standardOptions, dataDescription: 'The rich text content in RTF format.', }, - [supportedDocTypes.image]: { + image: { options: standardOptions, dataDescription: `A url string that must end with '.png', '.jpeg', '.gif', or '.jpg'`, }, - [supportedDocTypes.pdf]: { + pdf: { options: standardOptions, dataDescription: 'the pdf content as a PDF file url.', }, - [supportedDocTypes.audio]: { + audio: { options: standardOptions, dataDescription: 'The audio content as a file url.', }, - [supportedDocTypes.video]: { + video: { options: standardOptions, dataDescription: 'The video content as a file url.', }, - [supportedDocTypes.message]: { + message: { options: standardOptions, dataDescription: 'The message content of the document.', }, - [supportedDocTypes.diagram]: { + diagram: { options: standardOptions, dataDescription: 'diagram content as a text string in Mermaid format.', }, - [supportedDocTypes.script]: { + script: { options: standardOptions, dataDescription: 'The compilable JavaScript code. Use this for creating scripts.', }, - [supportedDocTypes.collection]: { + collection: { options: [...standardOptions, 'type_collection'], dataDescription: 'A collection of Docs represented as an array.', }, diff --git a/src/client/views/nodes/chatbot/tools/ImageCreationTool.ts b/src/client/views/nodes/chatbot/tools/ImageCreationTool.ts index 177552c5c..dc6140871 100644 --- a/src/client/views/nodes/chatbot/tools/ImageCreationTool.ts +++ b/src/client/views/nodes/chatbot/tools/ImageCreationTool.ts @@ -1,10 +1,10 @@ -import { v4 as uuidv4 } from 'uuid'; import { RTFCast } from '../../../../../fields/Types'; import { DocumentOptions } from '../../../../documents/Documents'; import { Networking } from '../../../../Network'; import { ParametersType, ToolInfo } from '../types/tool_types'; import { Observation } from '../types/types'; import { BaseTool } from './BaseTool'; +import { Upload } from '../../../../../server/SharedMediaTypes'; const imageCreationToolParams = [ { @@ -25,8 +25,8 @@ const imageCreationToolInfo: ToolInfo<ImageCreationToolParamsType> = { }; export class ImageCreationTool extends BaseTool<ImageCreationToolParamsType> { - private _createImage: (result: any, options: DocumentOptions) => void; - constructor(createImage: (result: any, options: DocumentOptions) => void) { + private _createImage: (result: Upload.FileInformation & Upload.InspectionResults, options: DocumentOptions) => void; + constructor(createImage: (result: Upload.FileInformation & Upload.InspectionResults, options: DocumentOptions) => void) { super(imageCreationToolInfo); this._createImage = createImage; } @@ -42,23 +42,19 @@ export class ImageCreationTool extends BaseTool<ImageCreationToolParamsType> { }); console.log('Image generation result:', result); this._createImage(result, { text: RTFCast(image_prompt) }); - if (url) { - const id = uuidv4(); - - return [ - { - type: 'image_url', - image_url: { url }, - }, - ]; - } else { - return [ - { - type: 'text', - text: `An error occurred while generating image.`, - }, - ]; - } + return url + ? [ + { + type: 'image_url', + image_url: { url }, + }, + ] + : [ + { + type: 'text', + text: `An error occurred while generating image.`, + }, + ]; } catch (error) { console.log(error); return [ diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index eb1f9d07b..3abb39ff2 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -135,7 +135,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB /** * ApplyingChange - Marks whether an interactive text edit is currently in the process of being written to the database. - * This is needed to distinguish changes to text fields caused by editing vs those caused by changes to + * This is needed to distinguish changes to text fields caused by editing vs those caused by changes to * the prototype or other external edits */ public ApplyingChange: string = ''; @@ -977,7 +977,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB }, icon: 'star', }); - optionItems.push({ description: `Generate Dall-E Image`, event: () => this.generateImage(), icon: 'star' }); + optionItems.push({ description: `Generate Dall-E Image`, event: this.generateImage, icon: 'star' }); // optionItems.push({ description: `Make AI Flashcards`, event: () => this.makeAIFlashcards(), icon: 'lightbulb' }); optionItems.push({ description: `Ask GPT-3`, event: this.askGPT, icon: 'lightbulb' }); this._props.renderDepth && @@ -1043,7 +1043,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB askGPT = action(async () => { try { - GPTPopup.Instance.setSidebarId(this.sidebarKey); + GPTPopup.Instance.setSidebarFieldKey(this.sidebarKey); GPTPopup.Instance.addDoc = this.sidebarAddDocument; const res = await gptAPICall((this.dataDoc.text as RichTextField)?.Text, GPTCallType.COMPLETION); if (!res) { @@ -1061,12 +1061,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB } }); - generateImage = async () => { + generateImage = () => { GPTPopup.Instance?.setTextAnchor(this.getAnchor(false)); - GPTPopup.Instance?.setImgTargetDoc(this.Document); - GPTPopup.Instance.addToCollection = this._props.addDocument; - GPTPopup.Instance.setImgDesc((this.dataDoc.text as RichTextField)?.Text); - GPTPopup.Instance.generateImage(); + GPTPopup.Instance.generateImage((this.dataDoc.text as RichTextField)?.Text, this.Document, this._props.addDocument); }; breakupDictation = () => { @@ -1660,7 +1657,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB } }; onSelectEnd = () => { - GPTPopup.Instance.setSidebarId(this.sidebarKey); + GPTPopup.Instance.setSidebarFieldKey(this.sidebarKey); GPTPopup.Instance.addDoc = this.sidebarAddDocument; document.removeEventListener('pointerup', this.onSelectEnd); }; diff --git a/src/client/views/nodes/imageEditor/ImageEditor.tsx b/src/client/views/nodes/imageEditor/ImageEditor.tsx index 6b1d05031..3c0ab3da5 100644 --- a/src/client/views/nodes/imageEditor/ImageEditor.tsx +++ b/src/client/views/nodes/imageEditor/ImageEditor.tsx @@ -90,18 +90,8 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc * @param type The new tool type we are changing to */ const changeTool = (type: ImageToolType) => { - switch (type) { - case ImageToolType.GenerativeFill: - setCurrTool(genFillTool); - setCursorData(prev => ({ ...prev, width: genFillTool.sliderDefault as number })); - break; - case ImageToolType.Cut: - setCurrTool(cutTool); - setCursorData(prev => ({ ...prev, width: cutTool.sliderDefault as number })); - break; - default: - break; - } + setCurrToolType(type); + setCursorData(prev => ({ ...prev, width: currTool().sliderDefault as number })); }; // Undo and Redo const handleUndo = () => { @@ -171,9 +161,8 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc // handles brushing on pointer movement useEffect(() => { - if (!isBrushing) return undefined; const canvas = canvasRef.current; - if (!canvas) return undefined; + if (!isBrushing || !canvas) return undefined; const ctx = ImageUtility.getCanvasContext(canvasRef); if (!ctx) return undefined; @@ -188,33 +177,29 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc }; drawingAreaRef.current?.addEventListener('pointermove', handlePointerMove); - return () => { - drawingAreaRef.current?.removeEventListener('pointermove', handlePointerMove); - }; + return () => drawingAreaRef.current?.removeEventListener('pointermove', handlePointerMove); }, [isBrushing]); // first load useEffect(() => { - const loadInitial = async () => { - if (!imageEditorSource || imageEditorSource === '') return; - const img = new Image(); - const res = await ImageUtility.urlToBase64(imageEditorSource); - if (!res) return; - img.src = `data:image/png;base64,${res}`; - - img.onload = () => { - currImg.current = img; - originalImg.current = img; - const imgWidth = img.naturalWidth; - const imgHeight = img.naturalHeight; - const scale = Math.min(canvasSize / imgWidth, canvasSize / imgHeight); - const width = imgWidth * scale; - const height = imgHeight * scale; - setCanvasDims({ width, height }); - }; - }; - - loadInitial(); + if (imageEditorSource && imageEditorSource) { + ImageUtility.urlToBase64(imageEditorSource).then(res => { + if (res) { + const img = new Image(); + img.src = `data:image/png;base64,${res}`; + img.onload = () => { + currImg.current = img; + originalImg.current = img; + const imgWidth = img.naturalWidth; + const imgHeight = img.naturalHeight; + const scale = Math.min(canvasSize / imgWidth, canvasSize / imgHeight); + const width = imgWidth * scale; + const height = imgHeight * scale; + setCanvasDims({ width, height }); + }; + } + }); + } // cleanup return () => { @@ -300,7 +285,7 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc if (!canvasMask) return; const maskBlob = await ImageUtility.canvasToBlob(canvasMask); const imgBlob = await ImageUtility.canvasToBlob(canvasOriginalImg); - const res = await ImageUtility.getEdit(imgBlob, maskBlob, input !== '' ? input + ' in the same style' : 'Fill in the image in the same style', 2); + const res = await ImageUtility.getEdit(imgBlob, maskBlob, input || 'Fill in the image in the same style', 2); // create first image if (!newCollectionRef.current) { @@ -569,11 +554,15 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc setIsFirstDoc(true); }; + function currTool() { + return imageEditTools.find(tool => tool.type === currToolType) ?? genFillTool; + } + // defines the tools and sets current tool - const genFillTool: ImageEditTool = { type: ImageToolType.GenerativeFill, name: 'Generative Fill', btnText: 'GET EDITS', icon: 'fill', applyFunc: getEdit, sliderMin: 25, sliderMax: 500, sliderDefault: 150 }; - const cutTool: ImageEditTool = { type: ImageToolType.Cut, name: 'Cut', btnText: 'CUT IMAGE', icon: 'scissors', applyFunc: cutImage, sliderMin: 1, sliderMax: 50, sliderDefault: 5 }; + const genFillTool: ImageEditTool = { type: ImageToolType.GenerativeFill, btnText: 'GET EDITS', icon: 'fill', applyFunc: getEdit, sliderMin: 25, sliderMax: 500, sliderDefault: 150 }; + const cutTool: ImageEditTool = { type: ImageToolType.Cut, btnText: 'CUT IMAGE', icon: 'scissors', applyFunc: cutImage, sliderMin: 1, sliderMax: 50, sliderDefault: 5 }; const imageEditTools: ImageEditTool[] = [genFillTool, cutTool]; - const [currTool, setCurrTool] = useState<ImageEditTool>(genFillTool); + const [currToolType, setCurrToolType] = useState<ImageToolType>(ImageToolType.GenerativeFill); // the top controls for making a new collection, resetting, and applying edits, function renderControls() { @@ -595,7 +584,7 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc labelPlacement="end" sx={{ whiteSpace: 'nowrap' }} /> - <ApplyFuncButtons onClick={() => currTool.applyFunc(cutType, cursorData.width, edits, isFirstDoc)} loading={loading} onReset={handleReset} btnText={currTool.btnText} /> + <ApplyFuncButtons onClick={() => currTool().applyFunc(cutType, cursorData.width, edits, isFirstDoc)} loading={loading} onReset={handleReset} btnText={currTool().btnText} /> <IconButton color={activeColor} tooltip="close" icon={<CgClose size="16px" />} onClick={handleViewClose} /> </div> </div> @@ -607,8 +596,8 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc return ( <div className="sideControlsContainer" style={{ backgroundColor: bgColor }}> <div className="sideControls"> - <div className="imageToolsContainer">{imageEditTools.map(tool => ImageToolButton(tool, tool.type === currTool.type, changeTool))}</div> - {currTool.type == ImageToolType.Cut && ( + <div className="imageToolsContainer">{imageEditTools.map(tool => ImageToolButton(tool, tool.type === currTool().type, changeTool))}</div> + {currTool().type == ImageToolType.Cut && ( <div className="cutToolsContainer"> <Button style={{ width: '100%' }} text="Keep in" type={Type.TERT} color={cutType == CutMode.IN ? SettingsManager.userColor : bgColor} onClick={() => setCutType(CutMode.IN)} /> <Button style={{ width: '100%' }} text="Keep out" type={Type.TERT} color={cutType == CutMode.OUT ? SettingsManager.userColor : bgColor} onClick={() => setCutType(CutMode.OUT)} /> @@ -617,7 +606,7 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc </div> )} <div className="sliderContainer" onPointerDown={e => e.stopPropagation()}> - {currTool.type === ImageToolType.GenerativeFill && ( + {currTool().type === ImageToolType.GenerativeFill && ( <Slider sx={{ '& input[type="range"]': { @@ -633,7 +622,7 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc onChange={(e, val) => setCursorData(prev => ({ ...prev, width: val as number }))} /> )} - {currTool.type === ImageToolType.Cut && ( + {currTool().type === ImageToolType.Cut && ( <Slider sx={{ '& input[type="range"]': { @@ -780,7 +769,7 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc {renderSideIcons()} {renderEditThumbnails()} </div> - {currTool.type === ImageToolType.GenerativeFill && renderPromptBox()} + {currTool().type === ImageToolType.GenerativeFill && renderPromptBox()} </div> ); }; diff --git a/src/client/views/nodes/imageEditor/ImageEditorButtons.tsx b/src/client/views/nodes/imageEditor/ImageEditorButtons.tsx index 985dc914f..3eaa251f2 100644 --- a/src/client/views/nodes/imageEditor/ImageEditorButtons.tsx +++ b/src/client/views/nodes/imageEditor/ImageEditorButtons.tsx @@ -53,10 +53,10 @@ export function ApplyFuncButtons({ loading, onClick: getEdit, onReset, btnText } export function ImageToolButton(tool: ImageEditTool, isActive: boolean, selectTool: (type: ImageToolType) => void) { return ( - <div key={tool.name} className="imageEditorButtonContainer"> + <div key={tool.type} className="imageEditorButtonContainer"> <Button style={{ width: '100%' }} - text={tool.name} + text={tool.type} type={Type.TERT} color={isActive ? SettingsManager.userVariantColor : bgColor} icon={<FontAwesomeIcon icon={tool.icon} />} diff --git a/src/client/views/nodes/imageEditor/imageEditorUtils/ImageHandler.ts b/src/client/views/nodes/imageEditor/imageEditorUtils/ImageHandler.ts index ece0f4d7f..1c6a38a24 100644 --- a/src/client/views/nodes/imageEditor/imageEditorUtils/ImageHandler.ts +++ b/src/client/views/nodes/imageEditor/imageEditorUtils/ImageHandler.ts @@ -87,7 +87,6 @@ export class ImageUtility { body: fd, }); const data = await res.json(); - console.log(data.data); return { status: 'success', urls: (data.data as { b64_json: string }[]).map(urlData => `data:image/png;base64,${urlData.b64_json}`), diff --git a/src/client/views/nodes/imageEditor/imageEditorUtils/imageEditorInterfaces.ts b/src/client/views/nodes/imageEditor/imageEditorUtils/imageEditorInterfaces.ts index a14b55439..02dbc0312 100644 --- a/src/client/views/nodes/imageEditor/imageEditorUtils/imageEditorInterfaces.ts +++ b/src/client/views/nodes/imageEditor/imageEditorUtils/imageEditorInterfaces.ts @@ -13,8 +13,8 @@ export interface Point { } export enum ImageToolType { - GenerativeFill = 'genFill', - Cut = 'cut', + GenerativeFill = 'Generative Fill', + Cut = 'Cut', } export enum CutMode { @@ -26,7 +26,6 @@ export enum CutMode { export interface ImageEditTool { type: ImageToolType; - name: string; btnText: string; icon: IconProp; // this is the function that the image tool applies, so it can be defined depending on the tool |
